﻿# 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:

1. **`AGENTS.md`** — the pillar rule (§2), the 27 hard invariants (§3), conventions (§6), and how decisions get recorded (§7).
2. **`CLAUDE.md`** — Claude-specific notes. Most relevant: *use plan mode for anything that touches a pillar*, and *the document wins until amended*.
3. **`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:

1. **Capture screen** (default) — today's rows; the primary job is logging a session in one tap
2. **Commitment index** — `&v=index`
3. **Detail page** — `&c=<slug>`: present reading, three-month calendar with a real `role="grid"` contract, day inspector, analysis zone under a window control, retirement

**Serve it:**

```bash
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`→`partial` *dropped* 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: `run` rendered but absent from the Key · `aria-expanded="false"` while the overflow menu is open · `.mini` border at 2.992:1 in paper · six `CSS.escape` calls 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 `periodsIn` and `coverageOver` out 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 --check` on 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 a `focusin`-based fix is unobservable *and so is the bug it fixes*. Any focus invariant must be maintained synchronously, never dependent on an event arriving.
- `:focus` does 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

1. **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 `targetRaw` cancels when periods share an in-scope length). **Run every §17 row against a deliberately wrong implementation before trusting it.**
2. **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.
