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
4.2 KiB
Spec: admin dashboard facelift — standardize on Tabler (dark mode)
Status: done -- admin portal shipped
Origin. The admin dashboard (admin/frontend/index.html) is a hand-rolled,
single-file dark theme — custom CSS variables, a monospace terminal
aesthetic, and vanilla-JS panels with no component library. The ask is to
replace that visual language with Tabler
— a fully open-source (MIT) Bootstrap 5 admin template with dark mode built
into the core product, not gated behind a paid tier — while keeping every
existing behavior intact. (An earlier pass at this same ask targeted
Themesberg's Volt Dashboard; switched to Tabler specifically because Volt's
dark theme may be Pro-only, a risk Tabler doesn't carry.)
Outcome (post-lift)
Spec became reality in commits 23b74a3–906c9e6. The portal is now four
pages with a consistent liquid-glass dark theme. The consolidated design
system is captured in **
Folders/files now in the admin portal
admin.py(~800 LOC) — FastAPI sub-router with API + static HTMLadmin_schema.sqladmin/frontend/index.html— dashboardadmin/frontend/models.html— model availabilityadmin/frontend/decisions.html— decision logadmin/frontend/controls.html— triggers, knobs, persisted config
Selected architecture decisions
- CDN for Tabler + Chart.js + Google Fonts, everything else inline. The
pages still contain their own
<style>and<script>blocks — the original "one self-contained file" architecture from F12 was relaxed, but no static mount was added. Tabler core CSS/JS, Chart.js, andQuicksandload from jsDelivr/fonts.googleapis.com. - Inline SVG icon helper. No Tabler Icons webfont; each page carries a
minimal
icon(name, size)dictionary to avoid an extra font dependency and potential FOUT/CSP issues.
What survived from the original guardrails
- API surface unchanged.
escapeHtml()applied to all interpolated text.- Chart instances reused where Chart.js is used.
saveAllConfig()posts sequential requests.- SSE status indicator reflects real connection state.
What was added beyond the spec
- Atomic config writes with threading.Lock +
.tmp+os.replace(admin.py) plus concurrent-write regression test. - Four-page navigation shell instead of a single-page dashboard.
- Warnings moved from a card into a navbar bell.
- Quota meter moved from a card into a header chip + modal.
- Verdict mix converted from a Chart.js doughnut to pure HTML/CSS partition bars (avoids canvas lifecycle issues entirely).
- History split from one chart into five per-metric mini charts.
Review / process gaps identified from the commit series
- Chart-instance assignment bug.
renderVerdictinitially failed to assign the created Chart instance back toverdictChart, causing repeated "Canvas is already in use" errors every 30-second poll. The QA evidence console log contained the error but was missed by the approving pass. Lesson: read saved console logs, not just screenshots. - Commit granularity. The 8-todo plan specified one commit per todo; the real work coalesced into fewer larger commits. Lesson: granular commits make review and bisection easier.
- Service restart gap. The atomic-write fix landed on disk but
llm-router.servicewas not restarted, so the running process still used the old code. Lesson: after a fix that protects the running service, restart explicitly. - Null numeric config fields.
objective.max_energy_per_requestisnullinconfig.yaml; the frontend sends empty string, whichOptional[float]rejects. Not data-destructive, but it produces spurious failure toasts on "Save All".
Suggested future work
- Extract the repeated cross-page CSS into
admin/frontend/admin.cssto prevent drift across the four pages. - Extract the inline
icon()helper into a sharedadmin/frontend/icons.jsmodule. - Null-aware config editor (empty string ↔ null round-trips cleanly).
- Wire
admin_schema.sqlRBAC/audit tables behind authentication. - Better feedback in
models.htmlwhen the override dropdown is disabled because a model is already overridden.
Original spec below preserved for context.