docs(concepts): capture the comms model (threads vs session log) (RIG-2717) - #624
docs(concepts): capture the comms model (threads vs session log) (RIG-2717)#624rigel-mintaka wants to merge 2 commits into
Conversation
f1343e8 to
6ba8e85
Compare
|
Compass engineering docs preview: https://compass-server-rig-2717-comm.compass-eng-docs.pages.dev Deployed from Changed pages: |
6ba8e85 to
1a5ece4
Compare
Spike **design record** for bridging Linear's Agent Session protocol into Compass — making the installed `Compass` Linear app (RIG-2682) a live participant when an issue is delegated to it. Stacked on #624 (the comms-model concept doc), which this record's premise cites. ## The premise this record is built on Matt's steer, now captured in #624 and load-bearing here: **Compass's comms model is deliberate, and Linear's Agent Session model is what it rejects.** Compass has stable agents (Managers + peers) each with a home channel, all *communication* on Zulip-style threads, and the agent's *work* (streamed reasoning, tool calls, code) kept OUT of the threads in a side-panel session log. Linear's Agent Session is the opposite — one ephemeral session per issue, no stable agent, everything dumped in one flat activity log. So we are **NOT** adopting the Agent Session model wholesale. A Linear delegation is *mapped onto* the Compass model: routed to a stable owning agent, its conversation carried as a topic in that agent's home channel. (An earlier draft of this record had the premise backwards — it framed Compass's model as "ad-hoc assign-back-and-forth" and the spike as "validate whether Linear's session model carries our workflow." Corrected.) ## The central open question — OQ-1 (Matt's call) How deep the bridge goes. A hard constraint forces the decision: **Linear's Agent Activity vocabulary is a fixed, server-validated set of five types** (`thought`/`action`/`elicitation`/`response`/`error`) with no extension point, so a Compass structured `ask` (question + discrete options) **cannot** round-trip — it degrades to a flat-markdown `elicitation`, losing its options. - **Option A — full relay (bidirectional).** Mirror the Compass conversation into the Linear session. Richest, but shows a degraded conversation and pulls in a settle-observability blocker (OQ-9) + session-lifecycle question (OQ-10). - **Option B — dumb link (defer to Compass) [recommended].** Emit one `thought` + an `externalUrls` "Open in Compass" deep link (natively supported) to the mapped Manager topic; all real interaction happens in Compass's own surface where the typed blocks work. Deletes the relay (Part 3 / T6 / T7) and dissolves OQ-9/OQ-10. - **Option C — hybrid.** Dumb link + coarse one-way status back, no message-level mirroring. The Approach + Plan are written for Option A (the maximal case, so the blockers surface concretely); Options B/C are strictly subsets — choosing them deletes tasks, not adds. Everything from "the activity relay" onward is Option-A-only and contingent on OQ-1. ## Provenance Drafted by the `design` subagent, red-teamed by a `design-critic` pass (10 findings: 2 blockers, 6 should-fix, 2 nits — both blockers resolved: F2 dedup folded to `client_request_id` idempotency, F1 settle-observability elevated to OQ-9), then premise-corrected per Matt's steer and re-structured around OQ-1. ## Open Questions (Matt rules before freeze) Ten load-bearing OQs, each with the designer's recommendation: - **OQ-1** mapping depth — full relay / dumb link / hybrid (rec: Option B) — the central fork - OQ-2 endpoint placement (rec: reuse the network door) - OQ-3 which agent runs a session (rec: single config-pinned responder; route-to-owning-Manager later) - OQ-4 session↔run mapping (rec: topic in home channel) - OQ-5 secret names + rotation (rec: short-TTL lazy resolve) - OQ-6 streaming cadence (rec: settled-messages-only; downstream of OQ-9) - OQ-7 spike vs production rigor (rec: production shape, staged) - OQ-8 elicitation answers free-text vs structured (rec: free text) - OQ-9 how the relay observes turn-settle (rec: widen `SetSettleSink` to multicast) — Option-A-only - OQ-10 Linear session lifecycle semantics (rec: observe empirically) — Option-A-only ## Ledger-impact Adds DL-253 (webhook receiver on the network door) and DL-254 (AgentSession → home-channel-topic mapping via the `@linear` bridge; return path gated on OQ-1) to `docs/designs/DECISIONS.md` at freeze. Rows proposed in the record's `## Ledger-impact`; the driver applies them with freeze-date + attribution at merge. This PR is pure design and does not edit `DECISIONS.md` — the touch-coupling gate is satisfied by this declared `Ledger-impact:` line. Spec-impact: none. Refs RIG-2717. Co-authored-by: Matt Wilkinson <matt@rigel.build>
…-2717) Capture Compass's comms model as a first-class `concepts/` doc: **threads for conversation, the session log for work**. ## Why The premise is load-bearing and already frozen across the ledger (DL-098 Zulip threading, DL-099 streamed-turn-writes-nothing-to-comms, DL-088 log-and-trace are two projections of one artifact, DL-158 workspace = home channel + session trace) — but it lived only in those scattered design records, never stated as one discoverable idea in the `concepts/` layer whose charter is exactly "the load-bearing ideas the code and prompts assume you already hold." The gap bites when bridging an external agent-session protocol in: the reflex is to adopt that system's flat single-session-per-task-with-everything-in-one-log shape, which is precisely the model Compass's threads/log split exists to reject. This doc states the premise plainly so an integration maps onto the Compass model rather than replacing it. ## What - New `docs/concepts/comms-model.md`: the two surfaces (session log = work; channels/threads = communication), why the split is structural not a prompting convention, stable agents with home channels, typed comms blocks (`ask` and planned kinds), and the explicit contrast an external-session bridge must respect. - `docs/concepts/README.md`: new "The comms model" section, placed before "The tools" (which references it). Spec-impact: none. Ledger-impact: none. Refs RIG-2717. Co-authored-by: Matt Wilkinson <matt@rigel.build>
1a5ece4 to
88e08df
Compare
Production **design record** for making the installed `Compass` Linear app (RIG-2682) a live Linear Agent App: receive Agent Session webhooks, route a delegated issue to the right stable Compass Manager, carry the conversation as a topic in that Manager's home channel, and link the Linear session back out to Compass. Stacked on #624 (the comms-model concept doc), which this record's premise cites. All ten open questions were ruled by Matt on 2026-08-25 (RIG-2729); this record folds every ruling in and freezes with no live open questions. ## The premise this record is built on Matt's steer, captured in #624 and load-bearing here: **Compass's comms model is deliberate, and Linear's Agent Session model is what it rejects.** Compass has stable agents (Managers + peers) each with a home channel; communication rides Zulip-style threads (channel → named topics) and the agent's *work* stays out of the threads in a side-panel session log. Linear's Agent Session is the opposite — one ephemeral flat activity log per issue, no stable agent, no threads. So the Linear session is a doorway, not a home. ## The decision — Option B (dumb link), Matt ruled RIG-2729 A hard constraint forces the mapping-depth choice: **Linear's Agent Activity vocabulary is a fixed, server-validated set of five types** (`thought`/`action`/`elicitation`/`response`/`error`) with no extension point, so Compass's typed comms blocks (a structured `ask` with discrete options) cannot round-trip through a Linear session. - **Option A — full relay (bidirectional).** Mirror the Compass conversation into the Linear session. Richest, but shows a degraded conversation. **Off-table** until Linear makes the activity format configurable. - **Option B — dumb link (chosen).** On `created`, emit one `thought` + an `externalUrls` "Open in Compass" deep link to the resolved Manager's home channel; all real interaction happens in Compass. Deletes the entire return relay. - **Option C — hybrid (ratified follow-up).** Dumb link + a coarse one-way status back, no message-level mirroring. ## What the bridge does 1. **Receive** — a plain `POST /webhooks` `http.Handler` on the network TLS door, fail-closed HMAC-SHA256 signature check (bad/missing → 400; stale-but-valid timestamp → 200-with-drop, never a retry-burning 400), ack 200 before any async work. 2. **Route** (Matt ruled routing is in scope, OQ-3) — resolve the delegated issue to a stable Manager keyed on Compass's **recorded** forge ownership index (`forge_authored_artifacts`, DL-055/DL-205), **never** a header parsed from forge text (DL-050/DL-094 forbid that reaching a routing decision). An unstamped issue routes to the supervisor via a dedicated routing channel, which decides the lane and stamps it. 3. **Map** (Matt ruled not 1-1, OQ-4) — the prompt lands in an issue-named comms topic in the Manager's home channel (sessions on one issue coalesce), delivered as `@linear`-authored messages on the durable deliver rail, deduped by `PostAsAccount`'s `client_request_id` on the `Linear-Delivery` UUID. The deep link targets the **home channel**, not the topic. 4. **Link back** — the two `created` emits (the ack `thought` + the session external URL) are the whole return path. No relay, no settle observation, no session-lifecycle machine. The public base URL (webhook host + deep-link base) is **per-deployment** (Matt ruled OQ-2): managed = `compass.rigel.build`, self-host = its own URL, dev/tailnet deploys need their own ingress. ## Resolved decisions (all ten OQs, Matt 2026-08-25) OQ-1 Option B (C follow-up, A off-table) · OQ-2 network door + per-deployment public URL · OQ-3 routing in scope, keyed on the recorded ownership index · OQ-4 not 1-1, link to the home channel · OQ-5 secret names as designed · OQ-6/8/9/10 dissolve under B (no relay → no streaming cadence, no Linear elicitation, no settle-edge observability, no session-lifecycle machine) · OQ-7 production, not a spike. ## Ledger-impact Adds DL-254 (webhook receiver on the network door + per-deployment URL), DL-255 (routing keyed on the recorded ownership index, never a parsed header), and DL-256 (Option-B dumb-link return path + not-1-1 topic mapping) to `docs/designs/DECISIONS.md` at freeze. Highest row on current main is DL-253; the driver re-verifies the next-free id at freeze. Spec-impact: none. Refs RIG-2717. Co-authored-by: Matt Wilkinson <matt@rigel.build>
|
Needs to also make clear that the user cannot prompt directly into the agent's session, at all. Everything goes over comms through the channels and threads. This is deliberate for the reasons captured but is a significant departure from standard agent models and a lot of assumptions about how those work have to be made aware of this change. The session log is a live look, streaming output, and the only methods of communication to that session are in the threads, pinging the agent for a steering message, and then the ability to stop the agent if needed. |
Production **design record** for making the installed `Compass` Linear app (RIG-2682) a live Linear Agent App: receive Agent Session webhooks, route a delegated issue to the right stable Compass Manager, carry the conversation as a topic in that Manager's home channel, and link the Linear session back out to Compass. Stacked on #624 (the comms-model concept doc), which this record's premise cites. All ten open questions were ruled by Matt on 2026-08-25 (RIG-2729); this record folds every ruling in and freezes with no live open questions. ## The premise this record is built on Matt's steer, captured in #624 and load-bearing here: **Compass's comms model is deliberate, and Linear's Agent Session model is what it rejects.** Compass has stable agents (Managers + peers) each with a home channel; communication rides Zulip-style threads (channel → named topics) and the agent's *work* stays out of the threads in a side-panel session log. Linear's Agent Session is the opposite — one ephemeral flat activity log per issue, no stable agent, no threads. So the Linear session is a doorway, not a home. ## The decision — Option B (dumb link), Matt ruled RIG-2729 A hard constraint forces the mapping-depth choice: **Linear's Agent Activity vocabulary is a fixed, server-validated set of five types** (`thought`/`action`/`elicitation`/`response`/`error`) with no extension point, so Compass's typed comms blocks (a structured `ask` with discrete options) cannot round-trip through a Linear session. - **Option A — full relay (bidirectional).** Mirror the Compass conversation into the Linear session. Richest, but shows a degraded conversation. **Off-table** until Linear makes the activity format configurable. - **Option B — dumb link (chosen).** On `created`, emit one `thought` + an `externalUrls` "Open in Compass" deep link to the resolved Manager's home channel; all real interaction happens in Compass. Deletes the entire return relay. - **Option C — hybrid (ratified follow-up).** Dumb link + a coarse one-way status back, no message-level mirroring. ## What the bridge does 1. **Receive** — a plain `POST /webhooks` `http.Handler` on the network TLS door, fail-closed HMAC-SHA256 signature check (bad/missing → 400; stale-but-valid timestamp → 200-with-drop, never a retry-burning 400), ack 200 before any async work. 2. **Route** (Matt ruled routing is in scope, OQ-3) — resolve the delegated issue to a stable Manager keyed on Compass's **recorded** forge ownership index (`forge_authored_artifacts`, DL-055/DL-205), **never** a header parsed from forge text (DL-050/DL-094 forbid that reaching a routing decision). An unstamped issue routes to the supervisor via a dedicated routing channel, which decides the lane and stamps it. 3. **Map** (Matt ruled not 1-1, OQ-4) — the prompt lands in an issue-named comms topic in the Manager's home channel (sessions on one issue coalesce), delivered as `@linear`-authored messages on the durable deliver rail, deduped by `PostAsAccount`'s `client_request_id` on the `Linear-Delivery` UUID. The deep link targets the **home channel**, not the topic. 4. **Link back** — the two `created` emits (the ack `thought` + the session external URL) are the whole return path. No relay, no settle observation, no session-lifecycle machine. The public base URL (webhook host + deep-link base) is **per-deployment** (Matt ruled OQ-2): managed = `compass.rigel.build`, self-host = its own URL, dev/tailnet deploys need their own ingress. ## Resolved decisions (all ten OQs, Matt 2026-08-25) OQ-1 Option B (C follow-up, A off-table) · OQ-2 network door + per-deployment public URL · OQ-3 routing in scope, keyed on the recorded ownership index · OQ-4 not 1-1, link to the home channel · OQ-5 secret names as designed · OQ-6/8/9/10 dissolve under B (no relay → no streaming cadence, no Linear elicitation, no settle-edge observability, no session-lifecycle machine) · OQ-7 production, not a spike. ## Ledger-impact Adds DL-254 (webhook receiver on the network door + per-deployment URL), DL-255 (routing keyed on the recorded ownership index, never a parsed header), and DL-256 (Option-B dumb-link return path + not-1-1 topic mapping) to `docs/designs/DECISIONS.md` at freeze. Highest row on current main is DL-253; the driver re-verifies the next-free id at freeze. Spec-impact: none. Refs RIG-2717. Co-authored-by: Matt Wilkinson <matt@rigel.build>
|
Folded — added a "The session log is read-only — you never prompt into a session" section. It states there is no direct prompt-into-session path, calls out that this is a deliberate departure from the standard prompt-into-the-session REPL model that a lot of assumptions have to be re-expressed against, and names the only three ways to reach a live session: post in a thread, ping for a steering message, or stop the agent. Additive commit |
689cdd8 to
3ae8486
Compare
Production **design record** for making the installed `Compass` Linear app (RIG-2682) a live Linear Agent App: receive Agent Session webhooks, route a delegated issue to the right stable Compass Manager, carry the conversation as a topic in that Manager's home channel, and link the Linear session back out to Compass. Stacked on #624 (the comms-model concept doc), which this record's premise cites. All ten open questions were ruled by Matt on 2026-08-25 (RIG-2729); this record folds every ruling in and freezes with no live open questions. ## The premise this record is built on Matt's steer, captured in #624 and load-bearing here: **Compass's comms model is deliberate, and Linear's Agent Session model is what it rejects.** Compass has stable agents (Managers + peers) each with a home channel; communication rides Zulip-style threads (channel → named topics) and the agent's *work* stays out of the threads in a side-panel session log. Linear's Agent Session is the opposite — one ephemeral flat activity log per issue, no stable agent, no threads. So the Linear session is a doorway, not a home. ## The decision — Option B (dumb link), Matt ruled RIG-2729 A hard constraint forces the mapping-depth choice: **Linear's Agent Activity vocabulary is a fixed, server-validated set of five types** (`thought`/`action`/`elicitation`/`response`/`error`) with no extension point, so Compass's typed comms blocks (a structured `ask` with discrete options) cannot round-trip through a Linear session. - **Option A — full relay (bidirectional).** Mirror the Compass conversation into the Linear session. Richest, but shows a degraded conversation. **Off-table** until Linear makes the activity format configurable. - **Option B — dumb link (chosen).** On `created`, emit one `thought` + an `externalUrls` "Open in Compass" deep link to the resolved Manager's home channel; all real interaction happens in Compass. Deletes the entire return relay. - **Option C — hybrid (ratified follow-up).** Dumb link + a coarse one-way status back, no message-level mirroring. ## What the bridge does 1. **Receive** — a plain `POST /webhooks` `http.Handler` on the network TLS door, fail-closed HMAC-SHA256 signature check (bad/missing → 400; stale-but-valid timestamp → 200-with-drop, never a retry-burning 400), ack 200 before any async work. 2. **Route** (Matt ruled routing is in scope, OQ-3) — resolve the delegated issue to a stable Manager keyed on Compass's **recorded** forge ownership index (`forge_authored_artifacts`, DL-055/DL-205), **never** a header parsed from forge text (DL-050/DL-094 forbid that reaching a routing decision). An unstamped issue routes to the supervisor via a dedicated routing channel, which decides the lane and stamps it. 3. **Map** (Matt ruled not 1-1, OQ-4) — the prompt lands in an issue-named comms topic in the Manager's home channel (sessions on one issue coalesce), delivered as `@linear`-authored messages on the durable deliver rail, deduped by `PostAsAccount`'s `client_request_id` on the `Linear-Delivery` UUID. The deep link targets the **home channel**, not the topic. 4. **Link back** — the two `created` emits (the ack `thought` + the session external URL) are the whole return path. No relay, no settle observation, no session-lifecycle machine. The public base URL (webhook host + deep-link base) is **per-deployment** (Matt ruled OQ-2): managed = `compass.rigel.build`, self-host = its own URL, dev/tailnet deploys need their own ingress. ## Resolved decisions (all ten OQs, Matt 2026-08-25) OQ-1 Option B (C follow-up, A off-table) · OQ-2 network door + per-deployment public URL · OQ-3 routing in scope, keyed on the recorded ownership index · OQ-4 not 1-1, link to the home channel · OQ-5 secret names as designed · OQ-6/8/9/10 dissolve under B (no relay → no streaming cadence, no Linear elicitation, no settle-edge observability, no session-lifecycle machine) · OQ-7 production, not a spike. ## Ledger-impact Adds DL-254 (webhook receiver on the network door + per-deployment URL), DL-255 (routing keyed on the recorded ownership index, never a parsed header), and DL-256 (Option-B dumb-link return path + not-1-1 topic mapping) to `docs/designs/DECISIONS.md` at freeze. Highest row on current main is DL-253; the driver re-verifies the next-free id at freeze. Spec-impact: none. Refs RIG-2717. Co-authored-by: Matt Wilkinson <matt@rigel.build>
This PR is part of a stack containing 2 PRs:
mainCapture Compass's comms model as a first-class
concepts/doc: threads for conversation, the session log for work.Why
The premise is load-bearing and already frozen across the ledger (DL-098 Zulip threading, DL-099 streamed-turn-writes-nothing-to-comms, DL-088 log-and-trace are two projections of one artifact, DL-158 workspace = home channel + session trace) — but it lived only in those scattered design records, never stated as one discoverable idea in the
concepts/layer whose charter is exactly "the load-bearing ideas the code and prompts assume you already hold."The gap bites when bridging an external agent-session protocol in: the reflex is to adopt that system's flat single-session-per-task-with-everything-in-one-log shape, which is precisely the model Compass's threads/log split exists to reject. This doc states the premise plainly so an integration maps onto the Compass model rather than replacing it.
What
docs/concepts/comms-model.md: the two surfaces (session log = work; channels/threads = communication), why the split is structural not a prompting convention, stable agents with home channels, typed comms blocks (askand planned kinds), and the explicit contrast an external-session bridge must respect.docs/concepts/README.md: new "The comms model" section, placed before "The tools" (which references it).Spec-impact: none.
Ledger-impact: none.
Refs RIG-2717.
Co-authored-by: Matt Wilkinson matt@rigel.build