From 80f2103fa04b5aa8dc82aebf6b14a16717d80048 Mon Sep 17 00:00:00 2001 From: mintaka Date: Sun, 13 Sep 2026 16:06:36 -0400 Subject: [PATCH 1/2] docs(ledger): correct the DL-241 forge toolset count to twelve (RIG-3774) DL-241 recorded ten tools and enumerated ten; createForgeTools ships twelve. The transition pair (forge_transition_issue_state, forge_transition_pull_request_state) arrived with DL-342 / RIG-3331 and the row was never refreshed. The row's own closing line makes the count load-bearing for future toolset-refresh rows, so a refresh diffing against 'ten' would start from a wrong base. Amended in place per Matt. Also retires the row's ratified subscribe caveat: both arms persist now (EnsureAgentForgeSubscription / DeleteAgentForgeSubscription), so they are no longer CodeUnimplemented stubs. The stale caveat was also shipping to agents at runtime. SUBSCRIBE_RULE told every model the subscribe calls 'return unimplemented' and 'should not be relied on yet', steering agents off a working feature; it now describes what a subscription does. Record tasks T1/T2 are checked, its counts and the ForgeCallRequest arm range (2-11 plus 14-15) corrected. Co-authored-by: Matt Wilkinson --- docs/designs/DECISIONS.md | 2 +- .../agent/compass-agent-forge-tools/design.md | 47 +++++++++++-------- packages/compass-agent/src/forge.ts | 4 +- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/docs/designs/DECISIONS.md b/docs/designs/DECISIONS.md index 75dfd6ca4..3a38eb287 100644 --- a/docs/designs/DECISIONS.md +++ b/docs/designs/DECISIONS.md @@ -220,7 +220,7 @@ check enforces the mechanical half. Full rationale: | DL-210 | Forge integration testing adds two live-contract tiers above the DL-174 hermetic pyramid: (1) a hermetic golden-fixture replay leg (committed `go/internal/forge/testdata/` fixtures replayed through the stub RoundTripper, an untagged Go test riding moon affected-detection on every forge-affected PR + the main/nightly sweep, no `ci.yml` project enumeration) and (2) a `//go:build livegithub` live-credentials oracle (same scenarios against a throwaway `RigelBuild/compass-forge-testbed` + a Linear test team under test-only bot PATs ≠ the `server_only` production secrets), REQUIRED on forge-affected PRs + nightly, covering GitHub and Linear co-equally (the GitHub-only F1 reviewer≠author 422→APPROVE headline; Linear's PR/review family is `ErrUnsupported`, not a scenario). This is a NEW secret-bearing per-PR CI step — a standalone decision, NOT an amendment: D2's full-stack deterministic tier (`compass-dogfood-e2e/design.md:815-830`) stays secret-free and unchanged; a forward pointer next to D2 records the carve-out. Fork-PR secret safety: the same-repo-head-guarded step goes vacuously green on fork heads (GitHub withholds secrets there); accepted, with the push-to-main run + a maintainer same-repo re-push convention as the catch (0 forks today) | Active (Matt, 2026-08-18) | [forge integration testing §Approach](server/compass-forge-integration-testing/design.md#approach) | | 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](server/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](server/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](agent/compass-agent-forge-tools/design.md#tool-set-and-shape--ten-native-tools-one-per-arm) | +| DL-241 | The agent forge native toolset is twelve 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_transition_issue_state`, `forge_transition_pull_request_state`, `forge_subscribe`, `forge_unsubscribe`), each a native `AgentTool` over a thin `ForgeBroker` on the `RunnerTransport.forge()` seam. 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. Amended 2026-09-13: the count was ten at ratification; the transition pair arrived with DL-342 (RIG-3331) and the original row was not refreshed. The ratified caveat that `forge_subscribe`/`forge_unsubscribe` return in-band `unimplemented` until the DL-163 writer lands is also retired — both arms persist (`EnsureAgentForgeSubscription` / `DeleteAgentForgeSubscription`) and `forge_subscribe` returns a real subscription id | Active (Matt, 2026-08-24; amended Matt, 2026-09-13) | [forge tools §Tool set and shape](agent/compass-agent-forge-tools/design.md#tool-set-and-shape--twelve-native-tools-one-per-arm) | | DL-291 | 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](agent/compass-agent-peer-dm/design.md#ledger-delta) | | DL-292 | 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](agent/compass-agent-peer-dm/design.md#ledger-delta) | | DL-293 | 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](agent/compass-agent-peer-dm/design.md#ledger-delta) | diff --git a/docs/designs/agent/compass-agent-forge-tools/design.md b/docs/designs/agent/compass-agent-forge-tools/design.md index c88113ed3..fa1256ce3 100644 --- a/docs/designs/agent/compass-agent-forge-tools/design.md +++ b/docs/designs/agent/compass-agent-forge-tools/design.md @@ -4,7 +4,7 @@ Status: Active Design for the TypeScript agent-side leg of the forge surface: a `forge()` method on the frozen `RunnerTransport` seam plus a native tool set (`forge.ts`) -that exposes all ten `ForgeCallRequest` arms to the containerized first-party +that exposes all twelve `ForgeCallRequest` arms to the containerized first-party agent, one tool per arm (the two subscription arms ship now but return the server's in-band `unimplemented` until the `agent_forge_subscriptions` store writer lands), with a @@ -49,7 +49,7 @@ But the agent cannot reach any of it: `RunnerTransport` (`packages/compass-agent/src/transport/index.ts`) exposes `comms()` and `lifecycle()` and no `forge()`, and no `forge.ts` exists (comms ships five tools, lifecycle two, forge zero). This record designs the missing TS leg — -the transport method, the `ForgeBroker`, the ten native tools (the two +the transport method, the `ForgeBroker`, the twelve native tools (the two subscription tools built now over the stub arms) with their multi-forge selector and per-tool prompt guidance, and the `cli.ts` wiring — and nothing else. @@ -58,7 +58,7 @@ else. ### Decisions (pre-resolved by Matt — not reopened below) -1. **Full surface: all ten `ForgeCallRequest` arms become tools.** Every arm +1. **Full surface: all twelve `ForgeCallRequest` arms become tools.** Every arm the wire carries ships as an agent tool in this record — the eight arms `ExecuteForgeCallAsAccount` dispatches for real PLUS `subscribe`/ `unsubscribe`. No partial MVP subset, no hidden arms. @@ -67,7 +67,9 @@ else. precedent. Names: `forge_get_issue`, `forge_get_pull_request`, `forge_comment_on_issue`, `forge_comment_on_pull_request`, `forge_submit_review`, `forge_create_issue`, `forge_create_pull_request`, - `forge_list_issues`, `forge_subscribe`, `forge_unsubscribe`. + `forge_list_issues`, `forge_transition_issue_state`, + `forge_transition_pull_request_state`, `forge_subscribe`, + `forge_unsubscribe`. 3. **`forge_subscribe`/`forge_unsubscribe` ship NOW, returning the server's in-band `unimplemented` until the writer lands.** The backend arms are `CodeUnimplemented` stubs (`go/server/forge.go`) pending the @@ -79,6 +81,12 @@ else. `unimplemented`, with the per-tool guidance saying "not yet wired") until the writer lands, at which point they light up with no contract rework and no re-registration. + **Landed since (2026-09-13): the writer is in, so this caveat is spent.** + `subscribeForge` calls `store.EnsureAgentForgeSubscription` and returns a + real subscription id; `unsubscribeForge` calls + `store.DeleteAgentForgeSubscription` (both in `go/server/forge.go`). The + two arms are no longer `CodeUnimplemented`, and the per-tool "not yet + wired" guidance no longer describes them. 4. **V1 is multi-forge: the `ForgeRef` selector is exposed on every tool.** The wave needs GitHub AND Linear (it files Linear issues and opens GitHub PRs), and the substrate resolves a per-provider coordinate end to end @@ -118,7 +126,7 @@ else. - **The proto carrier and its generated TS types exist.** `proto/compass/v1/agent_gateway.proto` `rpc Forge(ForgeCallRequest) returns (ForgeCallResult)` on `AgentGateway`; `ForgeCallRequest` carries `call_id`, - the ten-arm `call` oneof (arms 2–11), an optional `ForgeRef forge` (unset = + the twelve-arm `call` oneof (arms 2–11 plus 14–15), an optional `ForgeRef forge` (unset = configured default GitHub forge, DL-202), and `client_request_id` (create arms only, DL-206). `ForgeCallResult` retypes the domain arms to canonical `compass.v1.Issue`/`PullRequest` (DL-069/DL-092) plus `CommentRef`/`ReviewRef` @@ -162,9 +170,9 @@ else. customTools→state.tools→`#withNatives` path. Forge adds one broker and one spread to that list. -### Tool set and shape — ten native tools, one per arm +### Tool set and shape — twelve native tools, one per arm -All ten tools live in one new `packages/compass-agent/src/forge.ts`, +All twelve tools live in one new `packages/compass-agent/src/forge.ts`, authored as OMP `AgentTool`s with arktype parameters, closing over a `ForgeBroker`. Approval levels follow the comms precedent (`comms.ts`: mutations `approval: "write"`, reads `approval: "read"`; the container runs @@ -509,7 +517,7 @@ returns its result; `idempotencyKey` is nonce-prefixed, stable per broker, distinct across brokers. `direnv exec . moon run compass-agent:test` red first, then green; biome clean. -### T2 — Agent: `createForgeTools` (ten tools) + `cli.ts` wiring + prompt guidance +### T2 — Agent: `createForgeTools` (twelve tools) + `cli.ts` wiring + prompt guidance In `packages/compass-agent/src/forge.ts` (same file, tool half): @@ -546,7 +554,7 @@ const forgeSelector = type({ }); // EVERY tool schema below also spreads `...forgeSelector` (elided in each body // for brevity — the eight non-subscription sketches show only their arm-specific -// fields). An implementer adds `...forgeSelector,` to all ten object literals. +// fields). An implementer adds `...forgeSelector,` to all twelve object literals. export const getIssueParameters = type({ repo: /* non-blank */ "Repository as / (GitHub) or team key (Linear)", issue_number: type("1 <= number.integer"), @@ -623,7 +631,7 @@ implementer must copy that bound, not the literal `/* non-blank */` comment. Every schema also spreads `...forgeSelector` (shown once above, elided in each body for brevity): the optional `forge_provider`/`forge_host` pair is on all -ten tools. When either is set, `execute` builds +twelve tools. When either is set, `execute` builds `create(ForgeRefSchema, { provider: , host: forge_host ?? "" })` and sets `ForgeCallRequest.forge`; when both are unset it leaves `forge` nil (the default-GitHub path). The string enum maps to the generated @@ -688,7 +696,7 @@ const nativeTools = [ is unchanged. **Interfaces:** `createForgeTools(broker: ForgeBroker): AgentTool[]`; the -ten exported parameter schemas above (the shared `forgeSelector` spread +twelve exported parameter schemas above (the shared `forgeSelector` spread into each); `function forgeFailure(result: ForgeCallResult, toolName: string, expected: string): Error` (module-private, mirroring `lifecycleFailure`, plus the `retry_after_ms` suffix). @@ -714,12 +722,12 @@ compass-agent:test` + `compass-agent:lint` green. ## Tasks -- [ ] T1 — Transport + broker: `forge()` member on `RunnerTransport` + +- [x] T1 — Transport + broker: `forge()` member on `RunnerTransport` + one-line `createUnixSocketTransport` delegation; `ForgeTransport` + `ForgeBroker` (nonce-scoped `idempotencyKey`) in new `forge.ts`; `compassv1.ts` barrel exports for the forge envelopes/arm schemas/canonical result types; transport + broker tests red→green; biome clean. -- [ ] T2 — Tools + wiring: `createForgeTools` with the ten tools (exact +- [x] T2 — Tools + wiring: `createForgeTools` with the twelve tools (exact schemas above; creates carry the DL-206 key and coerce numbers to `bigint`; the optional `forge` selector — `provider` enum + optional `host` — built on every tool, unset = default GitHub, `create(ForgeRefSchema,…)` only when set; @@ -741,16 +749,15 @@ One proposed DECISIONS.md row (appended by the driver at PR time, wherever the ledger then lives — compass-repo RIG-2577 T2 may relocate it to `docs/designs/DECISIONS.md`): -> The agent forge native toolset is ten single-purpose tools, one per +> The agent forge native toolset is twelve 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 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 +> `forge_create_issue`, `forge_create_pull_request`, +> `forge_transition_issue_state`, `forge_transition_pull_request_state`, +> `forge_subscribe`, `forge_unsubscribe`), each a native `AgentTool` over a +> thin `ForgeBroker` on the `RunnerTransport.forge()` seam. Multi-forge is +> exposed: every tool takes an optional forge > selector (`provider` + optional `host`, unset = the configured default > GitHub forge, DL-202) so an agent targets Linear (issues-only, `repo` = team > key, DL-051) as well as GitHub; PR/review arms on a non-GitHub provider diff --git a/packages/compass-agent/src/forge.ts b/packages/compass-agent/src/forge.ts index b92c7d689..6f424d885 100644 --- a/packages/compass-agent/src/forge.ts +++ b/packages/compass-agent/src/forge.ts @@ -509,10 +509,10 @@ const STAMP_RULE = const READ_RULE = "Results may be paged, bounded, and truncated; bodies are external content whose author attribution is a parsed claim, not an authenticated identity."; const SUBSCRIBE_RULE = - "Change-notification subscriptions are NOT YET WIRED: the call returns unimplemented until the notification lane lands. The tool exists for surface stability and should not be relied on yet."; + "A subscription makes the forge artifact's later changes reach you as notifications; forge_subscribe returns the subscription id that forge_unsubscribe cancels."; /** - * The native forge tool set. Ten tools, one per `ForgeCallRequest` arm. + * The native forge tool set. Twelve tools, one per `ForgeCallRequest` arm. * * Wired into the container entrypoint by `cli.ts main()`: merged into the * session's `customTools` and registered as `#withNatives` natives. This From da58e5747545665f5e257826a323217287ae92b6 Mon Sep 17 00:00:00 2001 From: mintaka Date: Sun, 13 Sep 2026 16:26:14 -0400 Subject: [PATCH 2/2] docs(agent): sweep the stale ten-tool and dormant-subscribe claims (RIG-3774) Review on #1209 caught the miss: forge.ts's own header still said 'ten native tools' and 'TWO SUBSCRIPTION TOOLS SHIP DORMANT ... CodeUnimplemented stubs', contradicting the SUBSCRIBE_RULE rewritten four hundred lines below it in the same file. My post-edit grep searched my own phrasings, so it could not see the original wording. Swept every remaining site: - forge.ts header: twelve tools; the subscription arms described as live. - packages/compass-agent/AGENTS.md: the package contract forge.ts names as authoritative. Ten -> twelve, both transition tools added under writes (approval: "write", verified in forge.ts), and the 'unimplemented until the writer lands' sentence retired. - compassv1.ts barrel comment: ten -> twelve forge arms. - The prescriptive createForgeTools docstring in the design record's T2 block. Left deliberately: one 'ten precise JSON Schemas' in a rejected alternative (a historical argument about a multiplexed tool, not a claim about what shipped), and the gitignored eng-docs ledger copy, which regenerates. Co-authored-by: Matt Wilkinson --- .../agent/compass-agent-forge-tools/design.md | 15 ++++++--------- packages/compass-agent/AGENTS.md | 16 ++++++++++------ packages/compass-agent/src/compassv1.ts | 2 +- packages/compass-agent/src/forge.ts | 13 +++++++------ 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/designs/agent/compass-agent-forge-tools/design.md b/docs/designs/agent/compass-agent-forge-tools/design.md index fa1256ce3..737e3099e 100644 --- a/docs/designs/agent/compass-agent-forge-tools/design.md +++ b/docs/designs/agent/compass-agent-forge-tools/design.md @@ -523,11 +523,8 @@ In `packages/compass-agent/src/forge.ts` (same file, tool half): ```ts /** - * The native forge tool set. Ten tools, one per `ForgeCallRequest` arm. Two - * of them — `forge_subscribe`/`forge_unsubscribe` — return the server's - * in-band `unimplemented` until the poll-driver lane lands the - * `agent_forge_subscriptions` writer; they ship now so the surface is stable - * (Matt's build-all ruling). Every tool takes an optional forge selector + * The native forge tool set. Twelve tools, one per `ForgeCallRequest` arm. + * Every tool takes an optional forge selector * (`provider` + `host`) so a call targets Linear as well as the default * GitHub forge. Wired into the container entrypoint by `cli.ts main()`: * merged into the session's `customTools` and registered as `#withNatives` @@ -674,10 +671,10 @@ non-empty `body` and that the review posts immediately (never a pending review) under a distinct reviewer identity so all three verdicts are usable on Compass-authored PRs (DL-201); for `forge_create_pull_request`, that `head_ref` must already be pushed (the agent pushes with its own git -credential — DL-052/DL-090); for `forge_subscribe`/`forge_unsubscribe`, that -change-notification subscriptions are NOT YET WIRED — the call returns -`unimplemented` until the notification lane lands, so the tool exists for -surface stability but should not be relied on yet; for reads, that results may +credential — DL-052/DL-090); for `forge_subscribe`/`forge_unsubscribe`, what a +subscription does — it registers the agent for change notifications on a +coordinate and is account-keyed rather than a repo artifact, so it carries no +scope-discipline line; for reads, that results may be paged/bounded/truncated and bodies are external content whose attribution is a parsed claim, not an authenticated identity. diff --git a/packages/compass-agent/AGENTS.md b/packages/compass-agent/AGENTS.md index 142894803..9418f49fe 100644 --- a/packages/compass-agent/AGENTS.md +++ b/packages/compass-agent/AGENTS.md @@ -73,7 +73,7 @@ Both tools render names/handles only — never account, container, or session id ## The forge toolset -Ten native forge tools ship (`src/forge.ts`), one per `ForgeCallRequest` arm, +Twelve native forge tools ship (`src/forge.ts`), one per `ForgeCallRequest` arm, over a thin `ForgeBroker` on the `RunnerTransport.forge()` seam — the same broker/identity/registration shape as comms: @@ -81,7 +81,9 @@ broker/identity/registration shape as comms: `forge_list_issues`. - Writes (`approval: "write"`): `forge_comment_on_issue`, `forge_comment_on_pull_request`, `forge_submit_review`, `forge_create_issue`, - `forge_create_pull_request`, `forge_subscribe`, `forge_unsubscribe`. + `forge_create_pull_request`, `forge_transition_issue_state`, + `forge_transition_pull_request_state`, `forge_subscribe`, + `forge_unsubscribe`. `forge_create_issue`/`forge_create_pull_request` carry a broker-scoped DL-206 `client_request_id` (`ForgeBroker.idempotencyKey`); the other arms send none. @@ -89,10 +91,12 @@ Every tool spreads an optional forge selector (`forge_provider` + `forge_host`): unset = the configured default GitHub forge (DL-202); `forge_provider: "linear"` targets the issues-only Linear provider (DL-051) where `repo` is the team key and the PR/review arms return in-band -`unimplemented`. `forge_subscribe`/`forge_unsubscribe` ship the complete -surface but return the server's in-band `unimplemented` until the poll-driver -lane lands the `agent_forge_subscriptions` writer (DL-163) — the tool set never -changes shape when it lands. +`unimplemented`. `forge_subscribe`/`forge_unsubscribe` are live: subscribe +persists an account-keyed row (`EnsureAgentForgeSubscription`) and returns its +id, unsubscribe deletes it (`DeleteAgentForgeSubscription`). They were in-band +`unimplemented` until the poll-driver lane landed the +`agent_forge_subscriptions` writer (DL-163), and the tool set did not change +shape when it did. **The forge surface is prompt-contained, not authz-contained.** Unlike comms (channel membership), the substrate ships no scope rejection (A8): one forge diff --git a/packages/compass-agent/src/compassv1.ts b/packages/compass-agent/src/compassv1.ts index 905667804..007a5a399 100644 --- a/packages/compass-agent/src/compassv1.ts +++ b/packages/compass-agent/src/compassv1.ts @@ -22,7 +22,7 @@ export { type BoardCallResult, BoardCallResultSchema, // The agent-initiated forge call envelopes (internal-only AgentGateway gen). Request - // carries `call_id`, a oneof over the ten forge arms, an optional `ForgeRef`, and a + // carries `call_id`, a oneof over the twelve forge arms, an optional `ForgeRef`, and a // `client_request_id` (create arms); Result retypes to canonical Issue/PullRequest/etc // plus an in-band `error` arm carrying `retry_after_ms`. Reused as RelayForgeCall payloads. CommentOnIssueRequestSchema, diff --git a/packages/compass-agent/src/forge.ts b/packages/compass-agent/src/forge.ts index 6f424d885..c5a834605 100644 --- a/packages/compass-agent/src/forge.ts +++ b/packages/compass-agent/src/forge.ts @@ -1,6 +1,6 @@ -// The agent's forge surface: a thin broker over the Runner transport, plus the ten native -// tools to read and write forge artifacts — issues, PRs, comments, reviews, and -// change-notification subscriptions (design compass-agent-forge-tools T1 + T2). +// The agent's forge surface: a thin broker over the Runner transport, plus the twelve native +// tools to read and write forge artifacts — issues, PRs, comments, reviews, state +// transitions, and change-notification subscriptions (design compass-agent-forge-tools T1 + T2). // Mirrors comms.ts / lifecycle.ts: `AgentGateway.Forge` is a Connect unary over the // per-container Unix socket, so a result is the awaited return value — no pending map, no @@ -19,9 +19,10 @@ // prompt-level: every artifact-write tool's description carries the scope-discipline line, and // the DL-050 attribution trail is the only audit. -// TWO SUBSCRIPTION TOOLS SHIP DORMANT: `forge_subscribe`/`forge_unsubscribe` are built now -// though the server arms are `CodeUnimplemented` stubs until the poll-driver lands — the tools -// render the in-band `unimplemented` cleanly, so the surface never changes shape when it lands. +// THE SUBSCRIPTION ARMS ARE LIVE: `forge_subscribe` persists an account-keyed row +// (`EnsureAgentForgeSubscription`) and returns its id; `forge_unsubscribe` deletes it. Both +// were `CodeUnimplemented` until the poll-driver landed the writer — the surface did not +// change shape when it did, as designed. // The schema builder rides the SDK's own schema stack via its `/ark` compat facade — one // there is no two-copy mismatch to catch.