fix(typed-ooda): WAL-verified ledger open + gym self-eval status (#4480) - #4513
fix(typed-ooda): WAL-verified ledger open + gym self-eval status (#4480)#4513rysweet wants to merge 8 commits into
Conversation
Automatic checkpoint to preserve work in progress. Tests and implementation saved before refactoring phase.
… branch WAL journal mode is now applied unconditionally at connection open (CapabilityHandler::open), so re-asserting it inside the one-time schema migration branch is dead code. Removing it per design resolution A2 (move WAL to the open-time path, do not duplicate). Verified by the open_applies_wal_journal_mode_on_preexisting_v1_db regression test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review feedback (Philosophy, Zero-BS): the enabled-gym self_eval_state read as 'active' (implying an eval is currently running) when it is derived solely from the !skip_gym flag. Rename to 'enabled' for honest semantics; the real benchmark_scenarios().len() count is unchanged, and 'idle' still marks the skipped case. Updated the corresponding pure-helper test assertion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two integration tests read process-global state instead of their injected fixtures, so they passed in CI but failed on a host running the simard daemon with SIMARD_SCALING=auto: - tests/adaptive_scaling.rs::scaler_current_max_can_override_config built its OodaConfig via ..OodaConfig::default(), which consults SIMARD_SCALING and injects an AIMD scaler that overrode the explicit max_concurrent_actions under test. Force scaler: None (same hermetic pattern already used by the decide.rs unit tests, issue #2732) so the config cap is the sole limit. - tests/status_snapshot.rs::assemble_on_empty_state_root_never_panics_and_degrades asserted the daemon section is absent, but the daemon heartbeat fallback reads dirs::data_local_dir()/simard/daemon_health.json, which is NOT under the state root. A live daemon's heartbeat leaked in. Pin XDG_DATA_HOME to the empty tempdir so the heartbeat resolves to a nonexistent path. Test-only changes; no production behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rysweet
left a comment
There was a problem hiding this comment.
Step 17b — Comprehensive Code Review
Verdict: Approve with minor, non-blocking notes. The core change is correct and well-tested: WAL is now applied unconditionally at every ledger open() (the real defect — pre-existing user_version==1 DBs skipped the migration-only WAL pragma and serialized on a whole-file lock), busy_timeout raised 5s→30s, writers use Immediate txns, and the honest gym self-eval wiring + P1 canary-gate guard are sound.
Verification performed (on PR branch feat/issue-4480-…, NODE_OPTIONS=--max-old-space-size=32768)
- ✅ Targeted tests (6/6):
open_applies_wal_journal_mode_on_preexisting_v1_db,open_sets_busy_timeout_at_least_30s,concurrent_cross_connection_writers_never_hit_database_locked(6 writers × 40 iters, separate connections),assemble_gym_reports_real_scenarios_and_active_when_enabled,assemble_gym_reports_skip_flag_as_live_section,canary_default_gates_include_blocking_unit_test_gate. - ✅
cargo fmt --checkclean;cargo clippy --libclean (no warnings). - ✅ All internal
.mdlinks in the 4 new/changed docs resolve; referenced pages exist. - ✅ Renderer (
render.rs) passesself_eval_statethrough — no hardcoded consumer breaks on the new"enabled"value. - ✅
schema::initializehas exactly one caller (CapabilityHandler::open), so dropping the WAL pragma from the migration branch is safe. - ✅ No TODOs / stubs / swallowed errors introduced; lock errors are surfaced (fail-visible), not swallowed.
Findings
1. [Low] Stale "active" naming after the active→enabled rename.
src/status/render.rs:697 test fixture still sets self_eval_state: "active", and src/status/provider.rs test fn assemble_gym_reports_real_scenarios_and_active_when_enabled (+ its doc comment "an active state") reference the old value while the emitted/asserted value is now "enabled". Not a bug (fixture isn't asserted; renderer is pass-through), but the leftover naming is confusing. Suggest aligning fixture/test-name/comment to enabled.
2. [Low–Med] PR description does not match the actual diff.
The body's Changed files (24) and diff-stat (+849 / -1308, incl. gates.rs -418, overseer/deploy.rs -94, tests_deploy_drift.rs -238, source_prep.rs, operator_cli/mod.rs) do not match git diff main...HEAD (14 files, +822 / -7; those five files show zero changes vs main). Please regenerate the description so reviewers aren't misled about scope. (The canary-gate-convergence.md -380 deletion it lists also never existed on main and is referenced nowhere — no broken links, just stale metadata.)
3. [Low, optional hardening] WAL switch is not verified.
open() uses pragma_update(None,"journal_mode","WAL") but doesn't read back the effective mode. SQLite can silently retain the prior journal mode (e.g., on some network filesystems), which would let the exact crash-loop this fixes persist undetected. Since the how-to doc already instructs operators to check PRAGMA journal_mode manually, this is optional: consider reading the returned mode and emitting a tracing warning if it isn't wal.
Positives
Root-cause fix (not symptom quarantine); strong concurrency regression test; honest assemble_gym wiring to real benchmark_scenarios() count; the canary-gate guard test prevents a future silent gate removal; hermeticity fixes (scaler: None, XDG_DATA_HOME pin) are correct and clearly commented.
No blocking issues. Items 1–3 are follow-ups; 2 (description accuracy) is the most worth addressing before merge.
Step 17c — Security Review (MANDATORY)Verdict: APPROVE — no exploitable security vulnerabilities found. Reviewed the actual 14-file diff ( Checklist evidence
VerdictAssessed against injection, crypto, access-control, credential, data-leak, misconfiguration, auth, integrity, SSRF, supply-chain, and prompt-injection categories. No finding meets the reporting threshold. Nothing to remediate from a security standpoint. |
Step 17d — Philosophy Guardian ReviewVerdict: ✅ COMPLIANT. This PR is a model of the project philosophy: it replaces a stub with real behavior, fixes a real defect with a minimal change, and adds a guard against future BS — with no new abstractions or scope creep. Compliance checklist
Minor, non-blocking observations
No philosophy blockers. The change strengthens Zero-BS compliance (stub → real data, guard against gaming gates) while holding the line on ruthless simplicity. |
F1: Correct stale test fixture in status/render.rs — a skipped gym now reports self_eval_state "idle" (matching provider.rs semantics), replacing the dead "active" value left over from the enabled/idle rename. F3: Verify WAL was actually applied in ledger open(). pragma_update ignores the journal mode SQLite echoes back, so an exotic filesystem that silently refuses WAL would degrade to rollback journaling with no signal. Read the mode back and tracing::warn on mismatch — non-fatal, since rollback fails toward stricter locking, not data loss. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Quality audit of PR #4513 found the new/updated docs overclaimed behavior and tests that do not exist on this branch. This PR only ships the WAL + 30s busy_timeout ledger-open hardening (#4483); the reaper lease-ownership guard (#4467/#4464/#4462/#4500) and decide->act persistence fix (#4468) are delivered separately, and claim_reaper.rs has no lease_owner/lease_generation guard here. Corrections: - typed-ooda-ledger-concurrency.md: drop the unimplemented "bounded backoff" retry claim (terminal_call is single-shot; busy_timeout is the sole retry), remove the reaper lease-ownership Section 4 assertion (reframed as separate, tracked work), scope Status to #4483, and replace the regression-tests table with the three tests actually added (WAL-on-preexisting-v1, busy_timeout>=30s, concurrent-cross-connection-writers). Fix the security note to reference the real CapabilityResult Err path instead of nonexistent tests. - diagnose-typed-ooda-database-locked.md: scope Status to #4483, drop the "same release fixed the reaper races" and bounded-retry claims. - deploy-gate-unit-test-canary.md: attribute the exit-101 root fix to the ledger WAL/busy_timeout change, not to unshipped "reaper races". - gym-self-eval-status.md: report self_eval_state "enabled" (matches code) not the stale "active". - operations/index.md: trim the ledger-concurrency row to the shipped scope. Docs-only; no code behavior change. mkdocs build clean; all referenced links resolve; targeted lib+integration tests green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ready for Final ReviewWorkflow steps completed: requirements, design, implementation, tests, code review, philosophy compliance, cleanup, and quality audit. Ready for merge approval. |
Rename assemble_gym test and its doc comment from the stale 'active' value to 'enabled', matching the value the renderer/provider now emit. Cosmetic only; no behavior change. Closes non-blocking review finding #1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
39a3647 to
b6729f0
Compare
📊 Coverage Summary
Coverage data from CI run. Test files matching |
Summary
Harden the typed-OODA ledger against the
database is lockedcrash-loop andsurface a live gym self-eval state, with supporting docs. WAL journal mode is
now applied and verified on every ledger
open()(not only during theone-time schema migration), the busy-timeout absorbs cross-process contention,
and the status gym section reports
enabled/idleinstead of a deadactivevalue. This branch also folds in exe-mtime transient-failure resilience for the
OODA daemon helpers and the accompanying reference doc.
Issue
Closes #4480
Changed files (20 — matches
git diff main...HEAD)Source (7)
src/typed_ooda/ledger.rs— unconditional WAL atopen()+ single-statement WAL read-back verification (warns if SQLite reports non-WAL), 30s busy-timeout,Immediatewrite txns, concurrency regression testssrc/typed_ooda/schema.rs— drop the now-redundant WAL pragma from the migration branchsrc/status/provider.rs— gymself_eval_state→ real scenario count +enabled/idle(no deadactive)src/status/render.rs— align skipped-gym test fixture toidlesrc/self_relaunch/types.rs— supporting type changessrc/operator_commands_ooda/daemon/helpers.rs— exe-mtime transient-failure resilience (retry/backoff) +io::Error::otherclippy fixsrc/safe_update/tests_orchestrator.rs— test adjustments for the aboveDocs (7)
docs/howto/diagnose-typed-ooda-database-locked.mddocs/reference/typed-ooda-ledger-concurrency.mddocs/reference/deploy-gate-unit-test-canary.mddocs/reference/gym-self-eval-status.mddocs/reference/exe-mtime-transient-failure-resilience.mddocs/operations/index.mddocs/index.mdConfig / build (4)
Cargo.toml,Cargo.lock,mkdocs.yml,.github/hooks/amplihack-hooks.jsonTests (2)
tests/adaptive_scaling.rs,tests/status_snapshot.rsDiff stat (
git diff main...HEAD)Behavior
every
open()so a pre-existing v1 database still gets WAL; a read-back ofPRAGMA journal_mode=WALverifies the effective mode and emits atracingwarning if SQLite silently retained a non-WAL journal.
busy_timeoutraised5s→30s; writers use
Immediatetransactions.assemble_gymreports the realbenchmark_scenarios()count and an honestenabled/idlestate.failures instead of failing the tick.
Review follow-ups (Step 17b)
activenaming — resolved: provider test/comment andrender fixture now use
enabled/idle.regeneration (now matches the 20-file
main...HEADdiff exactly).open()reads back theeffective journal mode and warns on non-WAL.
Verification
6×40 concurrent cross-connection writers).
cargo fmt --checkclean;cargo clippy --release -D warningsclean.