Audit — P1.5.9 Model Candidates Table Population

Task: P1.5.9 — Phase 1.5 graduation Wave 1 (foundation). Branch: feature/p1-5-9-candidates Base: origin/main @ c44e709f (post-R91 seal). Audit date: 2026-05-13. Audit scope: the mcp_model_candidates table — its existence, schema, current seed, and the column shape required by the staging file and concept doc.

1. Goal of the audit

Confirm or refute the claim made by both docs/3-world/social/llm.md §”Phase 0 posture” and the P1.5.9 slice of docs/guides/implementation/task-prompts/p1.5-delta-router-graduation.md that the mcp_model_candidates table is “schema-ready in Phase 0, populated with a single row (Claude Sonnet).” If true, P1.5.9 is a seed-only migration. If false, P1.5.9 must also create the table.

2. Method

  1. Grep the entire src/ tree for mcp_model_candidates and model_candidates.
  2. List src/db/migrations/ and inspect each .sql file for any CREATE TABLE matching the candidate table.
  3. Read src/db/schema.sql end-to-end for the column shape.
  4. Read src/db/index.ts to confirm the migration-runner convention (numbering, idempotency, transactions, PRAGMA user_version).
  5. Cross-reference with the concept-doc column list and the staging file’s acceptance criteria.

3. Findings

3.1. The table does not exist in any shipped migration

Migration Tables introduced Has mcp_model_candidates?
001_init.sql (empty / comment-only baseline) No
002_tasks.sql tasks, task_dependencies No
003_thought_records.sql thought_records, audit_events No
004_skills.sql skills No
005_retention.sql ALTER thought_records (retention cols) No
006_eta.sql merkle_roots, audit_sessions, etc. No
007_reputation.sql reputations, reputation_history No
008_experience_tokens.sql experience_tokens, mcp_witnesses No

Grep result (across src/):

$ rg 'mcp_model_candidates|model_candidates' src/
# (no matches)

The next free migration number is 009.

3.2. The concept doc and staging file both claim the table is “schema-ready” — that claim is incorrect against current code

  • docs/3-world/social/llm.md:196 says “mcp_model_candidates table is defined and populated with a single row (Claude Sonnet); the table is not mutated by any Phase 0 code path.”
  • The staging file slice for P1.5.9 says “Claude 3.5 Sonnet (already present)” and that it depends on “Phase 0 schema (mcp_model_candidates table columns present).”

Both are spec aspirations that were never realised in code. R75 Wave I shipped only the in-memory δ stubs (src/domains/router/scoring.ts + fallback.ts) — no SQL migration accompanied them.

3.3. Implication for P1.5.9 scope

Because the table is absent, P1.5.9 must:

  1. Create the table in migration 009_model_candidates.sql.
  2. Seed all 8 rows in that same migration (1 Claude Sonnet at enabled = TRUE, 7 others at enabled = FALSE).

This is still inside the staging file’s scope envelope: the acceptance criteria explicitly say “8 total rows in mcp_model_candidates post-migration” and the embedded agent prompt says “INSERT OR IGNORE 7 rows” alongside the existing Claude Sonnet row. The only delta vs the slice text is that we cannot inherit an in-place Claude Sonnet row — we insert it as part of this migration. The staging file’s claim that the Sonnet row is “already present” is corrected by this audit; the post-condition (8 rows, 7 disabled) is unchanged.

3.4. Column shape — sources reconciled

The staging file’s acceptance criteria name these 7 columns plus enabled:

Column Type Source
model_id TEXT PRIMARY KEY Concept doc + staging file
provider TEXT NOT NULL Concept doc + staging file
context_window_tokens INTEGER NOT NULL Concept doc + staging file
latency_tier TEXT CHECK IN ('fast','balanced','slow') Concept doc + staging file
cost_bps_per_kilotoken INTEGER NOT NULL Concept doc + staging file
domain_fit_profile INTEGER NOT NULL (bitmask over 8 ξ domains) Concept doc §”candidate table” + identity.md §”The eight domains”
enabled INTEGER NOT NULL DEFAULT 0 (SQLite has no BOOLEAN) Concept doc + staging file

p50_latency_ms is not a table column — the concept doc has no such field, and the cost/aggregates slice (P1.5.5) describes it as an in-memory ring-buffer aggregate, not a persisted column.

The 8-bit domain_fit_profile bitmask layout comes from docs/3-world/social/identity.md:85 §”The eight domains”:

