Skip to content

Gate schema mutation of the operator's store on provenance and consent - #179

Merged
MJohnson459 merged 1 commit into
mainfrom
gate-schema-mutation
Aug 14, 2026
Merged

MJohnson459 merged 1 commit into
mainfrom
gate-schema-mutation

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

Every build migrated any database it opened as a side effect of opening it —
the mechanism behind the #417 incident, where a worktree's cargo run
inherited VORO_DB and applied an unreleased migration to real data. The
operator's store now never migrates without consent.

What changed. Migration 0020 adds a store_meta table, and a protected
marker is written there on any open at the production path — in the file
itself, so the property survives a symlink, a moved data directory, or a
restored copy. A protected store with pending migrations refuses to open:

  • TUI launch prints the pending count and asks y/N on plain stdin, before
    the terminal is taken. Reading the yes back is itself the proof a human is
    present — a headless bare voro (the incident's exact shape) cannot answer
    and is refused.
  • Every CLI verb refuses with a message naming the way out; the message
    explicitly tells an agent to stop and tell the operator.
  • voro migrate is the explicit spelling: asks at a terminal, takes
    --yes from a script, and either consent route is recorded in the journal's
    applied_by ("via voro migrate --yes" / "confirmed at the TUI prompt"), so
    even the override leaves a trace.

A fresh install (schema 0) creates its database silently, and unprotected
stores — dev.db, scratch --db, in-memory — migrate on open exactly as before.
voro-core gained no process I/O: the gate is a pure check inside open, and
all terminal interaction lives in the voro crate's main, before ratatui
initialises.

Deviation from the task body, agreed in-session: the proposed
build-provenance routing (release artifact → silent migrate) was dropped. The
CI stamp would live in a workflow cargo-dist regenerates and could silently
vanish — turning releases into prompting from-source builds for everyone — and
it bought one keypress per release. A release upgrade now costs one y/N at the
next TUI start. Both "worth deciding" questions dissolve with it: main-vs-
released has nothing left to decide, and the dev-store default keeps its job
unchanged. DESIGN.md §5 records the policy and the reversal; the "deferred to
its own change" note is gone. CHANGELOG updated.

Verification. Five new schema_guard tests (refusal leaves the store
untouched and unsnapshotted; consent applies and journals the route; the
marker travels with a renamed file; fresh production store creates silently
and marked; scratch stores migrate silently). Full live smoke against a
schema-19 store built by replaying migrations 1–19: CLI refusal, headless
bare-voro refusal, headless migrate refusal, interactive decline (pty),
interactive accept (pty), --yes, journal trace, snapshot in backups/, and
the nothing-pending report. cargo test --workspace (888 tests) and clippy
-D warnings clean; rebased onto origin/main (no migration-number collision).

Every build migrated any database it opened as a side effect of opening
it, which is how an unreleased migration from a worktree once landed on
real data (#417 made that detectable; this prevents it). The operator's
store now carries a `protected` marker in a `store_meta` table — in the
file, so it survives a symlink, a moved data directory, or a restored
copy — and a protected store with pending migrations never migrates
silently. The TUI asks at launch, on plain stdin before the terminal is
taken, so reading the yes back is itself the proof a human is present;
every CLI verb refuses, naming the new `voro migrate` verb; and
`voro migrate --yes` consents from a script, recorded in the journal's
`applied_by` so even the override leaves a trace. A fresh install still
creates its database with no ceremony, and unprotected stores — dev,
scratch, in-memory — migrate on open exactly as before.

The task proposed routing by build provenance instead, so release
artifacts kept migrating invisibly. Dropped after discussion: the CI
stamp lives in a workflow cargo-dist regenerates and would silently
vanish, and it bought one keypress per release. DESIGN.md §5 records
the policy and the reversal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThKpEECj8gR1xD167gAyKz
@MJohnson459
MJohnson459 merged commit 300acc6 into main Aug 14, 2026
7 checks passed
@MJohnson459
MJohnson459 deleted the gate-schema-mutation branch August 14, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant