Skip to content

docs(app): record the AUD-16 and AUD-19 decisions (P0-08) - #61

Merged
KassaSana merged 1 commit into
masterfrom
chore/p0-08-record-decisions
Sep 14, 2026
Merged

KassaSana merged 1 commit into
masterfrom
chore/p0-08-record-decisions

Conversation

@KassaSana

Copy link
Copy Markdown
Owner

Closes P0-08, the last remaining code task in Phase 0. Both AUD-16 and AUD-19 were flagged as decisions rather than bugs, so this records the decisions where the next reader will hit them.

AUD-16 — rollback stays user-facing

get_training_deploy_status, set_training_repo_path, and train_from_export all require developer_tools_enabled(). rollback_classifier_model and retry_model_deployment_cleanup do not, and that asymmetry read as an oversight.

It isn't. ADR-0006 scopes developer tooling to producing a model — "training, repo-path configuration, in-app train-from-export, and the CLI copy surface." Recovering from a bad one is the user's half of that line: someone whose classifier was ruined by a deployment must not need SNAPBACK_DEV_TRAINING or a Debug build to get back to a working model. Comments at both bind sites now say so.

AUD-19 — no-session predictions are an intended preview; the health field was wrong

The audit asked whether the live preview is intended or the suppression reason is lying. Tracing it, the field is the part that's wrong — and more narrowly than the ticket suggests.

prediction_suppression_reason has four values, and only two mean no prediction was computed:

Value Actually suppresses? Where
private_mode yes — early return state.cpp:2134
idle yes — AFK freeze bails before predict() state.cpp:2212
no_session no classifier_.predict() runs at state.cpp:2262
none n/a —

Without a session the engine scores normally, sets prediction_dirty_ so the tick emits it, and updates last_prediction_at_ms_. What the missing session costs is persistence — persist() early-returns on an empty session_id (state.cpp:2292).

That's intended: the Now surface previews live scores so the untracked-work nudge has something to react to before the user hits record. So the value is now not_recorded, which describes what actually happens.

Worth noting the old string was nearly unobservable anyway: DiagnosticsCard.tsx:50 renders the reason only when lastPredictionAgeSecs == null, but the no-session path sets that timestamp on every prediction — so the card showed "1.3s old", never "none (no_session)". Surfacing "predicting but not recording" in the UI is a real improvement but it's a product change, not P0-08; left alone deliberately.

Changes

  • Comments at the rollback_classifier_model and retry_model_deployment_cleanup bind sites
  • Comment at the prediction_suppression_reason assignment explaining which values mean what
  • no_session → not_recorded (C++ producer, the useDiagnostics.ts default, two test assertions)
  • New ARCHITECTURE.md subsection under IPC: "Two decisions the command surface encodes"
  • New doctest pinning the preview semantics — predicts with an empty session_id, which is what keeps persist() from writing it
  • P0-08 checked off in ROADMAP.md with the decisions recorded inline

Verification

  • ctest — 647/647 passed, including the new AppState predicts without a session but does not record it
  • npm run test:components — 31 files, 165 tests passed
  • npm run typecheck — exit 0
  • prettier --check on the one changed frontend file — clean (the repo-wide advisory warnings are pre-existing; the format job only gates added files, and this adds none)

Phase 0 now has one item left: P0-09, the manual Windows Release soak.

Both looked like inconsistencies and both are deliberate; this writes
that down at the bind sites, in ARCHITECTURE.md, and in a test.

AUD-16 - rollback stays user-facing. ADR-0006 scopes developer tooling
to producing a model: training, repo-path config, train-from-export, the
CLI copy surface. Recovering from a bad deployed model is the user's
half of that line, so rollback_classifier_model and, for the same
reason, retry_model_deployment_cleanup stay ungated. A user whose
classifier was ruined by a deployment must not need SNAPBACK_DEV_TRAINING
or a Debug build to get back to a working model.

AUD-19 - no-session predictions are an intended live preview, so the
health field was the thing that was wrong. Of its four values only
private_mode and idle actually stop compute_event before predict();
without a session the engine scores normally and only persistence is
skipped, since persist() early-returns on an empty session_id. The value
is now 'not_recorded' rather than 'no_session', which claimed a
suppression that never happened.

647/647 ctest and the frontend suite pass.
Copilot AI lite review requested due to automatic review settings September 14, 2026 01:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The demo diagnostics contract remains inconsistent, with documentation and test follow-ups also unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Records the AUD-16 and AUD-19 decisions for P0-08, clarifying user-facing model recovery and no-session preview diagnostics.

Changes:

  • Documents recovery command access decisions.
  • Renames no_session to not_recorded and adds regression coverage.
  • Updates architecture and roadmap documentation.
File summaries
File Summary and review notes
tests/test_app_state.cpp Adds preview coverage. Nit (2 votes): Assert prediction_history(10) is empty to verify it is not persisted.
src/app/state.cpp Updates prediction status semantics and explanatory comments.
src/app/commands.hpp Documents user-facing recovery commands.
frontend/src/useDiagnostics.ts Updates the diagnostics default. Moderate (1 vote): Update the demo backend, which still returns none after a session ends.
docs/ROADMAP.md Marks P0-08 complete. Nit (3 votes): Update or mark historical the remaining no_session descriptions.
docs/ARCHITECTURE.md Records both decisions and their rationale.
Review details

Suppressed comments (1)

frontend/src/useDiagnostics.ts:17

  • The browser demo is another producer of this diagnostics contract, but frontend/demo/backend.ts:225-237 still hard-codes predictionSuppressionReason: "none" even after activeSessionId becomes null. The real backend now returns not_recorded, so stopping a demo session leaves Diagnostics inconsistent with the documented/API value; update the demo health response too.
    predictionSuppressionReason: "not_recorded",
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/ROADMAP.md
Comment on lines +203 to +204
predictions are a deliberate live preview, so the health field was renamed
`no_session` → `not_recorded` to stop claiming a suppression that never happened.
Comment thread tests/test_app_state.cpp
const auto latest = state->latest_prediction();
REQUIRE(latest.has_value());
// ...but it carries no session, which is what keeps persist() from writing it.
CHECK(latest->session_id.empty());
@KassaSana
KassaSana merged commit d6fb654 into master Sep 14, 2026
16 checks passed
@KassaSana
KassaSana deleted the chore/p0-08-record-decisions branch September 14, 2026 01:57
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.

2 participants