WIN · LAUNCH WORKBOOK pre-UAT → production · mined from Codex session 019f25cc · snapshot 2026-07-06 · origin/main 734eff41

Lane 3 · Web + Slack + MCP/CLI

Channel spine — one engine, three doors, tested at the joints

Good news first: no channel bypasses the spine. Web, Slack, MCP, and CLI all terminate in the same buildLaunchAndSpawn → kernel.spawn → job-store path. The unification work left is narrower than feared: one real MCP correctness bug, a few parity gaps (cancellation), status-enum drift in three packages, and a governance spec that everyone cites but nobody ever committed.

1
shared terminus (buildLaunchAndSpawn)
0
tests in packages/mcp-server
3
packages hand-duplicating the status enum
104?
spec cited everywhere, exists nowhere

Entry-point map (verified)

ChannelEntryConverges viaIdentity mode
WebPOST /api/me/jobs (me.ts:65me-jobs.ts:683)ingestTurnbuildLaunchAndSpawncaller-scoped
Web (draft test)POST /api/workflows/:slug/test-run (workflows.ts:431)direct buildLaunchAndSpawn (workflows.ts:474)admin
Slackslack-events.ts:289slack-handlers.ts:27ingestTurn (slack-handlers.ts:124)caller-scoped
MCPwin_run tool (mcp-server/src/tools/run.ts:14) → POST /api/rundirect buildLaunchAndSpawnadmin
CLIwin run (run/index.ts:330; endpoint picked in run-plan.ts:129)/api/me/jobs or /api/runeither

The two “doors” (ingestTurn-wrapped caller-scoped vs direct admin) are documented as intentional in launch-builder.ts:53–64. Spine: job-store.ts (all status literals from @win/contracts) → spawn-queue.ts (Redis/BullMQ) → packages/kernel executeSpawn. Note: workflow-session-store is a pre-launch canvas concept, and packages/daemon is the suggestion router — neither is the dispatcher.

Spec 104 — the governance ghost

Drafted, never committed — the full text is on your disk docs/specs/104-surface-unification-windx.md exists on no branch of origin/main and has no docs/specs/README.md row — but this audit found the complete 285-line draft ADR untracked in the dirty coordinator checkout (/workspace/win), alongside its ratification package (docs/plans/pre-uat+prod-prep-3/slice-4a-ratification-package-20260703.md). Its scope is deliberately narrow: unify CLI+MCP over one shared operation registry (packages/win-core, thin adapters) and split privileged operator tooling into a separate windx binary — explicitly not the channel-spine work, which stays server-owned per specs 081/082. “RATIFY Spec 104” blocks decision D10 / gate PROD-4, and newer launch docs disagree on the number (104 vs 105). Salvage the draft as a PR (re-resolving the number via bun run specs:numbers and indexing it in the same change), then ratify or accept-gap.

Junction test-coverage matrix

JunctionWebSlackMCPCLI
Job creationTESTEDTESTEDNONETESTED
Status transitionsTESTEDPARTIAL outbound renderer untestedNONEPARTIAL terminal only
Delivery / artifactPARTIAL shape onlyPARTIAL upload mockedNONE no artifact tool existsNONE
CancellationTESTED strongest cellNO HANDLER source gapNONENONE
Correctness bug, not just coverage mcp-server/src/tools/run.ts:53–88 has no @win/contracts dependency and no case for cancelled / cancelling / error / paused / queued / pending / recovering — it falls through and reports “Job completed” for a cancelled or errored job. Any agent driving WIN over MCP gets lied to today.

On the “once 4 channels” memory: the canonical type is ChannelName = "slack" | "web" | "mcp" (channel.ts:44) — CLI shares the MCP surface. No fourth channel was ever built; spec 047’s registerChannel() SPI (Discord/email/iMessage) was deliberately deferred at the 2026-06-01 steer gate.

Plan of attack

Implementation worksheet0/8
Test deliverable: cancelled and error jobs are never reported “completed.” Set up the package’s test script while you’re in there.
Same item as SLK-6 — one PR closes both. Test: cancel intent → publishCancel invoked → status cancelling.
HTTP-call + error-path coverage; table-driven.
Guard: assert Object.values(JOB_STATUS).length matches each consumer, or a type-level exhaustiveness check per package.
Unit test enumerating every JOB_STATUS value through statusDot, asserting no default-branch hit.
Integration test: recovery-state update via either caller produces identical DB state; then delete the duplicated SQL.
Don’t rewrite — commit. Resolve the 104-vs-105 numbering dispute via bun run specs:numbers in the same PR. This clears launch decision D10 / PROD-4.
Closes the last all-NONE column in the junction matrix. Consider reusing the better-tested server-side OAuth MCP surface (routes/mcp.ts) as the reference.

Agent handoff prompt

prompt · SPN-1 + SPN-4 contract hardening
Harden WIN's cross-channel job-status contract. Two defects:

(1) packages/mcp-server/src/tools/run.ts:53-88 does not depend on @win/contracts and has no
case for cancelled/cancelling/error/paused/queued/pending/recovering — it reports "Job
completed" for cancelled and errored jobs. Import JOB_STATUS/isTerminalStatus from
@win/contracts, handle every status exhaustively (compile-time never check), and create the
package's first test setup with cases asserting cancelled and error are never reported as
completed.

(2) Three packages hand-duplicate the status enum and have already drifted:
packages/server/src/schemas/job.ts:12-25 (missing "queued"), packages/ui/src/lib/types.ts:32-45,
packages/cli/src/commands/jobs/shared.ts:8-24. Replace each with direct @win/contracts imports
and add a drift-guard (type-level exhaustiveness or a test asserting parity with
Object.values(JOB_STATUS)).

Rails: bun workspace; run full cross-package typecheck (bun run typecheck), affected package
tests; PR per repo protocol with docs/github/prs/PR-<n>.md record; note hosted validation as
deferred in the untested list.