Lane 3 · Web + Slack + MCP/CLI
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.
| Channel | Entry | Converges via | Identity mode |
|---|---|---|---|
| Web | POST /api/me/jobs (me.ts:65 → me-jobs.ts:683) | ingestTurn → buildLaunchAndSpawn | caller-scoped |
| Web (draft test) | POST /api/workflows/:slug/test-run (workflows.ts:431) | direct buildLaunchAndSpawn (workflows.ts:474) | admin |
| Slack | slack-events.ts:289 → slack-handlers.ts:27 | ingestTurn (slack-handlers.ts:124) | caller-scoped |
| MCP | win_run tool (mcp-server/src/tools/run.ts:14) → POST /api/run | direct buildLaunchAndSpawn | admin |
| CLI | win run (run/index.ts:330; endpoint picked in run-plan.ts:129) | /api/me/jobs or /api/run | either |
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.
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 | Web | Slack | MCP | CLI |
|---|---|---|---|---|
| Job creation | TESTED | TESTED | NONE | TESTED |
| Status transitions | TESTED | PARTIAL outbound renderer untested | NONE | PARTIAL terminal only |
| Delivery / artifact | PARTIAL shape only | PARTIAL upload mocked | NONE no artifact tool exists | NONE |
| Cancellation | TESTED strongest cell | NO HANDLER source gap | NONE | NONE |
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.
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.