feat!: rename validation window to verification phase#213
Conversation
Demo feedback: 'validation window' was misleading — people heard 'window' as a passive time range and missed that the feature is an active gate locking the agent into review-only mode before push. Rename across the domain layer to match the user mental model. This is a breaking change for any external caller of the policy or streaming API. Renames (core crate + migration): * PolicyScope::ValidationWindow → PolicyScope::VerificationPhase * ValidationWindowMode → VerificationPhaseMode * StreamEventType::ValidationWindowStart → ::VerificationPhaseStart * sessions.validation_window_started_at → sessions.verification_phase_started_at * repos.validation_window_mode → repos.verification_phase_mode * policies.scope value 'validation_window' → 'verification_phase' * Agent-policies renderer text rewritten to lead with the gate story (what is forbidden) instead of the time-range story (when measured). Migration 026_rename_validation_window_to_verification_phase.sql performs the column renames, drops + re-adds the relevant CHECK constraints with the new column names, and rewrites policies.scope='validation_window' rows in place. Closes the demo-presenter UX complaint.
Follow-up to the core rename. Wires the new domain types through: * Server API: PUT/GET repo settings JSON now uses verification_phase_mode; check endpoint matches policy scope on 'verification_phase'; SQL files renamed in lockstep with column renames from migration 026. * CLI: `tracevault validation-start` → `tracevault verify-start`. No alias for the old name; existing scripts will need to update. Command module renamed validation_window.rs → verification_phase.rs. * Web Settings: 'Validation Window' section heading and copy rewritten to lead with what the feature DOES (lock the agent into review-only mode before push) rather than what it MEASURES (a time range). New dropdown labels: 'Disabled (no enforcement)', 'Warn (record but allow push)', 'Block the push' — each option now says outcome explicitly. * Web Policies editor: scope dropdown renamed 'Validation' → 'Verification'. * MCP integration: tool description updated to match. * tracevault-server tests updated for new names; same coverage. Breaking changes for external integrators: * CLI scripts calling `tracevault validation-start` must change. * Anything reading the policies/check API for scope values must expect 'verification_phase' instead of 'validation_window'. * Anything reading the repo settings API for validation_window_mode must read verification_phase_mode instead. * StreamEventType wire value 'validation_window_start' is now 'verification_phase_start'.
Updates the project's own CLAUDE.md instructions (tracevault uses its own validation feature against this repo) and the public README to use 'verification phase' / 'tracevault verify-start' consistently. Cargo.lock churn from the v0.16.0 dep updates that landed via release-plz on main — picks up the same lockfile state as PRs #211 and #212.
| out.push_str( | ||
| "A validation window restricts which tools can be called before push, \ | ||
| gating the push on a clean validation run. Before pushing, open a \ | ||
| validation window:\n\n tracevault validation-start\n\n\ | ||
| The window stays open until you push, or until you open a new window. \ | ||
| Opening a new window invalidates the prior one.\n", | ||
| "When you are done changing code and ready to push, you must enter a \ | ||
| **verification phase** by running:\n\n tracevault verify-start\n\n\ | ||
| From that point until the push, you are only allowed to call the tools \ | ||
| listed below. Any other tool call will fail the push.\n\n\ | ||
| The intent: catch agents that pretend to review while still editing. \ | ||
| If you need to make a code change after entering the phase, that is fine — \ | ||
| make the change, then run `tracevault verify-start` again to restart the \ | ||
| phase and rerun the required tools. The most recent `verify-start` is the \ | ||
| only one that counts.\n", | ||
| ); |
There was a problem hiding this comment.
The verification-phase text says unknown tool calls "will fail the push," but this section also renders in Warn mode where pushes are allowed. This contradiction gives incorrect behavior guidance.
Show fix
| out.push_str( | |
| "A validation window restricts which tools can be called before push, \ | |
| gating the push on a clean validation run. Before pushing, open a \ | |
| validation window:\n\n tracevault validation-start\n\n\ | |
| The window stays open until you push, or until you open a new window. \ | |
| Opening a new window invalidates the prior one.\n", | |
| "When you are done changing code and ready to push, you must enter a \ | |
| **verification phase** by running:\n\n tracevault verify-start\n\n\ | |
| From that point until the push, you are only allowed to call the tools \ | |
| listed below. Any other tool call will fail the push.\n\n\ | |
| The intent: catch agents that pretend to review while still editing. \ | |
| If you need to make a code change after entering the phase, that is fine — \ | |
| make the change, then run `tracevault verify-start` again to restart the \ | |
| phase and rerun the required tools. The most recent `verify-start` is the \ | |
| only one that counts.\n", | |
| ); | |
| let consequence = match verification_phase_mode { | |
| VerificationPhaseMode::Block => "Any other tool call will fail the push.", | |
| VerificationPhaseMode::Warn => "Any other tool call will generate a warning.", | |
| VerificationPhaseMode::Disabled => unreachable!(), | |
| }; | |
| out.push_str(&format!( | |
| "When you are done changing code and ready to push, you must enter a \ | |
| **verification phase** by running:\n\n tracevault verify-start\n\n\ | |
| From that point until the push, you are only allowed to call the tools \ | |
| listed below. {consequence}\n\n\ | |
| The intent: catch agents that pretend to review while still editing. \ | |
| If you need to make a code change after entering the phase, that is fine — \ | |
| make the change, then run `tracevault verify-start` again to restart the \ | |
| phase and rerun the required tools. The most recent `verify-start` is the \ | |
| only one that counts.\n", | |
| )); |
Details
✨ AI Reasoning
The updated instruction text is trying to explain behavior inside the new phase. However, it now asserts that any non-allowlisted tool call "will fail the push" regardless of mode. In the same logic, this section is rendered for both blocking and warning configurations, so in warning mode that statement is false. This creates contradictory runtime guidance that can cause agents to act on incorrect assumptions.
Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
The function rename to `get_verification_phase_tool_call_stats` pushed two call sites past the 100-column limit. `cargo fmt --all` rewraps them onto a continuation line. CI's `cargo fmt -- --check` caught this on PR #213.
The rewritten verification-phase intro unconditionally said 'Any other tool call will fail the push.' That sentence is true in Block mode but wrong in Warn mode — the same section renders in both, so warn-mode agents were getting incorrect guidance. Switch on `verification_phase_mode` to emit: * Block: 'Any other tool call will fail the push.' * Warn: 'Any other tool call will be recorded as a warning on the push.' * Disabled: unreachable (gated upstream by has_verification). Two tests lock the per-mode wording in place so this can't drift again. Addresses Aikido review comment on PR #213.
| // never get here because has_verification short-circuits above), so | ||
| // it is safe to switch on those two arms and `unreachable!` Disabled. | ||
| let consequence = match verification_phase_mode { | ||
| VerificationPhaseMode::Block => "Any other tool call will fail the push.", | ||
| VerificationPhaseMode::Warn => { | ||
| "Any other tool call will be recorded as a warning on the push." | ||
| } | ||
| VerificationPhaseMode::Disabled => unreachable!( | ||
| "verification phase section is only rendered when mode is Warn or Block" | ||
| ), |
There was a problem hiding this comment.
The VerificationPhaseMode::Disabled arm containing unreachable! is unreachable due to the has_verification guard. Remove the unreachable arm or simplify the match to only the reachable modes.
Show fix
| // never get here because has_verification short-circuits above), so | |
| // it is safe to switch on those two arms and `unreachable!` Disabled. | |
| let consequence = match verification_phase_mode { | |
| VerificationPhaseMode::Block => "Any other tool call will fail the push.", | |
| VerificationPhaseMode::Warn => { | |
| "Any other tool call will be recorded as a warning on the push." | |
| } | |
| VerificationPhaseMode::Disabled => unreachable!( | |
| "verification phase section is only rendered when mode is Warn or Block" | |
| ), | |
| // never get here because has_verification short-circuits above). | |
| let consequence = match verification_phase_mode { | |
| VerificationPhaseMode::Block => "Any other tool call will fail the push.", | |
| VerificationPhaseMode::Warn => { | |
| "Any other tool call will be recorded as a warning on the push." | |
| } |
Details
✨ AI Reasoning
A match on verification_phase_mode was added that includes a VerificationPhaseMode::Disabled arm which calls unreachable!. Earlier in the function, has_verification is computed to be false when verification_phase_mode is Disabled, and the entire block guarded by if has_verification only runs when mode is Warn or Block. Thus the Disabled arm cannot be reached at runtime given the surrounding logic, making the unreachable! invocation dead code.
Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
There was a problem hiding this comment.
match on an enum must be exhaustive — VerificationPhaseMode has three variants, so removing the Disabled arm breaks compilation. The unreachable!() documents the invariant from has_verification and survives future refactors.
Breaking change. Renames the "validation window" feature to "verification phase" across the entire stack — DB, API, CLI, web, MCP, docs.
Why
Demo feedback: the presenter struggled to explain "validation window" because people heard "window" as a passive time range and missed the actual feature — an active gate that locks the agent into review-only mode before a push. Cleaner naming closes the comprehension gap.
The new term also splits the two concepts the old name conflated: "verification" (what the agent is doing inside) and "phase" (a discrete bounded period the agent must explicitly enter).
Renames
Wire format
PolicyScopevaluevalidation_windowverification_phasevalidation_window_modeverification_phase_modevalidation_window_startverification_phase_starttracevault validation-starttracevault verify-startDatabase (migration
026_rename_validation_window_to_verification_phase.sql)UX (web Settings page)
The Settings page copy was rewritten to lead with the value proposition rather than the mechanism:
Dropdown labels now say outcomes explicitly: "Disabled (no enforcement)", "Warn (record but allow push)", "Block the push".
Agent instructions
The Markdown rendered by
tracevault agent-policies(the doc agents read at session start) was also rewritten. New phrasing leads with the intent ("catch agents that pretend to review while still editing") before listing required/allowed tools.Compatibility
No back-compat aliases. Hard rename, per the design decision made during review of this change:
tracevault validation-startneed to update totracevault verify-start.verification_phaseinstead ofvalidation_window.validation_window_modemust readverification_phase_mode.validation_window_startevent type wire value is gone — clients sending it will get a deserialization error.Migration 026 handles all DB-side renames automatically on next deploy. No data migration needed beyond the column + value renames.
Verification
cargo fmt --checkcargo clippy --workspace --tests --all-targets -- -D warningscargo test --workspacepnpm checkFiles
30 files changed, ~660 insertions / ~200 deletions. Mostly mechanical renames; the substantive changes are:
crates/tracevault-server/migrations/026_*.sql— the rename migration.crates/tracevault-core/src/agent_policies.rs— agent-facing instruction text rewritten.web/src/routes/orgs/[slug]/repos/[id]/settings/+page.svelte— Settings page copy rewritten.crates/tracevault-core/src/policy.rs— enum names + doc comments.