Skip to content

fix(typed-ooda): WAL-verified ledger open + gym self-eval status (#4480) - #4513

Open
rysweet wants to merge 8 commits into
mainfrom
feat/issue-4480-nodeoptions-max-old-space-size32768-saved-preferen
Open

fix(typed-ooda): WAL-verified ledger open + gym self-eval status (#4480)#4513
rysweet wants to merge 8 commits into
mainfrom
feat/issue-4480-nodeoptions-max-old-space-size32768-saved-preferen

Conversation

@rysweet

@rysweet rysweet commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Harden the typed-OODA ledger against the database is locked crash-loop and
surface a live gym self-eval state, with supporting docs. WAL journal mode is
now applied and verified on every ledger open() (not only during the
one-time schema migration), the busy-timeout absorbs cross-process contention,
and the status gym section reports enabled/idle instead of a dead active
value. 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 at open() + single-statement WAL read-back verification (warns if SQLite reports non-WAL), 30s busy-timeout, Immediate write txns, concurrency regression tests
  • src/typed_ooda/schema.rs — drop the now-redundant WAL pragma from the migration branch
  • src/status/provider.rs — gym self_eval_state → real scenario count + enabled/idle (no dead active)
  • src/status/render.rs — align skipped-gym test fixture to idle
  • src/self_relaunch/types.rs — supporting type changes
  • src/operator_commands_ooda/daemon/helpers.rs — exe-mtime transient-failure resilience (retry/backoff) + io::Error::other clippy fix
  • src/safe_update/tests_orchestrator.rs — test adjustments for the above

Docs (7)

  • docs/howto/diagnose-typed-ooda-database-locked.md
  • docs/reference/typed-ooda-ledger-concurrency.md
  • docs/reference/deploy-gate-unit-test-canary.md
  • docs/reference/gym-self-eval-status.md
  • docs/reference/exe-mtime-transient-failure-resilience.md
  • docs/operations/index.md
  • docs/index.md

Config / build (4)

  • Cargo.toml, Cargo.lock, mkdocs.yml, .github/hooks/amplihack-hooks.json

Tests (2)

  • tests/adaptive_scaling.rs, tests/status_snapshot.rs

Diff stat (git diff main...HEAD)

 .github/hooks/amplihack-hooks.json                 |  12 +-
 Cargo.lock                                         |   2 +-
 Cargo.toml                                         |   2 +-
 docs/howto/diagnose-typed-ooda-database-locked.md  | 108 ++++++
 docs/index.md                                      |   2 +
 docs/operations/index.md                           |   9 +
 docs/reference/deploy-gate-unit-test-canary.md     | 151 +++++++++
 .../exe-mtime-transient-failure-resilience.md      | 364 +++++++++++++++++++++
 docs/reference/gym-self-eval-status.md             | 128 ++++++++
 docs/reference/typed-ooda-ledger-concurrency.md    | 180 ++++++++++
 mkdocs.yml                                         |   4 +
 src/operator_commands_ooda/daemon/helpers.rs       | 227 ++++++++++++-
 src/safe_update/tests_orchestrator.rs              |  14 +-
 src/self_relaunch/types.rs                         |  16 +
 src/status/provider.rs                             |  49 ++-
 src/status/render.rs                               |   2 +-
 src/typed_ooda/ledger.rs                           | 176 +++++++++-
 src/typed_ooda/schema.rs                           |   5 +-
 tests/adaptive_scaling.rs                          |   9 +-
 tests/status_snapshot.rs                           |   7 +
 20 files changed, 1446 insertions(+), 21 deletions(-)

Behavior

Review follow-ups (Step 17b)

  • [1] Stale active naming — resolved: provider test/comment and
    render fixture now use enabled/idle.
  • [2] Description ↔ diff mismatch — resolved by this description
    regeneration (now matches the 20-file main...HEAD diff exactly).
  • [3] WAL switch not verified — resolved: open() reads back the
    effective journal mode and warns on non-WAL.

Verification

  • Ledger concurrency tests pass (WAL-on-preexisting-v1, ≥30s busy-timeout,
    6×40 concurrent cross-connection writers).
  • cargo fmt --check clean; cargo clippy --release -D warnings clean.

rysweet and others added 5 commits July 23, 2026 12:54
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>
…o chang

Implements issue #4480

Changes:
- Implementation as per design specification
- Tests added for new functionality
- Documentation updated

Closes #4480

@rysweet rysweet left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 --check clean; cargo clippy --lib clean (no warnings).
  • ✅ All internal .md links in the 4 new/changed docs resolve; referenced pages exist.
  • ✅ Renderer (render.rs) passes self_eval_state through — no hardcoded consumer breaks on the new "enabled" value.
  • schema::initialize has 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 activeenabled 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.

@rysweet

rysweet commented Jul 23, 2026

Copy link
Copy Markdown
Owner Author

Step 17c — Security Review (MANDATORY)

Verdict: APPROVE — no exploitable security vulnerabilities found.

Reviewed the actual 14-file diff (+822/-7); security-relevant surface is 4 source files (typed_ooda/ledger.rs, typed_ooda/schema.rs, status/provider.rs, self_relaunch/types.rs). Every concern from the security checklist was checked concretely and cleared.

Checklist evidence

  • Injection (SQL/PRAGMA/command/path) — CLEAR

    • ledger.rs pragma_update(None, "journal_mode", "WAL") and the new PRAGMA journal_mode / PRAGMA busy_timeout query_row calls use constant literals only — no untrusted input reaches any PRAGMA/SQL string.
    • The write path exercised by the new concurrency tests, release_engineer_claim, uses a fully parameterized DELETE ... WHERE claim_key = ?1 (bound params![claim_key]) — no string concatenation, no injection via claim/goal identifiers.
    • Connection::open takes a typed Path; no shell invocation or dynamic path construction. Tests use tempfile::tempdir() (per-process, 0700).
  • Sensitive-data handling — CLEAR

    • assemble_gym surfaces only benchmark_scenarios().len() (count of compiled-in curated scenarios) and a static "enabled"/"idle" string. No secrets, tokens, PII, paths, or runtime state exposed.
    • WAL -wal/-shm sidecars inherit the existing ledger dir's ownership/permissions — the main DB already lives there; no new info-disclosure surface, no relocation to a temp/world-readable path.
  • Authentication / authorization — CLEAR (control strengthened, not weakened)

    • The new self_relaunch/types.rs test asserts default_gates() still contains the blocking RelaunchGate::UnitTest canary gate. This guards a security-relevant self-deploy invariant; it does not remove, disable, or make any gate non-blocking.
  • New vulnerabilities / concurrency — none

    • busy_timeout 5s→30s and unconditional WAL are local-file SQLite tuning. The 30s wait is at most an availability/latency (DoS-class) concern, not attacker-triggerable across a trust boundary — contending processes (daemon, engineer worktree, reaper) are all first-party.
    • Journal-mode pragma_update propagates errors via .map_err(persistence)? (not swallowed). It ignores the returned mode string, so on an exotic FS WAL could remain rollback mode — an integrity/robustness edge only, and it fails toward stricter whole-file locking, never weaker isolation. Removing the redundant WAL pragma from schema.rs is safe because open() now asserts WAL on every connection before initialize().
  • Supply chain — CLEAR

    • Cargo.toml/Cargo.lock: internal version bump 0.36.0 → 0.37.0 only. No new dependencies, no third-party version/source changes.

Verdict

Assessed 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.

@rysweet

rysweet commented Jul 23, 2026

Copy link
Copy Markdown
Owner Author

Step 17d — Philosophy Guardian Review

Verdict: ✅ 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

  • Ruthless simplicity — The core defect fix is two lines: raise busy_timeout 5s→30s and apply journal_mode=WAL unconditionally at open(). No config knobs, no retry frameworks, no new types. The gym fix swaps a hardcoded None/"idle" for a real benchmark_scenarios().len() count.
  • Bricks & studs / clean module boundaries — WAL responsibility is consolidated into a single owner (CapabilityHandler::open) and removed from the schema::initialize migration branch. Both sites carry a comment explaining the contract, so the seam is explicit rather than implicit. Changes stay within their modules (typed_ooda, status, self_relaunch).
  • Zero-BS implementation — This is the standout. assemble_gym previously always returned an inert stub (configured_scenarios: None, self_eval_state: "idle"); it now surfaces the REAL scenario count when enabled. The canary guard test fails loudly if a future change drops the blocking UnitTest gate to make self-deploy "pass" — directly codifying "root-cause, don't paper over." No stubs, no faked APIs, no swallowed exceptions (all persistence errors propagate via .map_err(persistence)?).
  • No over-engineering — 30s is a plain constant; WAL is asserted once. No speculative generality. Tests are behavior-specced (pre-existing-v1 WAL, busy_timeout floor, concurrent cross-connection writers) rather than mock-heavy.
  • Honest semanticsself_eval_state value derives from !skip_gym and is named "enabled" (not a fake "active" implying an in-flight eval). The "idle"/skipped path is unchanged. This is truthful state reporting.

Minor, non-blocking observations

  1. [Low] open() doesn't assert that PRAGMA journal_mode actually returned wal (SQLite can silently keep the prior mode if the DB is mid-transaction on another connection). Errors from the pragma statement itself are propagated; a follow-up tracing::warn! on an unexpected return value would fully close the Zero-BS loop. Already noted in the code review.
  2. [Low] The assemble_gym_reports_real_scenarios_and_active_when_enabled test name still says "active" though the asserted value is "enabled" — cosmetic drift, no behavioral impact.

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>
@rysweet rysweet changed the title Update Cargo.toml with 24 changed files (#4480) fix(typed-ooda): WAL-verified ledger open + gym self-eval status (#4480) Jul 23, 2026
@rysweet
rysweet marked this pull request as ready for review July 23, 2026 15:22
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>
@rysweet

rysweet commented Jul 23, 2026

Copy link
Copy Markdown
Owner Author

Ready for Final Review

Workflow 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>
@rysweet
rysweet force-pushed the feat/issue-4480-nodeoptions-max-old-space-size32768-saved-preferen branch from 39a3647 to b6729f0 Compare July 23, 2026 16:58
@github-actions

Copy link
Copy Markdown

📊 Coverage Summary

Generated by cargo llvm-cov --workspace --summary-only (nightly, excluding test files)

Module Lines Covered Coverage
Total 196315 165051 84.1%

Coverage data from CI run. Test files matching tests?/ are excluded from line counts.

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