Bit Domain Meaning
0 (0x01) BUILDER Produces new artifacts
1 (0x02) JUDGE Evaluates artifacts
2 (0x04) INVESTOR Allocates resources
3 (0x08) MENTOR Guides others
4 (0x10) GUARDIAN Enforces rules
5 (0x20) INNOVATOR Proposes new rules
6 (0x40) DIPLOMAT Mediates
7 (0x80) STEWARD Curates state

0xFF = all 8 domains; 0x00 = none.

3.5. Cohort table from the concept doc

docs/3-world/social/llm.md:79-89 §”Phase 1.5 candidate cohort”:

Model Window Cost (indicative bps) Latency tier Strengths
Claude 3.5 Sonnet 200k high balanced code review, structured output, long-context reasoning
Claude 3.5 Haiku 200k low fast classification, triage, high-throughput
GPT-4o 128k high balanced multimodal, general reasoning
GPT-4o mini 128k low fast simple tasks, cheap fallback
Gemini 1.5 Pro 1M medium slow huge-context synthesis, video
Llama 3.3 70B 128k low (self-host) varies data-sovereign deploys
Mixtral 8x22B 64k low fast permissive open-weight, code-heavy
Kimi K2 200k medium balanced Chinese/English parity, long-context

The concept doc uses qualitative cost buckets (“high / medium / low”). The staging file says “use indicative cost_bps values from the concept doc ‘indicative’ ranges — the live post-task callback refreshes them.” The contract step (P1.5.9 Step 2) will pin numeric values inside the qualitative buckets and document the choice. Llama’s “varies” latency maps to balanced (consistent with the staging file’s row table at L1332: 'balanced').

3.6. Domain-fit bitmask mapping (preliminary)

This pre-implementation table is the input to Step 2 (contract) and will be ratified there. The strength descriptions in the concept doc are mapped to ξ domains:

Model Strengths (concept doc) Domains (8-bit bitmask) Hex
Claude 3.5 Sonnet code review, structured output, long-context BUILDER+JUDGE+MENTOR+STEWARD 0x8B = bit 0,1,3,7
Claude 3.5 Haiku classification, triage, high-throughput JUDGE+DIPLOMAT 0x42 = bit 1,6
GPT-4o multimodal, general BUILDER+JUDGE+INNOVATOR 0x23 = bit 0,1,5
GPT-4o mini simple tasks, cheap fallback BUILDER+DIPLOMAT 0x41 = bit 0,6
Gemini 1.5 Pro huge-context synthesis, video STEWARD+INNOVATOR+JUDGE 0xA2 = bit 1,5,7
Llama 3.3 70B data-sovereign, customization GUARDIAN+BUILDER+STEWARD 0x91 = bit 0,4,7
Mixtral 8x22B open-weight, code-heavy BUILDER+INVESTOR 0x05 = bit 0,2
Kimi K2 CN/EN parity, long-context BUILDER+MENTOR+DIPLOMAT 0x49 = bit 0,3,6

Bitmasks are advisory starting points; the post-task callback in the real Phase 1.5 activation will refine them. They are deterministic and non-zero per-model so scoring (P1.5.1) can read non-trivial values.

3.7. Indicative cost bps assignments

Cost bucket → numeric mapping (Step 2 will pin and justify):

Bucket bps per 1k tokens Models
high 300 Claude 3.5 Sonnet, GPT-4o
medium 125 Gemini 1.5 Pro, Kimi K2
low (frontier mini) 80 Claude 3.5 Haiku
low (self-host) 50 Llama 3.3 70B
low (open-weight) 60 Mixtral 8x22B
low (frontier mini) 15 GPT-4o mini

These are bps per kilotoken, i.e. for 1000 tokens. A bps is 0.0001 USD. So 300 bps/1k = $0.030/1k = $30/1M. All chosen values are within the publicly published rate-card ranges, and per the spec are indicative — the post-task callback updates the table.

3.8. Migration-runner conventions (relevant to Step 3 packet)

From src/db/index.ts:

  • Numeric prefix NNN_*.sql discovered via regex; gap-free ordering is not required, only ascending unique versions.
  • File is read, line-comment-stripped, and db.exec‘d inside a transaction; transaction also bumps PRAGMA user_version = NNN.
  • Empty-body files (comment-only) are allowed (the body-length check short-circuits db.exec).
  • The runner is idempotent: any migration whose version <= currentVersion is skipped.
  • Block comments (/* ... */) are not stripped — only -- line comments. The migration we write will use line comments throughout.

