feat: chat channel routing for broker plugins#114
Closed
scion-gteam[bot] wants to merge 12 commits into
Closed
Conversation
Owner
|
This pull request has been recreated on the target repository as GoogleCloudPlatform#283. |
…leCloudPlatform#286) * Update docs-site landing page with Scion explainer deck content Incorporate key messaging from the Scion intro slides into the landing page. Adds a new "Scion Core" pipeline section showing the five-part orchestration flow (Define, Run, Spawn, Notify, Sync) and expands the feature cards with detailed descriptions from each slide topic (Agent Definition, Runtime, Collaborators, Notifications, Shared Filesystem). Other changes: - Replace "Boot" with "Run" per requested terminology change - Update "grove" to "project" in the how-it-works steps - Replace Google Slides embed with the HTML slides deck URL - Reorder sections: pipeline → features → video → slides → quickstart * Add project log entry for docs landing page update
…Platform#270) Add circuit breaker pattern to Cloud Logging integration: - ResilientCloudHandler wraps CloudHandler with three-state circuit breaker (closed/open/half-open) and background health checks - flushInFlight atomic prevents goroutine leaks from concurrent flushes - circuitGatedHandler gates request/message loggers through the circuit breaker so all log streams benefit from protection - CloudHandler gets BufferedByteLimit (8MiB) and ClientTimeout (15s) - Cleanup ordering ensures health check goroutine stops before client - Mutex-protected logging moved outside locks to prevent deadlocks
…ogleCloudPlatform#271) * Fix resume creating new agents instead of restarting stopped ones (#61) The Hub server's CreateAgentRequest struct was missing the Resume field that the CLI client sends, causing all stopped agents to be treated as stale and deleted+recreated rather than restarted in-place. Add the Resume field to the Hub's CreateAgentRequest and handle the stopped+resume case in handleExistingAgent: when Resume=true and the agent is in PhaseStopped, restart it in-place (preserving agent ID, metadata, and template association) rather than deleting and recreating. When Resume is not set (i.e. scion start), the existing delete+recreate behavior is preserved unchanged. * Add project log for issue #61 resume fix * Address PR review: nil-guard AppliedConfig on resume, fix TS unused var - Defensively initialize existingAgent.AppliedConfig when nil before applying task/attach params during resume, preventing potential nil pointer dereference (review feedback). - Fix profile-telegram.ts TS6133 error: _linkedTelegramId was declared but never read, causing CI Build & Test failure.
…#284) * Add engineering glossary (GLOSSARY.md) with canonical terms and cleanup tracker Add a root-level GLOSSARY.md capturing canonical Scion terminology in the ubiquitous-language format (preferred term + synonyms to avoid), grouped by domain cluster, plus an Exceptions & Future Cleanup section tracking known naming-convergence work. Link it from agents.md as the canonical engineering glossary. * Revise glossary: broker reframe, Event Bus, Hub-managed, and term refinements Refine entries from review: redefine Message Broker as the pluggable messaging-integration system (add Broker plugin, Built-in broker); add Event Bus for the NATS real-time/event capability; collapse hub-native/Hub Workspace into Hub-managed project/workspace; tighten Template (harness-agnostic, optional default harness-config), Skill (template-only, Agent Skills link), Profile (named runtime-broker settings bundle), Harness/Harness-config; reframe Hub as the control plane in both modes; add Group and Message Group. Expand Exceptions & Future Cleanup to nine tracked items. * Glossary: restructure headings, add cross-refs, modes table, and new terms - Retitle to "Scion Glossary"; drop the "Language" wrapper and promote the thematic categories to top-level sections - Add an Operations section (Attach, Dispatch) and move Profile next to Runtime Broker - Add a Local/Workstation/Hosted comparison table and "See also" cross-refs across the main confusable term clusters - Reframe the intro around the three-way broker collision (incl. Event Bus) and defer to the disambiguation rule; sentence-case "Shared directory" - Add canonical entries for Secret, Notification, and Schedule - Add a "Potential Future Additions" section cataloguing candidate terms * Glossary: remove Exceptions & Future Cleanup tracker The cleanup items are now tracked by dedicated agents that open GitHub issues and implementation PRs, so the staged tracker no longer lives in the glossary. Reword the two intro/disambiguation references that pointed at the removed section to point at GitHub issues instead. * docs: fix glossary typo, remove outdated shorthand terms, fix workstation state - agents.md: fix 'gossary' typo in section header; remove shorthand definitions (hub-native, hub-project, linked-project, agent-home, hub-broker) that contradicted the canonical GLOSSARY.md - GLOSSARY.md: clarify Workstation mode State & isolation column
* refactor(hub): consolidate content-hash onto canonical transfer impl
The hub's computeContentHash (template_handlers.go) was a separate hand-rolled
implementation of the same sorted-file-hash aggregation as the canonical
transfer.ComputeContentHash used by the broker-side hydrator, the transfer
collector, and the hubclient manifest builder. The two diverged on empty input
(the hub hashed an empty byte stream; transfer returns ""), exactly the silent
hub/broker drift risk that would break cache-key lookups.
Make the hub function a thin adapter that converts []store.TemplateFile to
[]transfer.FileInfo and delegates to transfer.ComputeContentHash, so every call
site shares one implementation. Add content_hash_test.go pinning the adapter to
transfer and the empty-input contract.
This is the §7.3 'pull-forward' from .design/hosted/resource-storage-refactor.md
and resolves the hash-canonicalization open question (§9). No behavior change
except empty file sets now hash to "" (one harmless re-sync for any degenerate
zero-file resource on next bootstrap).
* refactor(broker): single read path + thin content-addressed cache (7.1, 7.2a)
Implements sections 7.1 and 7.2a of the resource-storage refactor design. These
landed together with templates as the proving ground but were left uncommitted;
this captures them on the branch.
7.1 — single read path, remove co-located live-edit shortcut:
- hydrateTemplate no longer reads the import-source dir. It resolves through the
connection's storage backend in every topology. New resolveLocalTemplate reads
directly from the local backend's on-disk location when the backend is local
(co-located workstation mode), otherwise hydrates from remote storage.
- Replace HubConnection.TemplatesDir with HubConnection.LocalStorage
(storage.Storage), set only for a co-located connection whose backend is local
FS. IsColocated is kept (still used by the heartbeat loop).
- Add storage.LocalStorage.ObjectFSPath, reached via a localObjectResolver
interface assertion in the broker (placeholder for the 7.3 LocalDirBackend).
- Plumb runtimebroker.ServerConfig.ColocatedStorage from hubSrv.GetStorage() in
cmd/server_foreground.go, only in co-located mode.
7.2a — collapse the cache to a thin content-addressed store:
- templatecache.Cache is keyed solely by content hash: Get(hash)/Put(hash,files)
with a size-bounded LRU. Removed the templateID→hash index, GetByHash,
GetAnyVersion, GetFileHashes, per-file incremental download, the
store-nil-under-second-ID path, and shared-hash eviction refcounting.
- Hydrator simplified to: metadata -> cache.Get(hash) -> download-whole-on-miss
-> verify each file hash -> cache.Put(hash, files).
- Cache is only exercised on the remote/GCS path; the local direct-read path
never constructs a hydrator call.
* style: gofmt struct alignment in extras and pkg/messages
Pure gofmt formatting (struct field/tag alignment) on pre-existing files that
were failing the repo-wide fmt-check gate. No semantic changes.
* test: use project_id key in hub secret/sync test fixtures
Two tests asserted resolution of a project's ID from a legacy top-level
grove_id key in settings files (settings.json / settings.yaml), but the
grove->project rename only wired up project_id resolution for that key path —
top-level legacy grove_id no longer maps to project_id, so the fixtures
produced an empty ProjectID and the tests failed:
- cmd: TestResolveSecretScope_ProjectFallbackToProjectID
- hubsync: TestEnsureHubReady_EndpointOverrideBeatsSettings
Switch the fixtures to the modern project_id key, completing the rename for
these fixtures and restoring green CI. The tests still exercise their actual
assertions (scope fallback to ProjectID; endpoint override beating settings).
* style: gofmt rename-induced struct misalignment (heartbeat, chatapp)
The grove->project rename widened struct fields (ProjectID, projectFilter)
without re-running gofmt, leaving neighboring fields misaligned. Pure gofmt;
no semantic changes.
* docs(agents): add sandbox git/CI gotchas and memory-persistence policy
Capture operational learnings for future agent sessions in the root agents.md
(symlinked as CLAUDE.md/claude.md/gemini.md, so it applies to all harnesses):
- New 'Sandbox gotchas' subsection in the Git Workflow Protocol: Go version
skew vs the go.mod/CI-pinned gofmt, go build -buildvcs=false in worktrees,
inspecting main via a detached worktree (can't worktree-add the main branch),
golangci-lint OOM mitigation (scope + --new-from-rev=main), leaked SCION_*
env vars in tests, and how 'latest main' reaches a worktree without fetch.
- New 'Agent memory & durable notes' section: the harness built-in/native
memory dir is ephemeral (not persisted across container restarts), so durable
guidance must live in committed project files only. Migrates the two prior
ephemeral notes (lean forward on 'project' over 'grove'; proactively raise
adjacent cleanup) into agents.md.
* build: upgrade root module to Go 1.26.1
Bump the go directive in the root go.mod from 1.25.4 to 1.26.1, aligning it with
the core-base build image (image-build/core-base/Dockerfile already ships
GO_VERSION=1.26.1) and the runtime/sandbox toolchain. CI installs Go via
go-version-file: go.mod, so this also moves CI onto 1.26.1 and eliminates the
prior toolchain/CI skew (go.mod 1.25.4 vs image 1.26.1).
Update the agents.md sandbox-gotchas note accordingly. Builds, vet, and the full
'make ci' pass under 1.26.1.
The extras/* submodules are separate Go modules still on older go directives and
golang:1.25.x Dockerfile pins; they are left unchanged (bumping them needs
coordinated Dockerfile base-image/digest updates) and noted as a follow-up.
* refactor(hub): extract shared resource bootstrap mechanics (7.3 first step)
Mechanics-first first step toward the §7.3 ResourceStore/ResourceResolver
abstraction in the resource-storage refactor. No behavior change.
- storage: add ResourceKind + kind-keyed ResourceStoragePath/ResourceStorageURI
as the single source of truth for the scope layout; reduce the legacy
Template*/HarnessConfig* path/URI functions to thin wrappers.
- hub: add uploadResourceFiles + reconcileResourceStorage in storage_helpers.go
(the doc's named 'seed of the generalization') and route all four
bootstrap/sync call sites (template + harness-config) through them. Templates
keep their stale-object reconcile; harness-configs do not (unchanged).
- tests: cover ResourceStoragePath per kind x scope and pin the legacy wrappers
to the shared implementation so the storage layout can't silently drift.
- doc: update Status block + §7.3/§8 to reflect content-hash consolidation
landed and mechanics extraction in progress.
Note: the template reconcile warn lines now key the resource name as
"resource" instead of "template" (generic helper); warn-only, cosmetic.
* refactor(hub): dedupe FileInfo->manifest conversion; mark 7.3 mechanics done
Adjacent simplification on top of the 7.3 mechanics extraction:
- hub: add toResourceFiles(files) helper in storage_helpers.go and use it for
the content-hash preview build in syncExistingTemplate and the newHash build
in syncExistingHarnessConfig, replacing two more copies of the inline
[]transfer.FileInfo -> []store.TemplateFile loop. (The upload helper still
builds its manifest incrementally, appending only uploaded files, so it keeps
its own per-file build.)
- doc: mark the shared bootstrap mechanics (and earlier content-hash
consolidation) as done sub-items of §7.3; split §8 step 3 into 3a (done
mechanics) and 3b (remaining interfaces), noting 3b's broker-side
ResourceResolver is coupled with step 4 (harness-config onboarding) and may
land together. Fix stale 'sequencing step 3' cross-ref to step 4.
* refactor(hub): introduce ResourceStore over shared bootstrap primitives (7.3 step 3b, hub side)
Collapse the parallel template and harness-config import/sync routines onto one
kind-generic ResourceStore.Bootstrap that wraps the already-shared 3a mechanics
(uploadResourceFiles, reconcileResourceStorage, toResourceFiles,
computeContentHash, storage.ResourceStoragePath). A shared ResourceRecord is a
view over the common fields; a per-kind resourcePersistence bridges it to the
concrete store model, mutating the loaded model in place so the typed Config
payload (TemplateConfig / HarnessConfigData) survives a sync.
Behavior-preserving for templates. Harness-config sync now also reconciles
stale storage objects (a removed-file cleanup it previously lacked).
Guards added: typed-Config survival on sync (both kinds), harness-config
stale-object reconcile.
* refactor(broker): generalize Hydrator into a kind-generic Resolver (7.3 step 3b, broker side)
Extract the download-and-cache algorithm into a kind-agnostic templatecache.Resolver
parameterized by a resourceFetcher (Get metadata / RequestDownloadURLs /
DownloadFile), with adapters over hubclient.Templates() and hubclient.HarnessConfigs().
Hydrator is now a thin template-kind wrapper preserving its public API.
Fold the 7.1 localObjectResolver seam into a kind-aware resolveLocalResource +
resourceObjectPath in the broker, computing object paths via the kind-keyed
storage.ResourceStoragePath. One ObjectFSPath assertion serves every kind.
Behavior-preserving for templates; lays the resolver groundwork for the
harness-config consume path (step 4).
* feat(resources): hydrate harness-configs on the broker from the Hub (7.3 step 4)
Harness-configs were write-only to the storage backend: a broker that lacked the
harness-config on its local filesystem could not provision an agent that needed
it. Close that gap by giving harness-configs the same Hub-hydration path
templates already have.
Hub: populateAgentConfig resolves the harness-config name (or the template's
DefaultHarnessConfig) to a Hub record and stamps HarnessConfigID/Hash onto
AppliedConfig; the dispatcher threads them to the broker.
Broker: a per-kind harness-config cache + Resolver (NewHarnessConfigResolver),
and hydrateHarnessConfig (mirroring hydrateTemplate) resolves the config to a
local dir — via the co-located local backend or by downloading+caching from the
storage backend — and sets StartOptions.HarnessConfigPath.
Agent: provisioning honors HarnessConfigPath (threaded via context) and loads
the hydrated dir directly, bypassing the on-disk FindHarnessConfigDir search.
Name selection is unchanged; only the file source moves to the storage backend.
Adds HarnessConfigID/Hash to AgentAppliedConfig, RemoteAgentConfig, and
CreateAgentConfig; HarnessConfigPath to api.StartOptions.
Tests: hub ID/hash stamping (explicit + template-default), provision bypass of
disk search, broker hydrate fall-back guards.
* docs(resource-storage-refactor): mark 7.3 (3b) and step 4 as landed
* docs(resource-storage-refactor): record open follow-ups (errcheck lint + ci-full, hc resolver test)
* test(resources): harness-config Resolver test + errcheck fix (7.3 follow-ups)
Close the two open follow-ups from §7.3 of the resource-storage-refactor:
- Fix the carried-over errcheck on the unchecked f.Close() in
uploadResourceFiles (pkg/hub/storage_helpers.go) -> `_ = f.Close()`.
Scoped golangci-lint over pkg/hub and pkg/templatecache is now clean.
- Add a dedicated harness-config Resolver unit test
(pkg/templatecache/resolver_test.go) with a mock HarnessConfigService,
exercising the end-to-end download path (metadata -> signed URLs ->
download+verify -> cache), the content-addressed cache hit on
re-resolve, ResolveWithHash fast path, not-found, hash-mismatch, and
nil-hub-client cases. Wires a harnessConfigs field into the shared
mockHubClient.
make ci is green. (make ci-full still fails only on a pre-existing,
unrelated web-typecheck error in profile-telegram.ts, identical to main.)
* docs(resource-storage-refactor): consolidate to a single work progression
The doc had grown three parallel status trackers (the top Status paragraph,
per-subsection Status blockquotes in 7.1-7.4, and the Section 8 sequencing
list), with the same work double-numbered (e.g. 7.3 vs step 3) and drifting.
Collapse to one: Section 8 ('Work plan & status') is now the single ordered
progression and the only status tracker. Sections 7.1-7.4 become pure design
rationale with a one-line pointer to their step in Section 8; 7.3's as-built
detail moves into an 'Implementation notes (as landed)' block. The top Status
paragraph shrinks to a 'we are here' pointer. No work-state changes.
* docs(resource-storage-refactor): defer step 5 (skills) to skill-bank feature
* feat(hub): harness-config capability parity + project-scope list filter fix
Add capability annotations for harness-configs to match templates so the web
UI can gate editing consistently:
- register "harness_config" in ResourceActions/ScopeActions and add a
harnessConfigResource helper (project-scoped configs parent to their project)
- getHarnessConfig and the list handler now return _capabilities
(new HarnessConfigWithCapabilities), mirroring TemplateWithCapabilities
Also fix a harness-config store-filter gap: scope=project combined with a
projectId now narrows to that single project (previously matched every
project's configs, unlike the template filter). Covered by new tests.
* feat(web): harness-config editing, hub-scope resource editors, issue #80
Close the web UI gap for the resource-storage refactor:
- harness-config file browsing/editing: HarnessConfig{FileBrowser,FileEditor}
data sources + a harness-config-detail page reusing the shared
file-browser/editor (mirrors template-detail)
- new shared <scion-resource-list> component lists templates or
harness-configs for a scope and links to their detail pages; used by both
project settings and the hub page so they render identically
- Project Settings -> Resources gains a Harness Configs tab (list + edit); the
Templates list now uses the shared component
- rename "Hub Settings" -> "Hub Resources" and restructure /settings into a
project-settings-style Resources tab group (env vars, secrets, templates,
harness configs at global scope), with /settings/{templates,harness-configs}/{id}
detail routes
- issue #80: scope-aware back navigation adds a "Templates" link (deep-links
to the Templates tab via ?tab=); detail pages back-link to project settings
or Hub Resources depending on scope
* docs(resource-storage-refactor): record web UI (4b) and deferred import (4c)
Document the harness-config web UI + capability/store-filter parity that landed
this session (step 4b), and add step 4c: an import-harness-configs endpoint +
import UI mirroring templates, deferred to a future session. Update the
"We are here" status accordingly.
* feat(hub): harness-config import endpoint + web UI (step 4c)
Closes the last template/harness-config parity gap from the
resource-storage-refactor. Harness-configs can now be imported into the
Hub from a Git URL or workspace path, mirroring import-templates.
- Hub: handleProjectImportHarnessConfigs + import-harness-configs route;
importHarnessConfigsFromRemote/importHarnessConfigsFromWorkspace reuse
the shared harnessConfigStore Bootstrap. syncExistingHarnessConfig
gains a force param for reconcile-on-import.
- Web: Harness Configs tab in Project Settings gains the same
URL/workspace import controls as templates.
- Tests: workspace-import unit tests in harness_config_bootstrap_test.go.
- Docs: mark step 4c done in resource-storage-refactor.md.
* style: gofmt alignment after pkg/broker→pkg/eventbus rename
Pure gofmt (struct-tag/map alignment + import ordering) for files left
unformatted by the pkg/broker→pkg/eventbus rename on main (GoogleCloudPlatform#277). No
logic changes; restores a green fmt-check after rebasing onto main.
* fix: address PR GoogleCloudPlatform#285 review feedback
- Fix path traversal vulnerability in workspace validation using
filepath.Rel instead of strings.HasPrefix (security-high)
- Add mutual exclusion check for sourceUrl/workspacePath in import API
- Add nil checks for storage backend, downloadResp, cfg, harnessConfig,
template, and harnessConfigToRecord to prevent nil pointer panics
- Fix cache Get to require index entry, preventing untracked cache hits
and disk space leaks from eviction bypass
- Write cached templates atomically via tmp+rename for crash safety
- Restrict ProjectID SQLite filter to project/grove scopes only
* fix(lint): check os.RemoveAll errors flagged by errcheck
ci-full's golangci-lint surfaced unchecked os.RemoveAll returns:
- atomic-write cleanup paths in templatecache cache.Store (from the
PR GoogleCloudPlatform#285 review-feedback cherry-pick)
- defer os.RemoveAll(cachePath) in the remote import paths for both
templates and harness-configs
Cleanup-on-error sites where the original error is the one returned;
ignore explicitly to satisfy errcheck. No behavior change.
39847ca to
cddae16
Compare
…ports (GoogleCloudPlatform#260) * fix(hub): support project GITHUB_TOKEN secret fallback for remote template imports * address PR review feedback: drop redundant db fallback, simplify tests, and scope constants --------- Co-authored-by: Preston Holmes <ptone@google.com>
…dPlatform#244) Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 6.1.8 to 6.3.2. - [Release notes](https://github.com/withastro/astro/releases) - [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md) - [Commits](https://github.com/withastro/astro/commits/astro@6.3.2/packages/astro) --- updated-dependencies: - dependency-name: astro dependency-version: 6.3.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…oudPlatform#246) Bumps [devalue](https://github.com/sveltejs/devalue) from 5.6.4 to 5.8.1. - [Release notes](https://github.com/sveltejs/devalue/releases) - [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md) - [Commits](sveltejs/devalue@v5.6.4...v5.8.1) --- updated-dependencies: - dependency-name: devalue dependency-version: 5.8.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…oogleCloudPlatform#287) * Integrate Scion explainer slideshow directly into landing page Replace the iframe-embedded slide deck with a fully inline, interactive slideshow component. All 7 slides from the hosted deck are now rendered directly in the landing page with scoped CSS and inline JavaScript. Key changes: - Full slideshow with keyboard/touch/click navigation, progress dots, and animated transitions - All interactive widgets recreated inline: State Model Simulator, Collaborators Graph (with dynamic spawn/delete loop), Reactive Notification Wakeup flow, and Shared Filesystem Simulator - Replace "Boot" with "Run" throughout all slide content - Remove Overview Deck iframe section; add "technical deep dive" link to the Google Slides deck below the inline slideshow - Move "How it works" quickstart section below "See it in Action" video - Scope all slideshow CSS under .scion-deck to avoid style conflicts with the landing page * Update project log for inline slideshow integration
Add channel-aware message routing so broker plugins (Telegram, Google Chat, etc.) can tag messages with a channel name and optional thread ID. The FanOutEventBus routes channel-tagged messages only to the matching bus and the in-process bus, while untagged messages fan out to all buses as before. Includes CLI --channel/--thread-id flags, a /message-channels API endpoint, and client library support.
…rocess channel - Channel-targeted messages now publish to InProcessBus and the matched channel concurrently (via goroutines), preventing slow external RPC calls from blocking HTTP handlers - Reject msg.Channel="inprocess" with a clear error — inprocess is a reserved internal bus, not a user-visible channel - Unmatched channels now fail fast before publishing to any bus - Add test for reserved inprocess channel rejection
…utbound messages - Channel-targeted publish now checks target.Observer: observer channel errors are logged but not returned, consistent with the fan-out path - Add StructuredMessage.Validate() call in handleAgentOutboundMessage before publishing, catching invalid Channel/ThreadID combinations - Add test for observer error suppression in channel-targeted path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements chat channel routing for the message broker system, allowing messages to be directed to specific broker plugins instead of fan-out to all. Closes #113.
ChannelandThreadIDfields toStructuredMessage,deliveryMessage, andstore.Messagewith validation (channel format, thread requires channel)FanOutBroker.Publish()now routes to the matching broker plugin whenChannelis set, while preserving fan-out behavior when empty. InProcessBroker always receives for local dispatch. Errors on unmatched channels.Channel: "telegram"andThreadIDfrom forum topics. Chat-app setsChannel: "gchat". Web UI defaults toChannel: "web". Broker-log logs channel/thread fields.--channeland--thread-idflags toscion message. Addedscion message channelssubcommand andGET /api/v1/message-channelsendpoint for channel discovery.Design doc
See
.design/chat-channel-routing.mdfor full design rationale and phased implementation plan.Commits
ae49d05— Core message schema (Channel/ThreadID fields + validation + tests)8c6e467— Channel-aware routing in FanOutBroker (+ 4 routing tests)1e7e1f0— Inbound channel tagging (telegram, gchat, web, broker-log)cb0952b— CLI flags, message-channels API, channels subcommandTest plan
pkg/messages,pkg/broker,pkg/hub,pkg/store,pkg/hubclient)go build ./...compiles cleanlyscion message channelslists registered broker pluginsscion message agent:x "msg" --channel telegramroutes only to telegram pluginchannel: "telegram"in delivery