Skip to content

docs(concepts): capture the comms model (threads vs session log) (RIG-2717) - #624

Open
rigel-mintaka wants to merge 2 commits into
mainfrom
compass-server/rig-2717-comms-model-concept
Open

docs(concepts): capture the comms model (threads vs session log) (RIG-2717)#624
rigel-mintaka wants to merge 2 commits into
mainfrom
compass-server/rig-2717-comms-model-concept

Conversation

@rigel-mintaka

@rigel-mintaka rigel-mintaka commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

This PR is part of a stack containing 2 PRs:

  1. main
  2. "docs(concepts): capture the comms model (threads vs session log) (RIG-2717)" (this PR)
  3. docs(product): Linear Agent Session responder design (RIG-2717) #625

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

@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

RIG-2717

@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2717-comms-model-concept branch from f1343e8 to 6ba8e85 Compare August 25, 2026 20:52
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-server-rig-2717-comm.compass-eng-docs.pages.dev

Deployed from compass-server/rig-2717-comms-model-concept at 3ae8486.

Changed pages:

@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2717-comms-model-concept branch from 6ba8e85 to 1a5ece4 Compare August 25, 2026 21:08
rigel-mintaka added a commit that referenced this pull request Aug 25, 2026
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>
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2717-comms-model-concept branch from 1a5ece4 to 88e08df Compare August 25, 2026 21:56
rigel-mintaka added a commit that referenced this pull request Aug 25, 2026
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>
@mattwilkinsonn

mattwilkinsonn commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

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.

rigel-mintaka added a commit that referenced this pull request Aug 25, 2026
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>
@rigelbuild-bot

Copy link
Copy Markdown
Contributor

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 689cdd89.

@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2717-comms-model-concept branch from 689cdd8 to 3ae8486 Compare August 25, 2026 22:13
rigel-mintaka added a commit that referenced this pull request Aug 25, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants