From 036a8e61b6b9298a00e9682d9710bbd6ca91b8bd Mon Sep 17 00:00:00 2001 From: mintaka Date: Sat, 29 Aug 2026 21:18:10 -0400 Subject: [PATCH] docs(product): apply peer-DM ledger delta DL-283..289 (RIG-2956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Freeze follow-up to #726 (merged 774adb9a): the peer-DM record deferred its ledger delta out of the design PR via Ledger-impact: deferred to freeze. This applies the seven rows to docs/designs/DECISIONS.md now that the record is frozen, and flips the record Status Draft -> Active. - DL-283 name addressing (channels + topics by name; extends DL-269/DL-270) - DL-284 reply routing (steer/deliver carry source channel+topic; RIG-2956 T0) - DL-285 topic gate (create_topic required; amends DL-098 get-or-create) - DL-286 DM path (OpenDM; generalizes DL-190) - DL-287 DM namespace defense (reserved group create-guard) - DL-288 DM shape (kind=DM; GROUP_DM retired reserve-not-delete) - DL-289 scope (same-owner MVP) Purely additive: no existing row superseded (DL-098 amended, stays Active; GROUP_DM is a proto enum, not a ledger row). Record §Ledger delta renumbered from DL-new-N placeholders to the applied DL-283..289. Ledger-impact: applies DL-283..289 (peer-DM freeze delta) Co-authored-by: Matt Wilkinson --- docs/designs/DECISIONS.md | 7 ++++++ .../product/compass-agent-peer-dm/design.md | 22 +++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/designs/DECISIONS.md b/docs/designs/DECISIONS.md index 1196119a..92f8d9ff 100644 --- a/docs/designs/DECISIONS.md +++ b/docs/designs/DECISIONS.md @@ -167,6 +167,13 @@ check enforces the mechanical half. Full rationale: | DL-226 | A message owed to an OFFLINE channel agent member is never silently stranded: the server wakes the member by RESUMING its most recent session (a system-authorized internal sibling of the public resume path, via a new `LatestSessionForAccount` read; fresh start only for a never-started agent; per-agent singleflight + not-live pre-check as cost control — agent-triggered starts are a core product capability, and `StartAgentSession`'s adminOnly PUBLIC door is untouched). Durability split: a mentioned member outside the sweep set (unsubscribed, non-home, non-mandatory) gets a durable owed-mention row `(agent_account_id, message_id)` written at the settle edge before the wake and swept subscription-independently on session start AS A STEER (D5 mention→steer preserved; steer-only precedence intact), cleared on the frozen message_id ack; a subscribed/home/mandatory member gets no owed row — the D2 cursor sweep is its durable backstop and the wake (both the mention arm and the plain-deliver arm) is pure latency. Broadcast mentions (`@everyone`/`@agents`) wake too; N-start amplification accepted. The residual pre-settle window is accepted for MVP (RIG-2490). Amends D5/OQ-3's offline clause by citation | Active (Matt, 2026-08-21); residual pre-settle window closed by DL-240 (RIG-2490, 2026-08-23) | [mention offline redelivery §Decisions](product/compass-mention-offline-redelivery.md#decisions-ruled-by-matt-2026-08-21) | | DL-240 | The RIG-1641 OQ-5 residual pre-settle mention-loss window (a mention to an offline out-of-sweep-set member, lost when a crash or bus overrun drops `MessagePosted` before the settle-edge `routeMentions` pass) is closed by a per-message delivery marker, NOT a high-water cursor: a nullable `messages.mentions_routed_at BIGINT` (unix ms) set by the consumer after the settle-edge pass, with a recovery scan at consumer start + the `Lagged()` overrun re-subscribe reading `WHERE mentions_routed_at IS NULL` (skipping message_ids in `c.held`), replaying the ONE shared `routeMentions` body, then marking. The killed cursor could advance past a LOWER in-flight seq (`messages.seq` is `BIGSERIAL` assigned at INSERT but the bus event publishes post-commit, `comms.go:361-375`) whose crash-dropped event then stranded the mention forever — a new silent loss the marker cannot express. Seed-forward on first deploy (no historical backfill; compass pre-live). Extends DL-226's no-loss invariant from the settle edge back to the message-post commit; DL-226's wake+owed-mention mechanism is unchanged | Active (Matt, 2026-08-23) | [mention offline pre-settle closure §Resolved decisions](product/compass-mention-offline-redelivery-pre-settle-closure.md#resolved-decisions) | | DL-241 | The agent forge native toolset is ten single-purpose tools, one per `ForgeCallRequest` arm (`forge_get_issue`, `forge_get_pull_request`, `forge_list_issues`, `forge_comment_on_issue`, `forge_comment_on_pull_request`, `forge_submit_review`, `forge_create_issue`, `forge_create_pull_request`, `forge_subscribe`, `forge_unsubscribe`), each a native `AgentTool` over a thin `ForgeBroker` on the `RunnerTransport.forge()` seam; `forge_subscribe`/`forge_unsubscribe` ship the complete surface now but return the server's in-band `unimplemented` until the poll-driver lane lands the `agent_forge_subscriptions` writer (DL-163). Multi-forge is exposed: every tool takes an optional forge selector (`forge_provider` + optional `forge_host`, unset = the configured default GitHub forge, DL-202) so an agent targets Linear (issues-only, `repo` = team key, DL-051; PR/review arms return in-band `unimplemented`) as well as GitHub. Mirrors DL-212's comms-toolset count row; the tool-count claim is load-bearing for future toolset-refresh rows | Active (Matt, 2026-08-24) | [forge tools §Tool set and shape](product/compass-agent-forge-tools/design.md#tool-set-and-shape--ten-native-tools-one-per-arm) | +| DL-283 | Agents and client UIs address channels and topics by NAME, not id — request-input channel/topic fields on the agent tool surface are name-typed, resolved viewer-scoped at the service edge (`ChannelByNameForViewer`: miss ≡ invisible ≡ merged `not_found`; a multi-hit errors `invalid_argument`, never auto-picks); response/stored/event fields stay id-typed. Extends DL-269/DL-270 from accounts to channels + topics. | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | +| DL-284 | Steer/deliver control ops denormalize the source channel name + topic name (server-resolved at wrap in `deliverOp`/`steerOp`, the `from_handle` pattern; a name-resolve miss degrades like a handle miss, never blocks delivery); the agent renders `Channel › topic :` and must name both on every post — `comms_post_message` has NO home-channel default and never auto-picks a reply target. The dogfood reply-mis-routing fix (RIG-2956 T0). Proto: `DeliverControl.channel_name = 5`, `SteerControl.topic_name = 4`/`channel_name = 5`. | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | +| DL-285 | Creating a topic requires `create_topic: true` on the post, in every channel including DMs (`PostMessageRequest.create_topic = 6`; `TopicRef.Create` gates the store name path) — amends DL-098's topic get-or-create clause (the Zulip threading model stays live; only auto-mint-on-miss is gated). A gated miss errors in-band `not_found`, never mints and never drops the message. Escape hatch (Matt): may revert for DMs only if it proves a blocker. | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | +| DL-286 | Agent↔agent DMs are auto-created (resolve-if-exists) by a single server-side `OpenDM` op — deterministic name `dm----` in a per-owner reserved DM group — exposed publicly on `CommsService.OpenDM`, to agents as `comms_open_dm` (by peer handle) and the `comms_dm` direct-send composite (open + post in one call), and auto-opened manager↔peer at spawn (`SpawnPeerResponse.dm_channel_name = 4`). Generalizes DL-190's per-pair manager↔peer DM to any same-owner agent pair. | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | +| DL-287 | The reserved per-owner DM group is server-enforced: `CreateChannel` refuses any create targeting it (merged `not_found`) — only the OpenDM path writes there (primary squat defense); the resume/upsert path additionally verify-reconciles DM invariants (kind=DM ∧ mandatory ∧ both parties ⊆ members) as belt. | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | +| DL-288 | A peer-DM is born `kind=DM`, exactly two agent parties, `post_policy=OPEN` (ownerless), `mandatory_subscription=true`, members = both agents + pulled-in owner(s) (every member a delivery target from birth). A genuine member ADD converts it in the same tx to a named `kind=CHANNEL` (`UpdateChannelMembersRequest.convert_channel_name = 6`, required; leaves the reserved group, freeing the DM name); a remove below two agent parties errors. `CHANNEL_KIND_GROUP_DM = 2` is retired reserve-not-delete (enum number stays, deprecated, never produced; deleting it is buf-breaking). The only kinds are CHANNEL and DM. | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | +| DL-289 | Peer-DM scope is same-owner for MVP (the wave-shares-one-owner frame); a cross-owner peer handle is the merged in-band `not_found`. Cross-owner DMs are deferred to the bilateral owner-peering authz edge (RIG-2796-class). | Active (Matt, 2026-08-30) | [peer-DM §Ledger delta](product/compass-agent-peer-dm/design.md#ledger-delta) | ## Agent roles & prompts diff --git a/docs/designs/product/compass-agent-peer-dm/design.md b/docs/designs/product/compass-agent-peer-dm/design.md index 2edd531b..d94e6177 100644 --- a/docs/designs/product/compass-agent-peer-dm/design.md +++ b/docs/designs/product/compass-agent-peer-dm/design.md @@ -1,6 +1,6 @@ # Compass agent peer-DM — name-addressed comms + auto-created two-way agent↔agent DMs -Status: Draft +Status: Active Owner: agents Amends: compass-agent-org-mgmt-tools, compass-manager-comms-substrate, compass-zulip-threading-model (two explicit amendments, §Approach), and extends @@ -852,37 +852,37 @@ Interfaces: none new — consumes T0-T3. ## Ledger delta -Ledger-impact: deferred to freeze. Do not edit DECISIONS.md in this PR; the -caller applies these rows at freeze: +Ledger-impact: applied at freeze (2026-08-30) as DL-283..289 in +`docs/designs/DECISIONS.md` §Comms & tools: -- **DL-new-1 (name addressing):** Agents and client UIs address channels and +- **DL-283 (name addressing):** Agents and client UIs address channels and topics by NAME; request-input channel/topic fields on the agent tool surface are name-typed, resolved viewer-scoped at the service edge (unknown ≡ invisible ≡ merged `not_found`; ambiguous errors, never auto-picks); response/stored/event fields stay id-typed. Extends DL-269/DL-270 from accounts to channels + topics. -- **DL-new-2 (reply routing):** Steer/deliver control ops denormalize the +- **DL-284 (reply routing):** Steer/deliver control ops denormalize the source channel name + topic name (server-resolved at wrap, the `from_handle` pattern); the agent renders both and must name both on every post — `comms_post_message` has NO home-channel default and never auto-picks a reply target. -- **DL-new-3 (topic gate):** Creating a topic requires `create_topic: true` +- **DL-285 (topic gate):** Creating a topic requires `create_topic: true` on the post, in every channel including DMs — amends the zulip-threading-model get-or-create ruling; a gated miss errors in-band, never mints and never drops the message. (Escape hatch per Matt: may be reverted for DMs only if it proves a blocker.) -- **DL-new-4 (DM path):** Agent↔agent DMs are auto-created +- **DL-286 (DM path):** Agent↔agent DMs are auto-created (resolve-if-exists) by a single server-side `OpenDM` op — deterministic name `dm----` in a per-owner reserved DM group — exposed publicly on `CommsService`, to agents as `comms_open_dm` (by peer handle), as the `comms_dm` direct-send composite (open + post in one call), and auto-opened manager↔peer at spawn (`SpawnPeerResponse.dm_channel_name`). -- **DL-new-5 (DM namespace defense):** The reserved DM group is +- **DL-287 (DM namespace defense):** The reserved DM group is server-enforced: `CreateChannel` refuses any create targeting it (merged `not_found`) — only the OpenDM path writes there; the resume path additionally verify-reconciles DM invariants (belt-and-braces). -- **DL-new-6 (DM shape):** A peer-DM is born `kind=DM`, exactly two agent +- **DL-288 (DM shape):** A peer-DM is born `kind=DM`, exactly two agent parties, `post_policy=OPEN` (ownerless), `mandatory_subscription=true`, members = both agents + pulled-in owner(s); every member a delivery target from birth. A member ADD converts it — same tx — to a named `kind=CHANNEL` @@ -890,7 +890,7 @@ caller applies these rows at freeze: freeing the DM name for a fresh pair-DM). `CHANNEL_KIND_GROUP_DM` is retired reserve-not-delete: the enum number stays, deprecated, never produced. The only kinds are CHANNEL and DM. -- **DL-new-7 (scope):** Peer-DM scope is same-owner for MVP (the F2 +- **DL-289 (scope):** Peer-DM scope is same-owner for MVP (the F2 wave-shares-one-owner frame); a cross-owner handle is the merged in-band `not_found`. Cross-owner DMs are deferred. @@ -903,7 +903,7 @@ red-team (OQ-1, 2, 4, 6, 7, 8, 9) is now a Matt-ruled decision recorded in ### OQ-3 (non-load-bearing, deferred) — Cross-owner DMs Different-owner agents cannot DM under this record (same-owner gate, -DL-new-7). `expandOwnerMembership` would already pull both owners in, so the +DL-289). `expandOwnerMembership` would already pull both owners in, so the mechanism generalizes — the missing piece is an authz policy for cross-owner contact (the bilateral owner-peering edge the handle-cutover record files as RIG-2796-class scope). Deferred; the record is correct without it