P0.5.1 — δ Intent Scoring — Audit

Step 1 of the 5-step executor chain. Inventory of the surface that P0.5.1 must land into, the two-spec conflict this task resolves, and the seams already in the codebase.

Spec conflict and resolution

Two canonical docs disagree on the Phase 0 scope of δ Intent Scoring:

Source Claim
docs/architecture/decisions/ADR-005-multi-model-defer.md §Decision “Router interface is present. δ tools exist in the Phase 0 tool surface as thin stubs that always resolve to Claude. Scoring function returns a constant.”
docs/guides/implementation/task-breakdown.md §P0.5 header + §P0.5.1 “spec-only — deferred per ADR-005 … design only; no src/domains/router/ in Phase 0.”

ADR-005 wins. ADR is the authoritative source; the task-breakdown.md sentence is an obsolete Wave-G-era interpretation of ADR-005 that conflicts with ADR-005 itself. Evidence:

  1. ADR-005 §Decision explicitly calls for “thin stubs” in Phase 0.
  2. ADR-005 §Implementation §”Phase 0 router stub (P0.5)” names P0.5.1 — router_score stub that returns constant vector as one of two in-scope Phase 0 tasks.
  3. ADR-005 §Verification §”Phase 0 completion round (R80)” verifies that “no multi-model code exists in src/domains/router/” — which implies that a src/domains/router/ directory does exist at R80 with single-model code.
  4. task-breakdown.md’s own line 252 lists the “Output (deferred)” as src/domains/router/scoring.ts — the same path this task targets.

The doc-fix to reconcile task-breakdown.md against ADR-005 is out-of-scope for this PR; it belongs to the Wave I close commit. This task ships the code that ADR-005 §Decision mandates.

Task scope

This task ships the Phase 0 library-only stub for δ intent scoring:

  • A pure library module in src/domains/router/ (new directory — first δ lander)
  • scoreIntent(prompt, context) returns a constant { scores: { claude: 1.0 }, winner: 'claude' } for every input
  • Determinism trivially holds (output is frozen, constant)
  • Library-only: NO new MCP tools (follows ν pattern — ADR-004 post-Wave-H has 14 tools; this task adds zero)
  • The router_score MCP tool is Phase 1.5 scope per the ADR-005 transition table

Phase 0 acceptance (this task)

From the ADR-005 invariants:

  1. scoreIntent(prompt, context) returns { scores: { claude: 1.0 }, winner: 'claude' } for ALL inputs.
  2. Pure function — no I/O, no randomness, no time-dependent state.
  3. Deterministic — same input → same output (trivially true because the output is constant and frozen).
  4. Public shape preserved for Phase 1.5 drop-in replacement.
  5. Zero new MCP tools registered (tool surface stays at 14).
  6. Zero new env vars in src/config.ts (stub doesn’t need them; Phase 1.5 scope).
  7. No circular imports; no dependencies beyond types.

What this task does NOT do (Phase 1.5 scope)

  • Real intent scoring across multiple models — the “prompt length / complexity keywords / context size / tool requirements” logic in task-breakdown.md §P0.5.1 is Phase 1.5 work.
  • router_score MCP tool registration — Phase 1.5.
  • Model slots beyond Claude — Phase 1.5.
  • Adapter invocation — scoreIntent does not call Claude; it only picks the winner. Calling is P0.5.2 scope (a separate, follow-up task) and ultimately router_call in Phase 1.5.

Existing files this task depends on

Path Role here Key lines / notes
src/domains/integrations/index.ts Barrel pattern to follow for a new domain subdirectory. Exports ./notifications.js, ./claude.js, ./mcp-bridge.js. whole file — 3 lines
src/domains/integrations/notifications.ts Reference style for a pure library-only ν module that exports a typed function, uses JSDoc header citing donor references, and frees itself from any MCP tool registration. header + signature patterns
docs/architecture/decisions/ADR-005-multi-model-defer.md Authoritative scope doc for this task. §Decision = invariants; §Implementation §”Phase 0 router stub” = this task’s brief. L36–46 (Decision), L83–89 (Phase 0 stub), L91–100 (Phase 1.5 upgrade path)
docs/architecture/decisions/ADR-004-tool-surface.md Locks post-Wave-H surface at 14 tools. This task must not grow that number. L52–69 (the 14-tool table)
docs/reference/extractions/delta-model-router-extraction.md Donor-era δ spec (heritage-only). Not imported; Phase 1.5 will consult it when writing the real scorer. referenced in task-breakdown.md line 251

