plans/ held 58 documents and exactly one said whether it was open. The rest
mixed finished work, reviews of shipped work, parked specs and genuinely
pending ones, with nothing distinguishing them, so "how many plans are in
the queue" had no answer short of reading all 58.
Now `grep -H '^Status:' plans/*.md` is the answer:
50 done 3 in progress 2 planned 2 reference 1 parked
Statuses were derived rather than guessed: CLAUDE.md's own built list and
"What's NOT built yet" section, plus checking the subject exists in the
code. A review of work that shipped counts as done -- it records what was
found, it is not a request for anything. `reference` separates the two docs
that are conventions rather than work items (admin-design-standards,
admin-work-framework), which otherwise read as permanently-open plans.
The vocabulary is deliberately five words. A larger one invites "mostly
done" and "blocked-ish", which is how the directory became unreadable.
test_plans_declare_status.py keeps it from rotting: a new plan without a
marker fails, as does an unknown status, one buried below the eighth line,
or an open status with no reason -- "planned" alone is the state that rots,
since nobody can tell later whether it waits on a decision, a dependency,
or just nobody's turn.
Also updates the sweep plan with what landed and what did not, including
that #9 was not a defect.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
5.9 KiB
Review: admin-visual-fixes implementation
Status: done -- review of shipped work
What it was reviewing: the 8-todo admin-visual-fixes plan
(plans/.omo/plans/admin-visual-fixes.md), after its orchestration
reported 8/8 complete and F1-F4 all APPROVE. Verified against the actual
diff and the plan's own saved QA evidence
(plans/.omo/evidence/task-8-admin-visual-fixes-console.txt), not the
orchestration summary.
Verdict: 6 of 8 todos are genuinely fixed; Todo 2 is not, and the plan's own evidence file proves it
Confirmed correct
- Todo 1 (quota bar):
gauge-canvasfully removed,.quota-barmarkup in place. - Todo 3 (history chart): canvas id fixed to
history-chartandhistoryChart = renderChart(...)correctly captures the instance (admin/frontend/index.html:610). - Todo 4:
knobDisplayValue()(index.html:687-696) correctly unwraps{enabled: bool}before display. - Todo 5:
renderWarnings(index.html:538-547) rebuilds the#no-warningsplaceholder inline instead of referencing a detached node — no more stale reference. - Todo 6 (XSS):
task_categoryandmodelStr(selected_model/ selected_provider) are nowescapeHtml()-wrapped in bothrenderDecisionsandaddDecision.rejected_reason/runner_up_modelsare never rendered anywhere in the file, so there was nothing to escape there. - Todo 7 (config race): correct and thorough.
_persist_config_value(admin.py:349-374) takes a module-levelthreading.Lockaround the whole load→validate→backup→write→replace sequence (the right primitive —admin_config_writeis a sync handler, which FastAPI runs viaanyio.to_thread, soasyncio.Lockwould not have serialized anything), validates before backing up, and writes via.tmp+os.replaceso a reader never observes a truncated file.tests/test_admin_config.py::test_config_concurrent_writes_are_atomic_no_zero_byte_backupsis a real regression test — a live reader thread asserts the file is never empty while two writer threads race.saveAllConfig()(index.html:774-804) is now a sequentialfor...of awaitloop, notPromise.all. All 57 admin tests pass.
Not fixed: Todo 2 (verdict doughnut) — and the evidence already proves it
renderVerdict (index.html:508-536) fixed the canvas id
(verdict-canvas → verdict-chart) but never assigns the created chart
back to the verdictChart variable:
if (verdictChart) { updateChart('verdict', labels, values, 'doughnut', colors); }
else { renderChart('verdict-chart', labels, values, 'doughnut', colors); } // return value dropped
verdictChart stays null forever, so every 30-second poll
(REFRESH_MS) takes the else branch again and tries to create a second
Chart.js instance on a canvas that already has one attached. This is
already recorded in the plan's own saved evidence,
task-8-admin-visual-fixes-console.txt:
Uncaught (in promise) Error: Canvas is already in use. Chart with ID '1' must be
destroyed before the canvas with ID 'verdict-chart' can be reused.
at renderChart (http://127.0.0.1:8091/admin/:831:11)
at renderVerdict (http://127.0.0.1:8091/admin/:531:9)
at loadSnapshot (http://127.0.0.1:8091/admin/:332:4)
— repeated 6 times in the log. The doughnut renders once on first load, then throws and stops updating for the rest of the page's life. This directly contradicts F3's "clean console" sign-off and Todo 2's own acceptance criteria ("the verdict doughnut renders"); a single screenshot taken right after page load wouldn't catch it, but the saved console log already had it and nobody read it before approving.
Fix: mirror what historyChart already does correctly two functions
over — capture the return value:
else { verdictChart = renderChart('verdict-chart', labels, values, 'doughnut', colors); }
Also update the dead-code branch at index.html:512
(renderChart('verdict-chart', null, 'verdict') — missing arguments,
never actually reassigns anything either) to just verdictChart = null;
without the pointless re-render call, since there's no data to draw.
Secondary, out-of-scope-but-worth-a-look: objective.max_energy_per_request 422s on every "Save All"
Same evidence file shows two 422 responses for
POST /admin/api/config/objective.max_energy_per_request during the
save-all QA pass. config.yaml has this key as null; the config table's
text input renders it as an empty string, and saveAllConfig's
number-coercion (Number('') → 0, but the val.trim() !== '' guard
correctly keeps it as '' rather than coercing to 0) ends up POSTing an
empty string against an Optional[float] field, which RouterConfig
rejects. Not data-destructive (validation fails before the backup/write
step, exactly as designed) and not part of any of the 8 todos' scope, but
worth a follow-up: null-valued numeric config fields can't currently be
round-tripped through "Save All" without a spurious failure toast.
Process gap, not a code defect
No commits landed for any of the 8 todos despite the plan's explicit "one
commit per todo (8 total)" commit strategy — git log still ends at
b63234a, and admin.py / admin/frontend/index.html remain untracked.
Separately, the live llm-router.service has not restarted since 02:35:45
(same PID throughout), so the Todo 7 fix — the one that matters for
production — is sitting on disk but not yet protecting the running
service.
Recommendation
Fix the one-line verdictChart assignment (plus the dead branch at
:512), re-run the same Playwright QA against a fresh page load and at
least one 30s+ idle period to confirm the console actually stays clean
across a poll cycle this time — a single screenshot won't catch this class
of bug again. Then commit per the plan's own commit strategy. Restarting
llm-router.service to pick up the Todo 7 fix is a separate, explicit step
to take afterward, not bundled into the same action.