# agenda — session handoff
Date: 2026-08-14 · Repo: /Users/yachintyadav/Workspace/agenda · Branch: design (10 commits ahead of main, nothing pushed) · Working tree: clean
0. Read this first
Three files govern how you work here, and they outrank anything in this handoff:
AGENTS.md— the pillar rule (§2), the 27 hard invariants (§3), conventions (§6), and how decisions get recorded (§7).CLAUDE.md— Claude-specific notes. Most relevant: use plan mode for anything that touches a pillar, and the document wins until amended.MEMORY.md— the dated decision journal, D1–D59, with a Direction section at the bottom that states the current phase and the immediate order of work. Start there.
The project's own handoff mechanism is MEMORY.md's Direction section. It is current as of this commit. This document is a narrative layer over it, not a replacement.
1. What agenda is
A personal, single-user, self-hosted daily-practice tracker (PWA on a VPS behind Traefik). It exists because Habitica destroys task history when you miss a day — it is built on jeopardy (loss must be real) while being asked to serve as a record (nothing may ever be lost). Those purposes are incompatible.
agenda resolves that by separating two layers:
- The ledger — append-only, immutable, enforced by database triggers. Facts about what happened.
- The scoring layer — pure functions computed at read time. Opinionated, swappable, retroactively recomputable.
The product never scolds, never congratulates for streaks, and never implies a missed day is a character flaw. That is not decoration; it is enforced by invariants (I11 no streak summaries, I12 no punishment/HP/badges, I27 no hue may mean good or bad) and it is the thing to protect in every design decision.
2. Current phase
Phase 0 — pillars ratified and amended, no application code yet.
The repository contains no packages/, apps/, or schema.sql. What exists is four pillar documents, three agent files, and one very large reference prototype.
The four pillars (source of truth, ahead of code)
| Document | Governs | Amendments |
|---|---|---|
docs/PRD.md |
What agenda is, ratified scenarios, non-goals | A2, A3 |
docs/Architecture.md |
Stack, schema, reducers, API, deployment | A2, A3, A4, A5 |
docs/Metrics.md |
Every formula, rationale, worked examples, fixtures | A2, A3, A4, A5, A6 (new this session) |
DESIGN.md |
The visual system and the named rules | A1–A17 |
The amendment protocol is mandatory. If a change contradicts a pillar: stop, say so, and propose an amendment. An amendment = edit the pillar body so it reads as current truth + add a row to that document's Amendment Log + add a dated entry to MEMORY.md. All three, in the same commit.
The prototype
docs/ui/capture-prototype.html — a single file, ~3,100 lines, HTML+CSS+JS, no build step. It is the reference of record for the visual system and carries three surfaces:
- Capture screen (default) — today's rows; the primary job is logging a session in one tap
- Commitment index —
&v=index - Detail page —
&c=<slug>: present reading, three-month calendar with a realrole="grid"contract, day inspector, analysis zone under a window control, retirement
Serve it:
cd docs/ui && python3 -m http.server 8142
# http://127.0.0.1:8142/capture-prototype.html
A server was already running on 8142 (PID 3611) at handoff time; it may not survive. Do not commit anything into docs/ui/ as a temp file.
URL parameters: ?w=390 simulated width (container queries at 353/620/900) · &n=8 commitment count · &c=<slug> detail page · &v=index · ?day=YYYY-MM-DD overrides today. A floating Prototype controls panel carries dev overrides (coverage, warm-up, sync state).
3. What this session did
Ten commits on design. Grouped by theme:
The review surface (built and specified)
Index, detail page, calendar, day inspector, and retirement were shaped, ratified, and built into the prototype. One terminal state (retired) with a required note. Pillar amendments across DESIGN.md (A12–A17), Architecture.md (A5), and Metrics.md (A4).
Settlement correctness
Three defects found and fixed, verified over roughly 2.7M week-evaluations:
- Slot assignment in date order violated I22 — upgrading
skipped→partialdropped momentum. Fixed with surplus ranking (§5.4). - Momentum seeded at first observation — 39,916 property violations. Fixed to
s₀ = 1.0. - I20 violated in open weeks — logging a partial Monday cost more than logging nothing. Fixed by counted-side certainty (Metrics A5).
Coverage (last thing before the critique)
The prototype was computing coverage as day-based (inScopeDays − blocked) ÷ inScopeDays. Replaced with §7.1's obligation terms, which never reuse targetScaled (I24). Recorded as Metrics A6 with a new §7.1.1.
The amendment exists because implementing the formula forced a placement question the pillars disagreed about: §6.8 makes coverage a mandatory companion to momentum, §3.1 makes momentum window-independent and coverage window-governed. Resolved as one formula, two named spans — a fixed 28-day confidence statement beside momentum, and a windowed analysis reading.
Accessibility and focus
Escape focus restore on all five sheets; the roving tab stop now follows focus out of a dialog. Both of the last two defects here were found by the user pressing a key, not by any harness.
4. Where to resume — in this order
The user chose this order explicitly at the end of the session. Full detail in MEMORY.md D59 and the critique snapshot at .impeccable/critique/2026-08-14T09-45-26Z__docs-ui-capture-prototype-html.md.
(a) Three focus/motion defects — /impeccable harden
Causes are already located in source; these are not investigations.
| Sev | Defect | Cause |
|---|---|---|
| P0 | Answering the escalation question (Pause / Keep it scheduled) drops focus to <body> and announces nothing |
captureFocus() (line ~1665) builds button[data-dec="…"][data-slug="…"] — a selector that by definition cannot survive the action that removes the decision block. The restore is root.querySelector(sel)?.focus(); ?. no-ops in silence. Fix: fall back to the owning row, and add announce() to the [data-dec] branch. |
| P1 | Today's calendar cell — the default keyboard landing spot — shows no focus ring | .cell[data-today="true"]{outline:1px solid var(--c)} (line ~563, specificity 0,2,0) beats the global :focus-visible{outline:2px solid var(--amber)} (line ~83, 0,1,0). Recurrence of a bug the file already fixed for .f input — see the comment at line ~413. One selector fixes it. |
| P1 | The tapped row teleports 592–965px, 1.5s later, focus off-screen; duplicate group headers | holdOrder()'s 1500ms freeze defers the re-sort rather than removing the hazard; restore uses preventScroll:true (line ~1678). Logging a second row inside the freeze produces two LOGGED TODAY headers with NOT LOGGED TODAY between them. |
(b) Two pillar questions — /impeccable shape, in plan mode
Both are contract changes, so CLAUDE.md requires plan mode and approval before code.
1 — Coverage cannot soften a bad fortnight. Coverage guards against a thin record, not a bad stretch. After fourteen missed days, missed is covered (§7.1, correctly — not doing it is a real observation), so coverage stays 95–100% and the Confidence Rule cannot fire. Momentum reads 0.24 at full chroma and full authority: yes, trust this, and it is 0.24. The worst-feeling screen in the product is the one with nothing qualifying it — on the exact scenario agenda exists to survive.
Either that is right and the interface needs another way to say it, or coverage is not the only confidence term. This is upstream of the deferred absence-screen fix — settling the principle decides what that screen should say, which is why it comes first.
2 — The calendar's empty cell means four things. Before start, during a pause, after retirement, and not yet. The Key teaches exactly one meaning and DESIGN.md says it "means nothing else". Every future day currently announces "no obligation" on an active commitment. Either future earns a mark of its own the way missed did, or the stated single meaning is wrong.
Sequencing note given to the user: take the pillar questions before the type pass, because the calendar question may add a mark to the cell, which changes what the numerals sit beside.
(c) The 8px type step — /impeccable typeset
Raise .cn (calendar date numerals) and .cw (weekday initials) from 8px to the 11.5px step — three declarations, 65 visible instances, cells are 49×44 with room. .dlab (field axis labels) is the same declaration family; decide it in the same pass.
Deferred by the user's scope choice (recorded, not lost)
- [P1] The absence screen offers 20 obligations and no exit after 14 days away — blocked on pillar question 1
- [P2] The index is a right-aligned column of eight bare decimals, with no trace and no day composition
- Minor:
runrendered but absent from the Key ·aria-expanded="false"while the overflow menu is open ·.miniborder at 2.992:1 in paper · sixCSS.escapecalls inside quoted attribute selectors · the HSL hue ramp clusters five commitments in a 40° green band at n=20 (OKLCH would recover it)
After the prototype: phase 1–2
pnpm workspace scaffold, schema.sql with the immutability triggers, migrations, and packages/core with reducers and metrics — built from docs/Metrics.md against the §17 fixture table, not from the prototype.
The retire_needs_note CHECK must land in schema.sql at creation — I19 makes it unaddable afterwards, because the twelve-step rebuild SQLite needs to add a CHECK destroys the immutability triggers.
5. Method notes that will save you time
What works
- Extract functions from the artifact; never retype them. The fixture harness pulls
periodsInandcoverageOverout of the HTML by brace-matching and stubs only leaf dependencies. Testing a re-implementation is how the momentum seeding defect survived. - Property sweeps over exhaustive week shapes caught all three settlement defects. Single fixtures caught none of them.
node --checkon each extracted<script>block after every edit. This file has twice acquired${/* … */""}template comments written into plain JS, and once a literal NUL byte.- The design detector:
node ~/.claude/skills/impeccable/scripts/detect.mjs docs/ui/capture-prototype.html(currently 0 anti-patterns, 1 advisory false positive).
Harness limits — real, and they produced contradictory readings
- CDP cannot deliver Escape to a modal
<dialog>. Tab and arrows work; Escape produces zero keydown events in the page. .focus()does not fire focus events when the document lacks OS focus. So afocusin-based fix is unobservable and so is the bug it fixes. Any focus invariant must be maintained synchronously, never dependent on an event arriving.:focusdoes not match under some CDP conditions.- When the instrument cannot reach the case, the case is not verified — it is someone's five seconds. Two defects this session were found by the user pressing a key after four automated attempts had contradicted each other.
Two recurring failure patterns worth internalising
- Fixtures that cannot fail. Three separate defects survived a passing test because the test could not vary: the momentum seed (D47), settled-period-only momentum (D50), and the entire coverage fixture set (D58 — five fixtures all satisfied by the very formula §7.2 rejects, because
targetRawcancels when periods share an in-scope length). Run every §17 row against a deliberately wrong implementation before trusting it. - The Preserved Focus Rule has failed six distinct ways in this one file. The fifth and sixth were found after the rule was written down. Documenting a behaviour reliably produces the feeling of having handled it.
6. Commit log for this session
efef346 docs: record the fourth UX critique and the work it ordered (D59)
414813e fix(ui): coverage computed from obligations, not days
768fa7d docs: coverage gets its span named (Metrics A6)
f9a8523 fix(ui): the roving tab stop follows focus out of a dialog
a5fdbab docs: confirm the Escape focus path end to end (D57)
e240bb4 chore(impeccable): design sidecar and critique snapshots
f353800 docs: pillar amendments for the review surface and settlement
8090141 feat(ui): the review surface — index, detail page, calendar, day inspector, retirement
a31290d docs: add DESIGN.md as the fourth pillar, and PRODUCT.md
4d2a544 chore: ignore machine-local agent hooks and Impeccable caches
Branch design. Nothing has been pushed — AGENTS.md §8 says do not commit or push unless asked, and only committing was asked for.
7. Design health at handoff
Fourth UX critique (dual-agent, isolated): 32/40 — Good. Trend 23 → 29 → 27 → 32.
Measured clean: 0 text-contrast failures across 220+183 elements × both renditions · field bars and calendar marks floor at 4.82:1 (ink) / 3.99:1 (paper) across all 20 channel hues · 0 horizontal overflow in 24/24 width×surface combinations · 0 unnamed interactive nodes · 0 heading skips · 0 console errors · 0 layout-triggering transitions · roving-tabindex grid contract verified [1,1,1].
Strongest thing in the product, and the thing to protect: the Confidence Rule renders as written. At cov 35% the row's --c moves hsl(H 72% 68%) → hsl(H 42% 65%) and the momentum numeral drains from a hard boundary. Colour is removed to signal doubt, never added to signal fault. It is the rare case of an ethical claim surviving contact with CSS.