Files NOT touched by this task

  • src/server.ts — no tool registration; no changes.
  • src/config.ts — no new env vars; no changes.
  • src/domains/integrations/* — δ does not depend on ν; no changes.
  • src/domains/tasks/*, src/domains/trail/*, src/domains/proof/*, src/domains/skills/* — δ is purely additive and decoupled.
  • src/db/schema.sql — δ does not touch the database in Phase 0.
  • Migration slots — no migration added.

New files / directories this task creates

src/domains/router/                                       ← new domain subdirectory (first δ lander)
src/domains/router/scoring.ts                             ← impl
src/domains/router/index.ts                               ← barrel: export * from './scoring.js'
src/__tests__/domains/router/                             ← new test subdirectory
src/__tests__/domains/router/scoring.test.ts              ← tests

docs/audits/p0-5-1-scoring-audit.md                       ← this file (Step 1)
docs/contracts/p0-5-1-scoring-contract.md                 ← Step 2
docs/packets/p0-5-1-scoring-packet.md                     ← Step 3
docs/verification/p0-5-1-scoring-verification.md          ← Step 5

Constraints and hazards (memorized)

  1. Tool surface is locked at 14. ADR-004 post-Wave-H — this task must register zero MCP tools. The router_score tool is Phase 1.5 scope.
  2. No env vars. ADR-005 §Implementation §”Phase 1.5 upgrade path” §2 shows that adapter configuration is Phase 1.5 work. COLIBRI_MODEL_* env vars belong to P0.5.2 at the earliest, and even there are deferred under the ADR-005 umbrella.
  3. Pure function. scoreIntent does no I/O, reads no env, calls no time/random source. The Phase 1.5 replacement will still be pure — the interface promises that.
  4. Frozen output. Return value is Object.freezed (including the nested scores). Callers cannot mutate the shared constant and surprise a later caller. This is load-bearing for Phase 1.5 — the moment we swap to a real scorer, callers that accidentally mutated in Phase 0 would start seeing phantom cross-call state.
  5. Determinism is trivially true. Constant output ⇒ scoreIntent(p, c) === scoreIntent(p, c) (structural equality via frozen identity). Tests assert this explicitly because the Phase 1.5 scorer will have the same contract and the test must carry forward unchanged.
  6. Namespace. The underlying concept is δ, but the directory is src/domains/router/ (concept name + domain alias), matching ADR-005 §Implementation §”Phase 1.5 upgrade path” §1 which names src/domains/router/scoring.ts as the file to “replace the constant” in Phase 1.5.
  7. Cross-worktree leak guard. Step 1 began with git status on the worktree — clean. No non-owned files will be modified.
  8. Type exports. The module exports ModelId, ScoreContext, IntentScore as type aliases so Phase 1.5 can widen ModelId to 'claude' | 'kimi' | 'codex' | ... without breaking existing imports. ModelId stays a string-literal union to preserve exhaustive checks.
  9. No import from other domains. The scorer is pure. Importing from src/domains/integrations/claude.ts would couple the stub to the adapter; Phase 1.5 will do the wiring through a separate src/domains/router/fallback.ts (P0.5.2) and router_call tool (Phase 1.5).
  10. Jest ESM + zod. Not applicable — this module has zero runtime dependencies (no zod), so jest.isolateModulesAsync is a non-issue.

Risk ledger

Risk Likelihood Impact Mitigation
Phase 1.5 breaks the Phase 0 call sites Low High Types are exported with forward-compatible shape; ModelId union widens additively. Tests assert the constant so any widening that changes the Phase 0 constant will fail loudly.
Test author relies on mutation of the return value Low Medium Return value is deep-frozen; mutation attempts throw under strict mode.
ADR-005 is re-interpreted again (like the Wave-G mis-read) Low Low Audit + contract explicitly cite §Decision and §Implementation; future readers find the fight already settled.
task-breakdown.md doc-fix lags Certain Low Out-of-scope for this PR. Wave I close-commit will reconcile §P0.5.1 wording against ADR-005.
P0.5.2 collides with this file Medium Low P0.5.2 lives in src/domains/router/fallback.ts and adds export * from './fallback.js'; to the barrel. No overlap with scoring.ts.

Conclusion

Surface is empty (no src/domains/router/ exists yet). ADR-005 §Decision and §Implementation unambiguously describe a Phase 0 constant-return stub with the exact shape { scores: { claude: 1.0 }, winner: 'claude' }. Step 2 (contract) encodes that as the behavioral contract; Step 3 (packet) lays out the two source files + the test file; Step 4 implements; Step 5 verifies.


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.