feat(pipeline-crew-mcp): make the message-kind contract discoverable + fix the issue-number footgun (#3622)#3702
Conversation
|
review-code: PASS @ 75623d6 Fresh-eyes gate on PR #3702 against issue #3622. Non-§CP (diff lives entirely in Acceptance criteria
Scrutiny notes (wire-contract change)
Verdict bound to head |
…+ fix the issue-number footgun (#3622) The typed kind→schema map was enforced at send time (#3229) but not discoverable ahead of a send, so a sender had to already know a kind's exact shape or trigger a reject to learn it — the pre-typed-relay fragility ADR 0187 meant to remove. A live IntakePing send with the natural `{"issue": 3621}` was rejected because `issue` was modelled as a bare string. - Surface the contract: `channel_kinds` MCP tool returns each kind's payload shape (JSON Schema, derived from the existing `crewMessageKinds`/`payloadSchemaForKind` map — no divergent copy) + whether it awaits a reply, plus each role's sanctioned seams (sourced from `crewCatalog`/`seamsFor`, so a future non-flat topology stays in sync). Injected via a `ChannelDescribe` port like `ChannelSend`/`ChannelClaim`. - Startup invariant: a session resolves the whole shared kind set on its boot critical path and fails loud if any kind is unresolvable, rather than discovering the gap at first send. - Footgun fix: `IntakePing.issue` (and `EngineNudge`'s pr/issue target) now carry a branded positive-integer `IssueNumber`/`PrNumber`, so `{"issue": 3621}` is the accepted shape. The other numeric kind fields were already `Schema.Int`. - Unit coverage for the discoverable surface, the role↔kind contract, and the startup invariant's loud fail. Fixes #3622 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ypecheck exits 0 (#3622) `Effect.try`'s catch returned `cause` unchanged, leaving the error channel `unknown` and tripping the @effect/language-service diagnostic `effect(unknownInEffectCatch)` (TS377021). That diagnostic is warning-severity, so `tsgo` exited 2 — and turbo then segfaulted tearing down the failed task, surfacing as the deterministic exit 139 on the `lint / format / typecheck` job. The 139 was a downstream symptom, not a compiler crash. Return `String(cause)` instead, matching the in-package idiom in `standup/version-assert.ts`. The error is discarded by the following `Effect.orElseSucceed(() => undefined)`, so `describeKind`'s public signature and behavior are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75623d6 to
3f6fac1
Compare
Repair round 1 — CI exit 139 root causeThe 139 was not a compiler segfault in this diff. It was a downstream symptom. The CI log sequence:
Why it did not reproduce with a plain local
|
|
review-code: PASS @ 3f6fac1 — merge-ready Re-review after the CI-fix force-push. The prior Class routing: §CP: NON-control-plane, verified against What changed since the prior PASS — scoped, not assumedThe raw Every other file differing between the two heads is main's advance, not PR work. The previously-verified surface is byte-identical. The four verification points
Acceptance criteria (issue #3622)
Evidence run at this headVerified in an isolated throwaway worktree materialized from a per-run ref at
CI at this head — independently confirmed
Three non-green checks, all infrastructure, not code, and none in the blocking Run-evidence bundle: absent (its producing job failed on the infra step above). Per the graceful-degrade rule this is not a gate failure — and the degrade did not narrow the surface: I ran the full package suite, not a feature-scoped subset. The in-worktree typecheck + full suite are the authoritative signal here, so this is a full-trust PASS, not a fenced one. Unresolved inline review threads: none. The inline-comments endpoint 503'd persistently, so I resolved it via the reviews endpoint, which read cleanly at Reviewed-head: @ 3f6fac1 |
What
Surfaces the crew-mcp typed message-kind contract as a discoverable, resolvable surface every peer can read before sending — closing the gap that let a live
IntakePingsend fail on a shape ({"issue": 3621}rejected) that nothing exposed ahead of time. Also fixes the underlying footgun (issue/PR numbers modelled as bare strings) and makes schema availability a boot invariant. Enforcement is unchanged — no schema was relaxed.Fixes #3622
How
channel_kindstool).packages/pipeline-crew-mcp/src/protocol/describe.tsrenders each kind to{kind, awaitsReply, payload}wherepayloadis the payload's JSON Schema, derived straight from the existingcrewMessageKinds/payloadSchemaForKindmap (no second, divergent copy of the kind set).packages/pipeline-crew-mcp/src/edge/kinds-tool.tsexposes it as achannel_kindsMCP tool via an injectedChannelDescribeport (same DI shape asChannelSend/ChannelClaim), registered on the session inpackages/pipeline-crew-mcp/src/crew/session.ts.packages/pipeline-crew-mcp/src/crew/contract.tsjoins the kind map with each role's sanctioned seams, sourced fromcrewCatalog/seamsFor— so a future non-flat topology stays in sync automatically.resolveChannelContractruns on the session's boot critical path (assembleCrewSession), so a peer that cannot resolve the full shared kind set fails loud at boot withChannelContractErrorrather than discovering a gap at first send.IntakePing.issue(andEngineNudge's pr/issue target) now carry a branded positive-integerIssueNumber/PrNumber, so{"issue": 3621}is the accepted shape. The other numeric kind fields (DrainProgressTally,Heartbeat) were alreadySchema.Int— reviewed, no change needed.protocol/describe.test.ts), the role↔kind contract (crew/contract.test.ts), thechannel_kindstool (edge/kinds-tool.test.ts), and the startup invariant's loud fail. Existing send/round-trip tests updated to the numeric shape.Acceptance criteria
crewCatalog/seamsForIntakePing.issuefootgun resolved (number/brandedIssueNumber); other kinds reconsidered for the same footgun (EngineNudgefixed; the rest alreadyInt)Verification
pnpm typecheck(33/33),pnpm lint:worktree(clean),vitest runfor@kampus/pipeline-crew-mcp(277 passed, incl. 11 new).