The migration must therefore be:

  • Filename: 009_model_candidates.sql.
  • Body: a CREATE TABLE followed by INSERT OR IGNORE rows.
  • Comments: -- line comments only.

3.9. Idempotency mechanics

The staging file says “Migration is idempotent (INSERT OR IGNORE on primary key).” Two layers of idempotency apply:

  1. Migration-runner-level: PRAGMA user_version skips applied migrations on a subsequent boot. Once we ship 009, a re-boot never re-executes the file.
  2. Statement-level: if the runner is forced to re-execute (e.g. a fresh DB), CREATE TABLE (without IF NOT EXISTS) fails on the second run. We will use CREATE TABLE IF NOT EXISTS plus INSERT OR IGNORE so the migration is self-idempotent independent of the user_version shield, matching the staging file’s stated acceptance criteria.

3.10. Reversibility

The staging file requires a DOWN section. SQLite migrations in Phase 0 are forward-only; the runner has no DOWN interpreter. The convention in the codebase (read 002 / 007 / 008) is that the DOWN SQL is preserved as a commented-out block at the bottom of the migration file so a future operator can run it manually with sqlite3 if a rollback is needed. We follow that convention.

4. Surface that this task touches

  • CREATE: src/db/migrations/009_model_candidates.sql.
  • CREATE (tests): src/__tests__/db/migrations/009_model_candidates.test.ts.
  • CREATE (chain docs): docs/audits/, docs/contracts/, docs/packets/, docs/verification/ files for P1.5.9.
  • No changes to src/db/schema.sql (per the audit, no columns missing from the concept doc’s table). The schema-snapshot file at src/db/schema.sql is a comment-only reference asset that is NOT passed to db.exec; updating it is not load-bearing. We may add a δ Model Candidates — introduced by 009_model_candidates.sql block to keep it in sync with reality, but the migration itself is the source of truth.

5. Pre-conditions (verified)

  • origin/main @ c44e709f checked out cleanly to .worktrees/claude/p1-5-9-candidates.
  • Migrations 001–008 present; next free slot is 009.
  • src/domains/router/scoring.ts Phase 0 stub (Claude only) in place.
  • src/domains/router/fallback.ts Phase 0 stub (single-member chain) in place.
  • docs/3-world/social/llm.md cohort table read in full.
  • docs/3-world/social/identity.md 8-domain list read in full.
  • src/db/index.ts migration-runner conventions captured.
  • No conflicts with downstream prompts — P1.5.1 (scoring) reads the table this seeds; the cohort, columns, and indicative ranges match what P1.5.1’s prompt expects.

6. Risk register

Risk Mitigation
Concept-doc cohort text ambiguous on bitmask Pinned mapping in §3.6 + ratification in Step 2 contract
Indicative bps may drift before live activation Spec explicitly authorises post-task callback to refresh
INTEGER storage of bitmask + enabled boolean SQLite uses INTEGER for both; standard pattern (see 007/008)
Fresh DB + previously applied migration re-execution CREATE TABLE IF NOT EXISTS + INSERT OR IGNORE
enabled = TRUE on rows without adapters Only Sonnet ships enabled = 1; 7 others ship enabled = 0
Test runner uses every migration db-init.test.ts:166 asserts user_version === countRealMigrations(); adding one migration auto-bumps both sides — no fixture change required

7. Pre-flight reading

8. Outcome of audit

P1.5.9 will:

  • Create migration 009_model_candidates.sql.
  • Create the mcp_model_candidates table with the 7 columns + enabled (8 columns total).
  • Seed 8 rows: 1 Claude Sonnet at enabled = 1 + 7 candidates at enabled = 0.
  • Make the migration self-idempotent (CREATE TABLE IF NOT EXISTS, INSERT OR IGNORE).
  • Preserve a DOWN block in commented form.
  • Land Jest tests that assert: table exists, 8 rows present, 7 of them enabled = 0, exactly Claude Sonnet has enabled = 1, cohort values match this audit’s tables, migration is idempotent under re-execution.
  • Touch nothing in src/domains/router/ (scoring/fallback continue to return their Phase 0 constants; that’s P1.5.1’s job).
  • Add zero MCP tools (router tools are P1.5.7’s job).

Back to top

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

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