Data Storage Migration Backlog (2026)
Data Storage Migration Backlog (2026)
Section titled “Data Storage Migration Backlog (2026)”Execution order (authoritative)
Section titled “Execution order (authoritative)”The omni-agent reads this list top-down. An item is “landed” when git log --all --grep "^M-NN\b" --pretty=oneline returns at least one commit. The agent skips landed items and executes the first un-landed item whose listed blockers are all landed.
- M-00, M-01, M-02, M-03, M-04, M-05, M-06, M-07, M-08, M-09 (Phase 0 — scaffolding)
- M-20 (Phase 1b — baseline re-sync; promoted ahead of Phase 1 due to widening BASELINE_VERSION drift, see F1)
- M-10, M-11, M-12, M-13, M-14, M-15, M-16, M-17, M-18, M-19 (Phase 1 — contracts surface; skip M-20, already landed)
- M-21, M-22, M-23, M-24, M-25, M-26, M-27, M-28, M-29-spike, M-29, M-30 (Phase 2 — DDL flip; note M-29-spike before M-29)
- M-31, M-32, M-33, M-34, M-35, M-36, M-37, M-38, M-39 (Phase 3 — Tier B/C rollout)
- M-40, M-41, M-42, M-43, M-44, M-45, M-46, M-47, M-48, M-49 (Phase 4 — observability)
- M-50, M-51, M-52, M-53, M-54, M-55, M-56, M-57, M-58, M-59 (Phase 5 — memory hygiene)
- M-60, M-61, M-62, M-63, M-64, M-65, M-66 (Phase 6 — regression gates)
- M-67, M-68, M-69, M-70, M-71, M-72, M-73, M-74 (Phase 7 — gap cleanup)
- M-75, M-76, M-77, M-78 (Phase 7b — reconcile orchestration surface; added 2026-04-21)
Deferred side-items (M-55b, M-55c, M-57d) are opt-in and not part of the main sweep.
Ticket-level breakdown of the work described in data-storage-ssot-2026.md. Every ticket has:
- Owner — default claimant.
- Blast radius — H/M/L how much of the workspace changes.
- Blockers — tickets that must complete first.
- SSOT findings — cross-reference into the SSOT.
- Sub-steps — numbered, each with a concrete file path or function name so a future LLM or human contributor can execute without re-deriving context.
- Verification — exactly how to tell it’s done (a grep, a diff, a CI check, a test, or a doctor sub-command).
Every ticket ends by pointing at the guard sub-check or grep rule that prevents regression.
Phase 0 — Scaffolding
Section titled “Phase 0 — Scaffolding”M-00 · Land the SSOT triplet
Section titled “M-00 · Land the SSOT triplet”- Owner: Governance.
- Blast radius: L (doc-only).
- Blockers: none.
- SSOT findings: F56, F62–F74.
- Sub-steps:
- Write
docs/src/architecture/data-storage-ssot-2026.md(this series). - Write
docs/src/architecture/data-storage-migration-backlog-2026.md(this document). - Write
docs/src/architecture/data-storage-lint-and-ci-spec-2026.md. - Add entries under a new “Data Storage” section in
docs/src/architecture/research-index.md. - Link from
docs/src/SUMMARY.mdif it exists (check first).
- Write
- Verification:
rg '^# Data Storage SSOT' docs/src/architecture/returns exactly the three docs;rg 'data-storage-ssot-2026.md' docs/src/architecture/research-index.mdnon-empty.
Landed in a029121a on 2026-04-22; verification: green.
M-01 · Scaffold vox ci data-storage-guard sub-command (stub)
Section titled “M-01 · Scaffold vox ci data-storage-guard sub-command (stub)”- Owner: CI.
- Blast radius: L.
- Blockers: M-00.
- SSOT findings: F56.
- Sub-steps:
- Add a new variant to the
CiCmdenum atcrates/vox-cli/src/commands/ci/cmd_enums.rsnamedDataStorageGuard. - Create
crates/vox-cli/src/commands/ci/data_storage_guard.rswith apub fn run(opts: &GuardOpts) -> anyhow::Result<GuardReport>stub that logs “stub” and returnsOk(GuardReport::empty()). - Register the handler in
crates/vox-cli/src/commands/ci/mod.rsfollowing the pattern ofcontracts_index.rsorexec_policy_contract.rs. - Add a
GuardReporttype that mirrorscontracts/db/data-storage-guard-report.v1.schema.json(authored in M-04). - Wire a no-op invocation in
.gitlab-ci.ymlvox-ci-guardsjob so the command exists but is green.
- Add a new variant to the
- Verification:
vox ci data-storage-guard --helpsucceeds locally; CI pipeline on a PR showsvox-ci-guards: passedwith a stub log line.
Landed in 1fa8aa2c on 2026-04-22; verification: green.
M-02 · Land .cursor/rules/data-storage-policy.mdc (NET-NEW)
Section titled “M-02 · Land .cursor/rules/data-storage-policy.mdc (NET-NEW)”- Owner: Governance.
- Blast radius: L.
- Blockers: M-00.
- SSOT findings: §3 non-negotiables.
- Sub-steps:
- Confirm the file does not exist (it is net-new from this work):
ls .cursor/rules/data-storage-policy.mdc. - Draft using the 8 sibling
.mdcfiles as format template (build-environment.mdc,ci-runner-convention.mdc, etc.). - Include a
description:frontmatter andalwaysApply: true. - List non-negotiables from SSOT §3 verbatim.
- Cross-link to the SSOT + lint spec via
mdc:links.
- Confirm the file does not exist (it is net-new from this work):
- Verification:
cat .cursor/rules/data-storage-policy.mdcis non-empty.
Landed in df8fdb32 on 2026-04-22; verification: green.
M-03 · Extract vox-cli::telemetry_spool into new vox-spool crate
Section titled “M-03 · Extract vox-cli::telemetry_spool into new vox-spool crate”- Owner: Data Core + CLI.
- Blast radius: M.
- Blockers: M-00.
- SSOT findings: F21, F22, F30, F45.
- Sub-steps:
- Read
crates/vox-cli/src/telemetry_spool.rsin full and inventory every public function. - Create new crate
crates/vox-spool/viacargo new --lib crates/vox-spool(or write theCargo.tomlandsrc/lib.rsdirectly). This crate is born in this ticket — it did not pre-exist. - Add
vox-spool = { path = "crates/vox-spool" }to the workspace[workspace.dependencies]table in the rootCargo.toml. - Move the existing queue logic into
vox_spool::queue::{enqueue, pending_dir, spool_root}(preserving behavior). - Add new JSONL API:
vox_spool::jsonl::{SpoolWriter, SpoolReader, ChannelName, RotationPolicy, FsyncPolicy, SpoolConfig}. - Implement
ChannelName::new(raw)with regex^[a-z][a-z0-9-]{0,31}$. - Implement
SpoolWriter::append(&mut self, &str) -> Result<()>with rotation, fsync-per-rotation default, atomic rename. - Re-export queue surface from
crates/vox-cli/src/telemetry_spool.rsas thin wrappers that callvox_spool::queue::*; schedule removal in 0.6. - Write unit tests for rotation boundary (hour change), channel validation, fsync policy assertion via
fdatasynccounter. - Update
crates/vox-cli/src/commands/telemetry.rs(or equivalent) to use the new API.
- Read
- Verification:
cargo build -p vox-spoolsucceeds.cargo test -p vox-spoolpasses.rg 'OpenOptions::new\(\)[^;]*\.append\(\s*true' crates/ --glob '!crates/vox-spool/**' --glob '!tests/**'returns at most the allowlisted exceptions (see lint spec rule 8).
Landed in 1cf50d3b on 2026-04-22; verification: green.
M-04 · Land the guard’s policy-file schema
Section titled “M-04 · Land the guard’s policy-file schema”- Owner: CI.
- Blast radius: L.
- Blockers: M-01.
- SSOT findings: F56.
- Sub-steps:
- Confirm
contracts/db/data-storage-policy.v1.yamlalready exists (it is pre-existing, not net-new; modified in prior work). - Create a new JSON Schema
contracts/db/data-storage-policy.v1.schema.jsonthat validates the YAML shape:tiers,env_vars,rust_policy,repo_hygiene,contract_policy,ignored_paths,frozen_core_crates,generated_files. - Add entry to
contracts/index.yamlmatching the siblingexec-policyentry’s shape. - Create
contracts/db/data-storage-guard-report.v1.schema.jsonfor the guard’s output. - Wire loading inside
vox_cli::commands::ci::data_storage_guard::load_policy(path) -> anyhow::Result<DataStoragePolicy>usingserde_yaml+jsonschema::Validator.
- Confirm
- Verification:
vox ci contracts-indexstill passes.vox ci data-storage-guard --check-policy-onlyreturns 0 on a good policy file and nonzero on a malformed one (add a synthetic broken copy attests/fixtures/bad-data-storage-policy.yaml).
Landed in 9d5e915c on 2026-04-22; verification: green.
M-05 · Delete empty schemas/ directory
Section titled “M-05 · Delete empty schemas/ directory”- Owner: CI.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F16.
- Sub-steps:
- Verify
schemas/is empty:ls -la schemas/. - Create
.voxVoxScript atscripts/migrations/2026-phase1-delete-empty-schemas-dir.voxthat removes the dir. - Run the script:
vox run scripts/migrations/2026-phase1-delete-empty-schemas-dir.vox. - Add guard sub-check
schemas-dir-absent.
- Verify
- Verification:
test ! -d schemas. Guardschemas-dir-absentfails on a synthetic re-creation.
M-06 · Delete loose repo-root strays
Section titled “M-06 · Delete loose repo-root strays”- Owner: CI.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F23, F24, F25.
- Sub-steps:
- Inventory strays:
build_errors.txt,codex-cutover-20260412T065226Z.sidecar.json,codex-cutover-20260412T071753Z.sidecar.json,test_lexer.rs,error.vox,prototype_vox_tokenizer.json. - For
prototype_vox_tokenizer.json: reclassify — move tocrates/vox-compiler/tests/fixtures/prototype_tokenizer.json(see M-12 context). Others are pure deletion. - Create VoxScript
scripts/migrations/2026-phase1-delete-repo-root-strays.vox. - Add guard sub-check
repo-root-strays-absentthat fails if any of the six files reappears. - Update
.gitignorestanza to fail forward: don’t add them, just forbid re-creation in the guard.
- Inventory strays:
- Verification:
for f in build_errors.txt codex-cutover-*.sidecar.json test_lexer.rs error.vox; do [ ! -e "$f" ]; done.
Landed in a9372778 on 2026-04-22; verification: green.
M-07 · Fix vox-agent.json / vox-schema.json / vox.tokens.json ignored-but-tracked contradiction
Section titled “M-07 · Fix vox-agent.json / vox-schema.json / vox.tokens.json ignored-but-tracked contradiction”- Owner: Governance.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F26.
- Sub-steps:
- For each of the three files, determine intent by reading contents and grepping for their consumers.
- Decide per file: keep-tracked (then remove from
.voxignore/.aiignore) or gitignore (thengit rm --cachedand add to.gitignore). - Add a
docs/src/reference/repo-root-files.mdexplaining the canonical list. - Add guard sub-check
ignore-tracked-paritythat enumerates.voxignore,.aiignore,.cursorignore,.aiexcludeand fails if any listed file is also tracked by git.
- Verification: guard check passes; documentation reflects the decision.
Landed in 64b17bf4 on 2026-04-22; verification: green.
M-08 · scratch/ hygiene
Section titled “M-08 · scratch/ hygiene”- Owner: CI.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F27.
- Sub-steps:
git add scratch/.gitkeep(empty file).- Add
scratch/*to.gitignorewith a!scratch/.gitkeepnegation. - Add guard sub-check
scratch-clean:git ls-files scratch/ | grep -v '.gitkeep' | wc -lmust be 0.
- Verification: guard passes on a fresh clone.
Landed in 6c000bd9 on 2026-04-22; verification: green.
M-09 · .vox/ boot contract
Section titled “M-09 · .vox/ boot contract”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-26.
- SSOT findings: F28.
- Sub-steps:
- Inventory current
.vox/on a fresh clone: which subdirs are documented, which are write-on-demand, which are stale. - Author
docs/src/reference/dot-vox-layout.mdlisting each subdir (agents/,artifacts/,bin/,cache/,memory/,sessions/) with purpose, writer crate, and lifetime. - In
crates/vox-config/src/paths.rs, addpub fn known_dot_vox_subdirs() -> &'static [&'static str]. - Add guard sub-check
dot-vox-layout-contract: on a fresh clone,.vox/contents must be a subset of the known list. - Create a VoxScript
scripts/migrations/2026-phase7-dotvox-cleanup.voxthat deletes unknown subdirs after prompting.
- Inventory current
- Verification: guard check passes post-migration.
Phase 1 — Contracts canonical (first cut)
Section titled “Phase 1 — Contracts canonical (first cut)”M-10 · First codegen pass inside vox-jsonschema-util
Section titled “M-10 · First codegen pass inside vox-jsonschema-util”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-04.
- SSOT findings: F11, F64 (no new crate needed).
- Sub-steps:
- In
crates/vox-jsonschema-util/src/lib.rs, declarepub mod codegen;. - Create
crates/vox-jsonschema-util/src/codegen/mod.rsexportingloader,validator,emit_rust,emit_sql,diff. - Implement
codegen::loader::load_contracts(root: &Path) -> Result<ContractIndex>that parsescontracts/index.yamland loads each file. - Implement
codegen::validator::validate(&ContractIndex) -> Result<()>that checks each file againstcontracts/index.schema.jsonand enforcesx-vox-version/filename parity (F12). - Pick the generator for the first domain: start with a hand-rolled emitter for
contracts/orchestration/agent-harness.schema.json. Benchmark againsttypifybefore committing the choice (Open Question #4). - Output to
crates/vox-orchestrator/src/generated/agent_harness.rs(new directorysrc/generated/with a singlemod.rsre-export). - Add
crates/vox-orchestrator/src/generated/to.gitignoreAND also commit agenerated.manifest.sha256that CI diffs against.
- In
- Verification:
cargo build -p vox-jsonschema-utilsucceeds.cargo run -p vox-jsonschema-util --example emit_agent_harness -- --dry-runprints a diff but makes no writes.cargo run -p vox-jsonschema-util --example emit_agent_harnesswrites the file; re-running is a no-op (idempotent).
Landed in 2a819464 on 2026-04-22; verification: green.
[x] M-11 · First consumer — vox-orchestrator::harness (Landed: 2026-04-22)
Section titled “[x] M-11 · First consumer — vox-orchestrator::harness (Landed: 2026-04-22)”- Owner: Orchestrator.
- Blast radius: M.
- Blockers: M-10.
- SSOT findings: F11.
- Sub-steps:
- Move legacy hand-maintained harness structs and tests from
crates/vox-orchestrator/src/harness.rstocrates/vox-orchestrator/src/legacy/harness_hand.rs. - Update
vox-orchestratorconsumers to import generated types fromcrate::generated::agent_harness. - Add CI sub-check
schema-codegen-driftthat runsvox-jsonschema-util’s emitter with--verifyand fails if the output diff is non-empty. - Update
contracts/index.yamlentry foragent-harness.schema.jsonto addenforced_by: - vox ci data-storage-guard schema-codegen-drift.
- Move legacy hand-maintained harness structs and tests from
- Verification:
cargo test -p vox-orchestratorpasses;vox ci data-storage-guard --only schema-codegen-driftis green.
Landed in [x] on 2026-04-22; verification: green.
M-12 · Version header parity (x-vox-version ↔ filename .vN.)
Section titled “M-12 · Version header parity (x-vox-version ↔ filename .vN.)”- Owner: Data Core.
- Blast radius: L.
- Blockers: M-10.
- SSOT findings: F12.
- Sub-steps:
- Inventory: for every file under
contracts/matching*.v[0-9]+.*, readx-vox-versionfield if present. - Find mismatches. For each, decide: rename filename OR fix header (prefer header fix).
- Create VoxScript
scripts/migrations/2026-phase1-contract-headers.voxthat performs the fix with--checkdry-run mode. - Add guard sub-check
version-header-parity.
- Inventory: for every file under
- Verification: after running once, a second
--checkrun produces no changes (idempotent).
M-13 · Author contracts/config/env-vars.v1.yaml
Section titled “M-13 · Author contracts/config/env-vars.v1.yaml”- Owner: Config owner.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F13, F47.
- Sub-steps:
- Grep the workspace:
rg 'env::var(_os)?\s*\(\s*"(VOX|TURSO|XDG)_' crates/ --type rust -o— inventory all env-var reads. - Create
contracts/config/env-vars.v1.yamllisting each with:name,owner_crate,kind(string/int/bool/path),default,required,introduced_in,deprecates(optional),see_also(optional). - Add
contracts/config/env-vars.v1.schema.json(JSON Schema for the YAML). - Add to
contracts/index.yaml.
- Grep the workspace:
- Verification: file exists and validates against its schema.
M-14 · Enforce env-var parity
Section titled “M-14 · Enforce env-var parity”- Owner: Config owner.
- Blast radius: L.
- Blockers: M-13.
- SSOT findings: F47.
- Sub-steps:
- Add guard sub-check
env-parity: everyenv::var("VOX_…")/env::var_os("VOX_…")incrates/must match aname:incontracts/config/env-vars.v1.yaml. - Allow a per-file
// vox-env-skip: reasonpragma to opt out, logged in the guard report.
- Add guard sub-check
- Verification: guard fails on a synthetic PR that introduces
env::var("VOX_NEW_SYNTHETIC")without updating the contract.
M-15 · Author contracts/telemetry/events.v1.yaml (event catalog)
Section titled “M-15 · Author contracts/telemetry/events.v1.yaml (event catalog)”- Owner: Platform (observability).
- Blast radius: M.
- Blockers: none.
- SSOT findings: F14.
- Sub-steps:
- Inventory
contracts/telemetry/*.schema.json(there are several:completion-run.v1.schema.json,completion-detector-snapshot.v1.schema.json, …). - Create
contracts/telemetry/events.v1.yamlwith an array of event records:id,schema_path,owner_crate,tier(must be “B”),retention_days,description. - Add
contracts/telemetry/events.v1.schema.json. - Add to
contracts/index.yaml.
- Inventory
- Verification: file validates;
events.v1.yamllists every existing telemetry schema.
M-16 · OpenAPI $ref drift
Section titled “M-16 · OpenAPI $ref drift”- Owner: Populi.
- Blast radius: M.
- Blockers: M-10.
- SSOT findings: F18.
- Sub-steps:
- For every OpenAPI spec under
contracts/populi/*.openapi.yaml, resolve inline type definitions that duplicate top-level schemas. - Rewrite inline duplicates to use
$ref: '../<domain>/<type>.v1.schema.json'. - Add guard sub-check
openapi-contract-ref-parity: fail if atype/propertiesblock is structurally equivalent to a top-level schema but not a$ref.
- For every OpenAPI spec under
- Verification: guard passes;
vox schema generate --dry-runproduces byte-identical output before/after.
M-17 · Format consolidation
Section titled “M-17 · Format consolidation”- Owner: Governance.
- Blast radius: L.
- Blockers: M-16.
- SSOT findings: F19.
- Sub-steps:
- For each subdir of
contracts/, list distinct file extensions. - Author a per-subdir README.md that declares allowed formats (start with one, allow openapi as a sibling for public surface).
- Add guard sub-check
domain-format-single.
- For each subdir of
- Verification: every
contracts/<subdir>/has a README listing allowed formats.
Phase 2 — DDL flip
Section titled “Phase 2 — DDL flip”M-20 · Re-sync BASELINE_VERSION and baseline digest (raised priority: Phase 1b)
Section titled “M-20 · Re-sync BASELINE_VERSION and baseline digest (raised priority: Phase 1b)”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-04.
- SSOT findings: F1, F20, F49.
- Priority note: Between 2026-04-20 and 2026-04-21
BASELINE_VERSIONmoved 55 → 58 → 59 while the contract stayed at 54. The gap is widening with each orchestration-schema PR; M-20 must land before Phase 2 rather than mid-Phase-2. - Sub-steps:
- Compute current baseline digest:
cargo run -p vox-db --example print_baseline_digest— or, if no such example exists, write one atcrates/vox-db/examples/print_baseline_digest.rsthat printsvox_db::schema::schema_baseline_digest_hex(). - In
contracts/db/baseline-version-policy.yaml, updaterepository_baseline_integerto matchcrates/vox-db::schema::manifest::BASELINE_VERSION(59 at HEAD 2026-04-21; read the value at PR-land time, do not hard-code in this ticket). - Update
repository_baseline_digest_hexto matchvox_db::schema::schema_baseline_digest_hex(). - Route
VOX_DB_URL/VOX_DB_TOKENreads throughvox_secrets::resolve_secret(...): modifycrates/vox-db/src/config.rsto call vox-secrets first, fall back toenv::varonly during tests. - Run existing
vox ci check-codex-ssot— should now pass. - Add auto-update logic inside the guard: when
schema_baseline_digest_hex()changes, a follow-up PR is suggested (or auto-drafted via a CI bot) rather than a hard failure that blocks all PRs. - Add a pre-landing check to the PR template: if
crates/vox-db/src/schema/manifest.rswas touched in the PR, the PR body must include a lineBASELINE_VERSION: <new>matching the Rust constant; used to auto-update the contract via a post-merge workflow.
- Compute current baseline digest:
- Verification:
vox ci check-codex-ssotpasses.grep -E 'BASELINE_VERSION: i64 = ' crates/vox-db/src/schema/manifest.rsoutput andgrep 'repository_baseline_integer:' contracts/db/baseline-version-policy.yamloutput agree byte-for-byte after stripping labels.cargo run -p vox-cli --quiet -- ci data-storage-guard --check codex-ssot-digest --jsonreturns"status": "pass".
Landed in 87d4031a on 2026-04-22; verification: green.
M-21 · Delta migration framework
Section titled “M-21 · Delta migration framework”- Owner: Data Core.
- Blast radius: H.
- Blockers: M-20.
- SSOT findings: F2.
- Sub-steps:
- Create
crates/vox-db/src/schema/deltas/with amod.rs. - For each future bump of
BASELINE_VERSION, add avNN_vMM.rsfile withpub fn upgrade(conn: &Connection) -> Result<()>. - Write
vox_db::migrate_from(current: i64, target: i64)that applies deltas in order. - Write integration test that starts from an old baseline, migrates to head, and verifies row parity.
- Decide forward-only vs forward+backward (Open Question #5). Default: forward-only.
- Create
- Verification:
cargo test -p vox-db --test delta_migrationpasses; a synthetic “old” database migrates cleanly.
M-22 · Emit SQL side of the codegen pipeline
Section titled “M-22 · Emit SQL side of the codegen pipeline”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-10, M-21.
- SSOT findings: F3.
- Sub-steps:
- Implement
vox_jsonschema_util::codegen::emit_sqlas a visitor that walks aContractIndexand produces DDL per domain. - For the first domain (
foundation), generatecrates/vox-db/src/schema/generated/foundation.sqland verify byte-equality withcrates/vox-db/src/schema/domains/foundation.rs’sSCHEMA_FOUNDATIONconstant. - Commit the generated SQL as canonical if equal; else file a drift-fix ticket before proceeding to the remaining 18 domains.
- Implement
- Verification:
cargo test -p vox-jsonschema-util --test sql_roundtrippasses.
M-23 · Flip DDL ownership
Section titled “M-23 · Flip DDL ownership”- Owner: Data Core.
- Blast radius: H.
- Blockers: M-22.
- SSOT findings: F3.
- Sub-steps:
- For each of the 19 domain fragments in
crates/vox-db/src/schema/domains/, convert the Rust string constant into aninclude_str!()that reads fromcrates/vox-db/src/schema/generated/<domain>.sql. - Move the authoritative DDL into
contracts/db/domains/<domain>.v1.yaml. - Remove the
pub const SCHEMA_<DOMAIN>: &str = r#"…"#;definitions fromdomains/<domain>.rs, replace with a re-export of the included file. - Add guard sub-check
ddl-owner-parity: every SQL fragment invox-dbmust beinclude_str!, never a hand-written string constant.
- For each of the 19 domain fragments in
- Verification:
cargo build -p vox-dbsucceeds;rg 'pub const SCHEMA_' crates/vox-db/src/schema/domains/returns zero (all constants now come via include_str or generation).
M-24 · Fold research-audit-codex.db into store.db
Section titled “M-24 · Fold research-audit-codex.db into store.db”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-23.
- SSOT findings: F4.
- Sub-steps:
- Locate the crate that writes to
research-audit-codex.db:rg 'research-audit-codex' crates/. - Add a
research_audit_table prefix to the existing DDL (as a new domain incontracts/db/domains/research_audit.v1.yaml). - Write VoxScript
scripts/migrations/2026-phase2-fold-research-audit.voxthat reads the old DB and inserts intostore.db. - Delete
research-audit-codex.dbpost-fold. - Update writers to point at
store.db.
- Locate the crate that writes to
- Verification:
ls .vox/does not containresearch-audit-codex.db; writer tests pass.
M-25 · Delete orphan vox_hardened.db
Section titled “M-25 · Delete orphan vox_hardened.db”- Owner: Data Core.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F5.
- Sub-steps:
- Final confirmation:
rg 'vox_hardened|hardened\.db' crates/returns zero (verified 2026-04-21). - Create VoxScript
scripts/migrations/2026-phase2-delete-vox-hardened-db.vox. - The script: removes
vox_hardened.db,vox_hardened.db-wal, andvox_hardened.db-shmif present. - Add to
.gitignoreto prevent re-creation. - Add guard sub-check
orphan-db-absentthat fails ifvox_hardened.dbreappears at repo root.
- Final confirmation:
- Verification:
[ ! -e vox_hardened.db ]; guard passes.
M-26 · Route .vox_modules/local_store.db opens through vox-db + vox-config
Section titled “M-26 · Route .vox_modules/local_store.db opens through vox-db + vox-config”- Owner: CLI + Data Core.
- Blast radius: M.
- Blockers: M-23.
- SSOT findings: F6.
- Sub-steps (HEAD grep 2026-04-21 found three call-site families; the ticket must close all three):
- Add
pub fn local_pm_store_path(root: &Path) -> PathBuftocrates/vox-config/src/paths.rs. This is the only place that constructs the.vox_modules/local_store.dbpath string. - Add
pub fn open_local_pm_store(root: &Path) -> Result<VoxDb>tocrates/vox-db/src/lib.rsthat wraps the open and applies the PM subset of baseline. This replaces the existing helper shape atcrates/vox-cli/src/commands/pm_lifecycle.rs:10–14— move, don’t duplicate. - Rewrite
crates/vox-cli/src/commands/pm_lifecycle.rs:10–14::open_local_pm_store()to delegate tovox_db::open_local_pm_store(&root)+vox_config::paths::local_pm_store_path(&root)instead of hand-building the path; delete the literal.vox_modulesat line 6 and the literallocal_store.dbat line 13. - Replace the three raw-literal callers that bypass the helper today:
crates/vox-cli/src/commands/search.rs:34— delete thelet store_path = ".vox_modules/local_store.db";line and theVoxDb::open(store_path)at line 35; callpm_lifecycle::open_local_pm_store(&root)instead.crates/vox-cli/src/commands/diagnostics/tools/search.rs:35— same shape as above, same fix.crates/vox-cli/src/commands/info.rs:33— same shape as above, same fix.
- Rewrite the three
.context("open .vox_modules/local_store.db")strings atupdate.rs:17,sync.rs:33,lock.rs:11to reference the helper (e.g.,.context("open_local_pm_store")) so grep rule 16 (vox-modules-local-db-literal) can flip fromScaffolded→Errorwithout a false positive on context messages. The doc comment atpm/mod.rs:66may keep its.vox_modules/local_store.dbreference — doc comments are excluded from the grep rule’s glob. - Update
crates/vox-cli/tests/pm_lifecycle_integration.rsto exercise the new API. - Flip the guard sub-check
vox-modules-local-db-literalincrates/vox-cli/src/commands/ci/data_storage_guard/grep_rules.rsfromSeverity::ScaffoldedtoSeverity::Error.
- Add
- Verification:
cargo test -p vox-cli --test pm_lifecycle_integrationpasses.rg '"\.vox_modules/local_store\.db"' crates/ --type rustreturns zero matches.rg 'let store_path = "\.vox_modules' crates/ --type rustreturns zero matches.cargo run -p vox-cli --quiet -- ci data-storage-guard --check vox-modules-local-db-literal --jsonreturns"status": "pass".
M-27 · Collection::new() name validation
Section titled “M-27 · Collection::new() name validation”- Owner: Data Core.
- Blast radius: L.
- Blockers: M-23.
- SSOT findings: F7.
- Sub-steps:
- Read
crates/vox-db/src/collection.rs:83to confirm the string-interpolated INSERT. - Replace with a bound-parameter statement OR validate the collection name against
^[a-zA-Z_][a-zA-Z0-9_]{0,63}$onCollection::new(name)construction before reaching the INSERT. - Prefer the validation approach because collection names are the table identifier, not a value.
- Add unit tests for invalid names.
- Read
- Verification:
cargo test -p vox-db collection::passes; an attemptedCollection::new("'; DROP TABLE")returnsErr(InvalidCollectionName).
M-28 · Replace fuzzy legacy-schema heuristics with digest check
Section titled “M-28 · Replace fuzzy legacy-schema heuristics with digest check”- Owner: Data Core.
- Blast radius: L.
- Blockers: M-20.
- SSOT findings: F8.
- Sub-steps:
- Read
crates/vox-db/src/codex_legacy.rs:50-51to understand the current fuzzy checks. - Replace
is_legacy_schema_chainwith a comparison against the historical baseline digests committed incontracts/db/baseline-version-history.yaml(new file, part of this ticket). - On an unknown DB, fail fast with a clear error.
- Read
- Verification: unit tests for each historical baseline digest.
M-29-docs · Document Turso-specific workarounds module-level
Section titled “M-29-docs · Document Turso-specific workarounds module-level”- Owner: Data Core.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F9.
- Sub-steps:
- Add a module-level doc-comment at the top of
crates/vox-db/src/store/ops_retention.rsexplaining why the code does row-by-row deletion. - Link to the Turso upstream issue if one exists.
- Add a TODO with a sunset condition: “remove when Turso batch DELETE is supported”.
- Add a module-level doc-comment at the top of
- Verification: doc-comment present;
cargo doc -p vox-dbrenders it.
M-29-spike · Pick Tier C CAS owner
Section titled “M-29-spike · Pick Tier C CAS owner”- Owner: Data Core.
- Blast radius: L (spike).
- Blockers: none.
- SSOT findings: F62, Open Question #3.
- Sub-steps:
- Write a 1-page decision memo at
docs/src/architecture/decisions/010-tier-c-cas-owner.md. - Options: (a)
vox-db::cassubmodule, (b) newvox-cascrate, (c) re-purposevox-bounded-fs. - Criteria: (i) dependency fan-out, (ii) whether CAS needs Tier A row coupling, (iii) whether CAS needs to live on Populi nodes (no libSQL).
- Recommendation default (absent counter-evidence): (a) — extend vox-db.
- Update SSOT Open Question #3 with the decision.
- Write a 1-page decision memo at
- Verification: decision memo exists and is linked from SSOT.
M-30 · Sunset TURSO_* env vars
Section titled “M-30 · Sunset TURSO_* env vars”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-20.
- SSOT findings: F10.
- Sub-steps:
- Inventory: grep
crates/for literals enumerated under Turso compatibility aliases incontracts/config/env-vars.v1.yaml/ env-vars SSOT. - In
crates/vox-secrets/src/backend/vox_vault.rsandlib.rs, add a deprecation warning branch that logs once when aTURSO_*var is used and routes it into the canonicalVOX_DB_URL/VOX_DB_TOKEN. - Add the deprecation list to
contracts/config/env-vars.v1.yaml(requires M-13). - Plan sunset for release 0.6 (6 months).
- Add guard sub-check
retired-env-varthat warns invox-secrets(sunset window), fails elsewhere.
- Inventory: grep
- Verification: guard green; CI passes; a test with a legacy Turso URL env var set produces the deprecation warning.
Phase 3 — Tier B / C rollout
Section titled “Phase 3 — Tier B / C rollout”M-31 · dist/schemas.ts provenance spike
Section titled “M-31 · dist/schemas.ts provenance spike”- Owner: Frontend.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F17, F67, Open Question #9.
- Sub-steps:
- Identify whether
dist/schemas.tsis source or build output. Checkpackage.jsonbuild scripts,dist/.gitignore,docs/src/reference/frontend.mdif it exists. - If source: move to
apps/interop/marquee_app/src/schemas.tsor similar, and commit decision. - If build output: move out of
dist/, add to.gitignore, and write a regeneration task. CI check:dist/schemas.tsbyte-identical to regeneration. - Author decision memo at
docs/src/architecture/decisions/011-dist-schemas-ts-provenance.md.
- Identify whether
- Verification: decision memo committed; follow-up ticket filed.
M-32 · Inventory JSONL / append-file call sites
Section titled “M-32 · Inventory JSONL / append-file call sites”- Owner: Data Core.
- Blast radius: L (inventory).
- Blockers: M-03.
- SSOT findings: F22.
- Sub-steps:
rg '\.jsonl\b|OpenOptions::new\(\)[^;]*\.append\(\s*true' crates/ --type rust -l— produce file list.- For each match, annotate in a table:
path,crate,current writer,is_test,intended_tier,planned_migration_ticket. - Commit the table at
docs/src/architecture/data-storage-jsonl-inventory-2026.md.
- Verification: inventory doc exists; each entry either routed to a migration ticket or explicitly marked “test-only, no migration”.
M-33 · Migrate append-file callers to vox-spool
Section titled “M-33 · Migrate append-file callers to vox-spool”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-03, M-32.
- SSOT findings: F22, F30.
- Sub-steps:
- Using the inventory from M-32, migrate each non-test caller. One crate per PR.
- Each PR: replace direct file open with
vox_spool::jsonl::SpoolWriter::append. - Update tests accordingly.
- After all crates are migrated, flip
append-only-file-opengrep rule to error severity.
- Verification: guard green; inventory doc shows 100% migrated.
M-34 · Repo index cache manifest
Section titled “M-34 · Repo index cache manifest”- Owner: Data Core.
- Blast radius: L.
- Blockers: M-03.
- SSOT findings: F29.
- Sub-steps:
- Inventory
target/dogfood/consumers:rg 'target/dogfood|CANONICAL_TRAIN_DATA_DIR' crates/. - Author
contracts/cache/repo-index-manifest.v1.yamldescribing: each cache dir, owner crate, writer function, TTL, regeneration command. - Update
CANONICAL_TRAIN_DATA_DIRincrates/vox-corpus/src/training/mod.rs:13to resolve throughvox_config::paths::cache_dir().join("dogfood").
- Inventory
- Verification: guard sub-check
cache-manifest-completepasses.
M-35 · target-* sibling directories
Section titled “M-35 · target-* sibling directories”- Owner: CI.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F31.
- Sub-steps:
- Inventory:
ls | rg '^target-[a-z]'— list all siblings. - Create VoxScript
scripts/migrations/2026-phase7-target-cleanup.voxthat removes them (already scaffolded). - Flesh out the script body (currently
vox:skipillustrative): enumerate, confirm no tracked files, rm -rf, update.gitignore. - Add guard sub-check
abandoned-target-dir.
- Inventory:
- Verification:
ls | rg '^target-[a-z]'returns onlytarget/; guard passes.
M-36 · Canonical VOX_SPOOL_DIR env var
Section titled “M-36 · Canonical VOX_SPOOL_DIR env var”- Owner: Config owner.
- Blast radius: L.
- Blockers: M-03, M-13.
- SSOT findings: F45.
- Sub-steps:
- Add
VOX_SPOOL_DIRresolution tocrates/vox-config/src/paths.rs:pub fn spool_dir() -> PathBufwith defaultdata_dir().join("spool"). - In
crates/vox-cli/src/telemetry_spool.rs:13, theVOX_TELEMETRY_SPOOL_DIRlookup is replaced by a call tovox_config::paths::spool_dir(). Keep reading the legacy var for one release with a deprecation warning. - Update
contracts/config/env-vars.v1.yaml(depends on M-13).
- Add
- Verification: a test that sets
VOX_SPOOL_DIRplaces files there; settingVOX_TELEMETRY_SPOOL_DIRstill works but warns.
Phase 4 — Observability unification
Section titled “Phase 4 — Observability unification”M-40 · Consolidate subscriber init
Section titled “M-40 · Consolidate subscriber init”- Owner: Platform.
- Blast radius: M.
- Blockers: M-15.
- SSOT findings: F50, F74.
- Sub-steps:
- Author
docs/src/architecture/telemetry-trust-ssot.md(currently missing per F74). Minimal scope: trust surface, subscriber policy, redaction. - Author
contracts/telemetry/subscriber-policy.v1.yaml— profilesCli,Daemon,Test, each with level + appender config. - In
crates/vox-actor-runtime/src/observability.rs, generalize the existinginit_structured_telemetry()(HEAD L15 area, currently Daemon-shaped) intopub fn init(policy: SubscriberPolicy) -> bool. Keepinit_structured_telemetryas a thininit(SubscriberPolicy::Daemon)wrapper so no current caller breaks. - Refactor
crates/vox-cli-core/src/lib.rs:29::init_tracing_for_clito callvox_actor_runtime::observability::init(SubscriberPolicy::Cli). Final body: two lines (vox_actor_runtime::observability::init(SubscriberPolicy::Cli); ()). - Remove the duplicated
EnvFilter/tracing_subscriber::fmt()setup fromvox-cli-core. After this step,vox-cli-core/Cargo.tomlgainsvox-actor-runtime = { path = "../vox-actor-runtime" }and loses directtracing_subscriberplumbing (it may keep the crate for macro use). - Add guard sub-check
single-subscriber-init:rg 'tracing_subscriber::fmt\(\)|Registry::default\(\)' crates/returns onlycrates/vox-actor-runtime/src/observability.rs+crates/*/tests/**.
- Author
- Verification:
cargo test -p vox-cli-corepasses (existing tracing test).cargo run -p vox-cli --quiet -- --versionandcargo run -p vox-cli --quiet -- ci manifestboth emit identically-shaped JSON trace lines (diffable up to timestamps).- Guard
single-subscriber-initreturnspass.
M-41 · Non-blocking file appender
Section titled “M-41 · Non-blocking file appender”- Owner: Platform.
- Blast radius: L.
- Blockers: M-40.
- SSOT findings: F54.
- Sub-steps:
- Add
tracing-appenderto workspace deps. - In
vox-actor-runtime::observability::init, wire a rolling file appender under$VOX_STATE_DIR/logs/. - Document in
docs/src/reference/logging.md.
- Add
- Verification: logs appear under the configured path; rotation happens at midnight.
M-42 · Structured event macro
Section titled “M-42 · Structured event macro”- Owner: Platform.
- Blast radius: M.
- Blockers: M-40, M-03.
- SSOT findings: F51.
- Sub-steps:
- Add a macro
vox_event!(kind = "foo", field1 = value, …)invox-actor-runtime::events. - The macro emits via
tracing::info!AND appends a JSONL record to the Tier B spool usingvox-spool. - Generated types from
contracts/telemetry/events.v1.yaml(M-15) gate allowedkindvalues at compile time.
- Add a macro
- Verification: a test calls
vox_event!and finds both the trace log and the spool record.
M-43 · Canonical span registry
Section titled “M-43 · Canonical span registry”- Owner: Platform.
- Blast radius: L.
- Blockers: M-15.
- SSOT findings: F52.
- Sub-steps:
- Author
contracts/telemetry/spans.v1.yamllisting all canonical span names. - Generate a
vox_actor_runtime::spans::Nameenum. - Add guard sub-check
span-registry-parity: everyspan!(…, "name", …)in crates must use a variant of the enum.
- Author
- Verification: guard green.
M-44 · RUST_LOG documentation
Section titled “M-44 · RUST_LOG documentation”- Owner: Platform.
- Blast radius: L.
- Blockers: M-40.
- SSOT findings: F55.
- Sub-steps:
- Author
docs/src/reference/logging.md(if not created in M-41). - List per-crate tracing targets.
- Cross-link from the top-level README and from
AGENTS.md.
- Author
- Verification: doc exists.
Phase 5 — Memory hygiene
Section titled “Phase 5 — Memory hygiene”M-50 · Row ↔ DTO ↔ Conv split in vox-db
Section titled “M-50 · Row ↔ DTO ↔ Conv split in vox-db”- Owner: Data Core.
- Blast radius: H.
- Blockers: M-11.
- SSOT findings: F32.
- Sub-steps:
- Under
crates/vox-db/src/store/types/, introduce three sibling modules per domain:row/<domain>.rs,dto/<domain>.rs,conv/<domain>.rs. - For each type currently carrying BOTH libSQL
FromRowlogic andSerialize/Deserialize, split: row variant keeps libSQL, dto variant keeps serde, conv holdsFrom/TryFrompairs. row/modules MUST NOT deriveSerialize/Deserialize— enforced by theserde-on-row-structgrep rule.
- Under
- Verification:
cargo build -p vox-dbpasses; guardrow-wire-separationgreen.
M-51 · Same in vox-orchestrator + vox-orchestrator-types
Section titled “M-51 · Same in vox-orchestrator + vox-orchestrator-types”- Owner: Orchestrator.
- Blast radius: H.
- Blockers: M-50.
- SSOT findings: F33, F41.
- Sub-steps: analogous to M-50; files at
crates/vox-orchestrator/src/types/. - Verification: guard green; existing tests pass.
M-52 · Same in vox-gamify::schema
Section titled “M-52 · Same in vox-gamify::schema”- Owner: Ludus.
- Blast radius: M.
- Blockers: M-50.
- SSOT findings: F34.
- Sub-steps: analogous to M-50; files at
crates/vox-gamify/src/schema/. - Verification: guard green.
M-53 · ObservationReport libsql leak
Section titled “M-53 · ObservationReport libsql leak”- Owner: Platform.
- Blast radius: L.
- Blockers: M-50.
- SSOT findings: F35.
- Sub-steps:
- Identify the file that imports
libsql::ValueintoObservationReport. - Replace with a plain
serde_json::Valueor a domain enum. - Guard rule
row-types-outside-vox-dbprevents regression.
- Identify the file that imports
- Verification:
rg 'libsql::Value' crates/ --type rustreturns onlyvox-db.
M-54 · Shared error primitives
Section titled “M-54 · Shared error primitives”- Owner: Platform.
- Blast radius: M.
- Blockers: none.
- SSOT findings: F36.
- Sub-steps:
- Decide home:
vox-primitives::errorsis the default. - Define a small set of canonical errors:
ResourceNotFound,SchemaMismatch,Timeout,PermissionDenied,ExternalService. - Migrate crates one at a time; preserve
thiserror+sourcechains.
- Decide home:
- Verification:
rg 'pub struct .*Error' crates/— surface narrows over time; CI doesn’t enforce this as a hard gate (too invasive); tracked in the regression doc.
M-55 · Serde rename_all normalization
Section titled “M-55 · Serde rename_all normalization”- Owner: Platform.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F40.
- Sub-steps:
- Grep for
rename_all = "kebab-case"andrename_all = "PascalCase". - Decide case-by-case: change default if internal; leave if external-wire (document with a comment).
- Add guard sub-checks
renameAll-kebabandrenameAll-PascalCase(deny unless allowlisted).
- Grep for
- Verification: guard green.
M-56 · Deny blocking-in-async (with allowlist)
Section titled “M-56 · Deny blocking-in-async (with allowlist)”- Owner: Platform.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F42.
- Sub-steps:
- Update
clippy.tomlat repo root withdisallowed-methods = [{ path = "futures::executor::block_on", … }, { path = "tokio::task::block_in_place", … }]. - Author an allowlist at
clippy-allowlist.tomlOR inline#[allow(clippy::disallowed_methods)]with a justification comment at each of the existing allowed call-sites (crates/vox-db/src/store/ops.rs,crates/vox-db/src/research.rs,crates/vox-orchestrator/src/session/manager/db_io.rs).
- Update
- Verification:
cargo clippy --workspace -- -D clippy::disallowed_methodspasses.
M-57 · XDG VOX_CACHE_DIR / VOX_STATE_DIR / VOX_CONFIG_DIR
Section titled “M-57 · XDG VOX_CACHE_DIR / VOX_STATE_DIR / VOX_CONFIG_DIR”- Owner: Config owner.
- Blast radius: M.
- Blockers: M-13.
- SSOT findings: F43, F47, F48.
- Sub-steps:
- In
crates/vox-config/src/paths.rs, addpub fn cache_dir() -> PathBuf,pub fn state_dir() -> PathBuf,pub fn config_dir() -> PathBufwith XDG defaults. - Update all call sites that use
.vox/cache,.vox/state, or.vox/configto route through these. - Add guard sub-check
hardcoded-vox-subdir:"\.vox/"literal only invox-config.
- In
- Verification: guard green.
M-58 · Env var contract enforcement wired to CI
Section titled “M-58 · Env var contract enforcement wired to CI”- Owner: Config owner.
- Blast radius: L.
- Blockers: M-13, M-14.
- SSOT findings: F47.
- Sub-steps: wire the existing
env-parityguard sub-check into.gitlab-ci.ymlvox-ci-guardsjob as a hard failure. - Verification: CI blocks a synthetic PR that adds an undocumented
VOX_*read.
M-59 · VOX_USER_ID deterministic default
Section titled “M-59 · VOX_USER_ID deterministic default”- Owner: Config owner.
- Blast radius: L.
- Blockers: M-57.
- SSOT findings: F46.
- Sub-steps:
- In
crates/vox-config/src/paths.rs:52, replace the currentVOX_USER_IDdefault with a deterministic value:sha256(hostname + username + repo_root_path)[:16]. - Document the scheme in
docs/src/reference/identity.md.
- In
- Verification: two invocations in the same shell produce the same
VOX_USER_ID.
Phase 6 — Regression gates (hard)
Section titled “Phase 6 — Regression gates (hard)”M-60 · Promote data-storage-guard to hard blocker
Section titled “M-60 · Promote data-storage-guard to hard blocker”- Owner: CI.
- Blast radius: L.
- Blockers: all Phase 0–5.
- SSOT findings: F56.
- Sub-steps:
- In
.gitlab-ci.yml, addvox ci data-storage-guard --fail-on warnto thevox-ci-guardsjob. - In
.github/workflows/ci.yml, add the same call to the check phase. - Remove the
--stubflag that M-01 used.
- In
- Verification: CI fails a synthetic PR that introduces any guarded violation.
M-61 · deny.toml ban on direct Turso imports
Section titled “M-61 · deny.toml ban on direct Turso imports”- Owner: CI.
- Blast radius: L.
- Blockers: M-60.
- SSOT findings: F9.
- Sub-steps:
- Update
deny.tomlwith a[[bans.deny]]entry fortursooutside allowlisted crates. - Add a similar entry for
libsql(the lower-level crate). - Run
cargo deny check banslocally.
- Update
- Verification:
cargo deny check banspasses on a clean tree, fails on a synthetic PR that importstursointovox-actor-runtime.
M-62 · Fuzz targets per wire schema
Section titled “M-62 · Fuzz targets per wire schema”- Owner: Platform.
- Blast radius: L.
- Blockers: M-11, M-50.
- SSOT findings: F59.
- Sub-steps:
- For each generated DTO, add a
cargo fuzztarget that deserializes arbitrary bytes and asserts no panic. - Wire into the nightly workflow
.github/workflows/mutation-nightly.yml(or sibling).
- For each generated DTO, add a
- Verification: nightly runs and catches regressions.
M-63 · strace canary
Section titled “M-63 · strace canary”- Owner: CI.
- Blast radius: L.
- Blockers: M-03, M-60.
- SSOT findings: F61.
- Sub-steps:
- Author a benchmark harness in
crates/vox-db/benches/storage_canary.rs. - CI (Linux only) runs it under
strace -f -e trace=openat -o trace.log, then asserts: no file intrace.logmatches a path outside the allowlisted set (test tempdir,/dev/urandom,/proc/*).
- Author a benchmark harness in
- Verification: CI job
canary-storagegreen; a synthetic PR that opens a stray file fails it.
M-64 · Property / roundtrip tests for Tier A
Section titled “M-64 · Property / roundtrip tests for Tier A”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-50.
- SSOT findings: F58.
- Sub-steps:
- For each row struct in
crates/vox-db/src/store/types/row/, add aproptest-based roundtrip: construct arbitrary values, insert, select, assert equality. - Test the conversion layer: Row → Dto → serialize → deserialize → Dto → Row yields original.
- For each row struct in
- Verification:
cargo test -p vox-db --test roundtrippasses.
M-64b · Golden fixture policy
Section titled “M-64b · Golden fixture policy”- Owner: Platform.
- Blast radius: L.
- Blockers: M-64.
- SSOT findings: F57.
- Sub-steps: author
docs/src/contributors/golden-fixtures.mddeclaring the single-tool choice. Default:insta. - Verification: doc exists; follow-up migration tickets track conversion per-crate.
M-65 · Tests never touch real DB
Section titled “M-65 · Tests never touch real DB”- Owner: Data Core.
- Blast radius: M.
- Blockers: M-26.
- SSOT findings: F60.
- Sub-steps:
- Audit tests under
crates/**/tests/andcrates/**/src/**/tests.rsfor real-filesystem paths. - Require use of
vox-test-harness::TempDbortempfile::tempdir(). - Add guard sub-check
test-db-isolation: any test that opens a path literal not rooted intempdir()fails.
- Audit tests under
- Verification: a synthetic test that opens
.vox/store.dbfails the guard.
M-66 · .vox/ post-merge cleanliness
Section titled “M-66 · .vox/ post-merge cleanliness”- Owner: CI.
- Blast radius: L.
- Blockers: M-09.
- SSOT findings: F28.
- Sub-steps:
- After each
mainmerge, a CI nightly job runsvox doctor --check-dot-vox. - Any unknown subdir or stale file produces a ticket.
- After each
- Verification: nightly runs for a week with no findings.
Phase 7 — Gap cleanup
Section titled “Phase 7 — Gap cleanup”M-67 · Reserved for future fold (if M-29-spike picks vox-db for PM)
Section titled “M-67 · Reserved for future fold (if M-29-spike picks vox-db for PM)”- Owner: Data Core + CLI.
- Blast radius: M.
- Blockers: M-26, M-29-spike.
- SSOT findings: F65.
- Sub-steps: only executes if the M-29-spike decision recommends folding project-local PM state into vox-db as a reusable sub-DB. Otherwise closed with reference to M-26 sufficing.
- Verification: decision memo references this ticket.
M-68 · Frozen Core cross-link
Section titled “M-68 · Frozen Core cross-link”- Owner: Governance.
- Blast radius: L.
- Blockers: M-23.
- SSOT findings: F66.
- Sub-steps:
- In
crates/_frozen.md, add a paragraph requiring data-storage-guard green for any frozen crate PR. - In
contracts/db/data-storage-policy.v1.yaml, list frozen crates underfrozen_core_crates:. - Add guard sub-check
frozen-core-ddl-guard: any diff touchingcrates/<frozen>/src/**/row|domains/*requires aFrozen-Core-Amendment: <token>trailer in the commit.
- In
- Verification: attempted unconfirmed frozen-core DDL PR fails the guard.
M-69 · dist/schemas.ts gate (conditional on M-31 outcome)
Section titled “M-69 · dist/schemas.ts gate (conditional on M-31 outcome)”- Owner: Frontend + Data Core.
- Blast radius: L.
- Blockers: M-31.
- SSOT findings: F17, F67.
- Sub-steps:
- If M-31 resolves “build output”: add
dist-schemas-driftguard sub-check that regenerates and diffs. - If “source”: skip.
- If M-31 resolves “build output”: add
- Verification: per the M-31 decision.
M-70 · Delete target-* cleanup (finalize)
Section titled “M-70 · Delete target-* cleanup (finalize)”- Owner: CI.
- Blast radius: L.
- Blockers: M-35.
- SSOT findings: F31.
- Sub-steps:
- Add
target-*/to.gitignore. - Add guard sub-check
abandoned-target-dir. - Flesh out
scripts/migrations/2026-phase7-target-cleanup.vox.
- Add
- Verification: guard green.
M-71 · Document .jj/ coexistence
Section titled “M-71 · Document .jj/ coexistence”- Owner: Governance.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F68.
- Sub-steps:
- Add
.jj/toignored_paths:incontracts/db/data-storage-policy.v1.yaml. - Add a short note to
AGENTS.md§Archival Protocol siblings.
- Add
- Verification: policy file lists
.jj/; guard ignores.jj/.
M-72 · Grammar SSOT parity + vox-tensor / vox-ml-cli boundary
Section titled “M-72 · Grammar SSOT parity + vox-tensor / vox-ml-cli boundary”- Owner: Platform + Mens.
- Blast radius: M.
- Blockers: none.
- SSOT findings: F69, Open Question #8.
- Sub-steps:
- Add guard sub-check
grammar-ssot-drift: changes totree-sitter-vox/grammar.jsorsrc/grammar.jsonrequire a sibling change totree-sitter-vox/GRAMMAR_SSOT.md. - Author
docs/src/architecture/decisions/012-vox-tensor-vs-vox-ml-cli-boundary.mdresolving Open Question #8.
- Add guard sub-check
- Verification: guard green; decision memo committed.
M-73 · Top-level dir READMEs (patches/examples/apps/interop/marquee_app/tools/infra)
Section titled “M-73 · Top-level dir READMEs (patches/examples/apps/interop/marquee_app/tools/infra)”- Owner: Governance.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F71, F72, F73.
- Sub-steps:
- Author a README.md in each if missing, declaring: tier (if any), whether runtime or build-time, guard coverage.
- Add
patches/toignored_paths:in the policy.
- Verification: each dir has a README.
M-74 · .gitignore hygiene
Section titled “M-74 · .gitignore hygiene”- Owner: CI.
- Blast radius: L.
- Blockers: none.
- SSOT findings: F70.
- Sub-steps:
- Ensure
.gitignore,.gitattributes,rust-toolchain.toml, and allcontracts/**/*.yamlare UTF-8 without BOM. - Add guard sub-check
bom-config-files: fails on any of these starting with0xEF 0xBB 0xBF.
- Ensure
- Verification: a synthetic BOM-adding PR fails.
Phase 7b — Reconciliation with 2026-04-21 orchestration surface (M-75..M-78)
Section titled “Phase 7b — Reconciliation with 2026-04-21 orchestration surface (M-75..M-78)”These four tickets close the findings added in SSOT §I (F75–F78) after reconciling the plan with the 24-hour commit window 411adcac..6249453b.
M-75 · Cross-link contracts/orchestration/ as a first-class contract domain
Section titled “M-75 · Cross-link contracts/orchestration/ as a first-class contract domain”- Owner: Platform + Data Core.
- Blast radius: S.
- Blockers: M-04.
- SSOT findings: F75.
- Sub-steps:
- Update
contracts/index.yaml(if not already present) so every file undercontracts/orchestration/is listed; runvox ci contracts-indexto confirm. - Allowlist
contracts/orchestration/indata-storage-policy.v1.yaml::contract_policy.allowed_formats_by_domainas a mixed-format domain ([yaml, schema.json, json]), mirroring the existingmcpexception. Pattern: the JSON fixtures + YAML catalog + schema sidecar are all legitimate siblings. - Extend the lint spec’s
domain-format-singlerule so the policy-YAML allowlist governs exceptions. Incrates/vox-cli/src/commands/ci/data_storage_guard/checks/domain_format.rs, readallowed_formats_by_domain[<subdir>]before flagging. - Cross-reference
contracts/orchestration/from SSOT §4 Tier A as a seed source for themodel_pricing_catalogandmodel_scoreboardtables. - Add a new sub-check
orchestration-contract-sibling-parity— every.v1.yamlincontracts/orchestration/has a sibling.v1.schema.json; every.v1.schema.jsonhas a referenced YAML or fixture.
- Update
- Verification:
vox ci contracts-indexpasses.vox ci data-storage-guard --check domain-format-single --jsonreturns"status": "pass"withcontracts/orchestration/allowlisted.
M-76 · Normalize contract version-header field name
Section titled “M-76 · Normalize contract version-header field name”- Owner: Platform.
- Blast radius: S–M (touches ~15 files).
- Blockers: M-75.
- SSOT findings: F76.
- Sub-steps:
- Author ADR-style note in
docs/src/architecture/(small, one page):x-vox-versionis the canonical version-header key for all contracts;versionandschema_versionare deprecated aliases with a one-release sunset. - Run inventory:
rg -n '^(version|schema_version|x-vox-version):' contracts/ --type yaml— record the full list in the ADR. - In
crates/vox-cli/src/commands/ci/data_storage_guard/checks/version_header.rs, accept{x-vox-version, version, schema_version}as synonyms during the sunset window; emitSeverity::Warn(notError) for the non-canonical forms. - Script-rename: add
scripts/migrations/2026-q2-normalize-version-header.voxthat rewrites every non-canonical key tox-vox-versionand removes duplicates (the policy YAML has bothversion: 1andx-vox-version: 1— keep the latter). - After script lands, flip the warn to error and drop the synonym allowance.
- Author ADR-style note in
- Verification:
rg -n '^(version|schema_version):' contracts/ --type yamlreturns zero post-script.vox ci data-storage-guard --check version-header-parity --jsonreturns"status": "pass"with no warnings.
M-77 · Provider-secret parity (providers.v1.yaml ↔ vox-secrets)
Section titled “M-77 · Provider-secret parity (providers.v1.yaml ↔ vox-secrets)”- Owner: vox-secrets.
- Blast radius: S.
- Blockers: M-75.
- SSOT findings: F77.
- Sub-steps:
- Read every
providers[].secret_idfromcontracts/orchestration/providers.v1.yaml. - Assert each is registered in
crates/vox-secrets/src/spec/ids.rs(the enum thatff0fdcccextended by 31 lines) and handled bycrates/vox-secrets/src/spec/registry/llm.rs(new inff0fdccc, 165 lines). - Add guard sub-check
provider-secret-parityindata_storage_guard/checks/provider_secret.rs. Implementation follows the same YAML-read + source-grep pattern asdata-ssot-guards::run_scientia_consumption_registry_guard. - Extend existing
vox ci secrets-parityto cross-check in the reverse direction: everySecretIdthat carries thellmtag has exactly one referencing entry inproviders.v1.yaml.
- Read every
- Verification:
- Adding a dummy
- name: FakeProvider, secret_id: "DoesNotExist"toproviders.v1.yamlmakesvox ci data-storage-guard --check provider-secret-parityfail. - Removing a real
secret_idmakesvox ci secrets-parityfail.
- Adding a dummy
M-78 · Retention policy for model_scoreboard / model_pricing_catalog
Section titled “M-78 · Retention policy for model_scoreboard / model_pricing_catalog”- Owner: Data Core + Platform.
- Blast radius: S.
- Blockers: M-75.
- SSOT findings: F78.
- Sub-steps:
- Inspect
contracts/db/retention-policy.yaml— add rules for both new tables. Rollup sources:llm_interactions(existing) →model_pricing_catalog(daily rollup, keep 90d) →model_scoreboard(per-(category, strength, window) aggregate, keep 180d). - Enforce: extend
data-ssot-guards(notdata-storage-guard) with a new file-check: every Tier A table listed in a domain fragment undercrates/vox-db/src/schema/domains/appears inretention-policy.yamlwith either a concretekeep_*rule or an explicitretention: append-only. - Add unit test
crates/vox-db/tests/retention_policy_coverage.rsthat parses the domain SQL, enumeratesCREATE TABLEnames, and asserts each is named in the retention contract. - Backfill: for each existing table (~80 tables), add either a retention rule or
append-onlymarker. This is mechanical but large; the unit test gates it.
- Inspect
- Verification:
cargo test -p vox-db --test retention_policy_coveragepasses.vox ci data-ssot-guardsoutput includesretention-policy coverage OK.
Deferred / side-item tickets
Section titled “Deferred / side-item tickets”- M-55b: benchmark
smol_str::SmolStrinvox-actor-runtimerequest hot path (F38). - M-55c: adopt
SmallVec<[T; N]>forintent_tags, CLIargs, etc. (F39). - M-57d: profile
vox-corpus/vox-ml-cliparsers; adoptbumpaloonly if allocation cost is visible (F43).
Index: finding → migration item
Section titled “Index: finding → migration item”- F1 → M-20
- F2 → M-21
- F3 → M-22, M-23
- F4 → M-24
- F5 → M-25
- F6 → M-26
- F7 → M-27
- F8 → M-28
- F9 → M-29-docs
- F10 → M-30
- F11 → M-10, M-11
- F12 → M-12
- F13 → M-13
- F14 → M-15
- F15 → M-40
- F16 → M-05
- F17 → M-31, M-69
- F18 → M-16
- F19 → M-17
- F20 → M-20
- F21, F22, F30 → M-03, M-32, M-33
- F23, F24, F25 → M-06
- F26 → M-07
- F27 → M-08
- F28 → M-09, M-66
- F29 → M-34
- F31 → M-35, M-70
- F32, F33, F34, F41 → M-50, M-51, M-52
- F35 → M-53
- F36 → M-54
- F37, F38, F39, F43 → policy-only / deferred
- F40 → M-55
- F42 → M-56
- F44, F47, F48 → M-57
- F45 → M-36
- F46 → M-59
- F49 → M-20
- F50 → M-40
- F51 → M-42
- F52 → M-43
- F53 → M-41
- F54 → M-41
- F55 → M-44
- F56 → M-01, M-60
- F57 → M-64b
- F58 → M-64
- F59 → M-62
- F60 → M-65
- F61 → M-63
- F62 → M-29-spike, §5.4
- F63 → §5.3 (no crate created)
- F64 → §5.1 (no crate created)
- F65 → M-03, M-67
- F66 → M-68
- F67 → M-31, M-69
- F68 → M-71
- F69 → M-72
- F70 → M-74
- F71 → M-71, M-73
- F72, F73 → M-73
- F74 → M-40
Ownership legend
Section titled “Ownership legend”- Data Core: owners of
vox-db,vox-jsonschema-utilcodegen module,vox-test-harness. - Platform: owners of
vox-actor-runtime,vox-primitives. - CLI: owners of
vox-cli,vox-cli-core. - Orchestrator: owners of
vox-orchestrator,vox-orchestrator-types. - Mens: owners of
vox-ml-cli,vox-populi,vox-tensor. - Ludus: owners of
vox-gamify. - Config owner: owners of
vox-config. - Frontend: owners of
apps/interop/marquee_app/,dist/,apps/editor/vox-vscode. - Governance: owners of
AGENTS.md,crates/_frozen.md, anddocs/src/architecture/decisions/. - CI: owners of
.gitlab-ci.yml,.github/workflows/, and thevox cisubcommands.
If a team lead isn’t named when a ticket is claimed, the default claimant is the crate’s most recent non-agent com