# easel — diary

> Session summaries. Interim: appended manually; post-T2.3 auto-updated by
> `/checkpoint` / SessionEnd hook. Not authoritative — `handoff.md` is.

## 2026-09-03 (Adi) v0.46 to v0.53: variations made usable, and six bugs found by using it
- rian's ask "easel still does not have the variations feature" was fair from
  the outside: it shipped the day before, nothing used it, and the way in was
  a small button inside a folded section. Today made it real, and league is
  using it.
- Shipped in order: the board notices an unwired look (0.46); the way in is a
  button on the option row (0.46.1); setup by naming the looks, and you can
  upload over a design that already exists (0.47); the blank-board hooks fix
  (0.47.1); honest empty states (0.47.2); the switch into the app bar (0.48);
  the em-dash sweep of everything a client reads (0.49.x); reorder plus a demo
  click that can open a CSS menu (0.50); the area-drag jump fix (0.50.1); the
  pack Menu keeping itself inside the window (0.51); bare-hash links, the
  synthetic Escape ending the walkthrough, Escape precedence (0.51.x); move a
  point by typing its position (0.52); the demo click waits while the point is
  read (0.53).
- 253 tests green. Migration `a1c9f3e7d5b2` applied to a scratch database
  before deploying.
- League's three homepage builds were edited IN PLACE to carry the hive
  variation, so the default rendering is byte for byte what it was and no note
  or point lost its anchor.
- Every bug today came from Adi using the app, not from a test. Three of them
  were mine from earlier the same day.

## 2026-09-02 (checkpoint) v0.35 to v0.45.2 in one day, and standards review 2
- Shipped, in order: the whole tour in the panel with comments on points
  (0.35); attachments per the Interaction Standard addendum (0.36 to 0.36.2);
  panel offset, footer exit, Escape across the frame (0.36.3 to 0.36.5); the
  board as the project's journey with stages and Feedback complete (0.38);
  tool tour folded into Start, greeting by name, scope copy (0.38.1); one
  Discussion mode and the cleaned-up bar (0.39 to 0.39.2); area notes, draft
  marker, click-to-locate (0.40); move/resize markers, card click to canvas,
  blurred surrounds (0.41 to 0.41.1); re-file conversations and breadcrumb
  labels (0.42); the tool introduction as a panel walkthrough (0.43); inline
  conversations, points as roots, lightbox, the introduction on the board
  (0.44 to 0.45.2).
- Verified each in the browser through the MCP tab (JS-driven); learned that
  the tab is hidden: rAF, smooth scroll and CSS transitions do not run there.
- Backend: 239 tests green. Adi committed v0.37.x in the same tree mid-day.
- Docs: `caddie-standards-review-2.md`, pass 1 pointer, pack README, caddie
  runbook §5 entry. rian added the no-em-dash rule to agents.md at the end.
- Note for whoever runs `/checkpoint`: it is a built-in CLI command, run by
  the person, not by the assistant; this entry is the human half of it.

## 2026-09-02 — v0.38.0: the board as the project's journey
- Read: caddie 01-vision §4–5 (stages, client timeline), leaguelaw proposal +
  design-direction.md, the old board (ProjectOverview), models/rollup.
- Backend: ProjectStage model + migration b8d4f2a6c1e3, services/stages.py
  (ROADMAP, stages_of, set_status), feedback_completed_at/by + rollup rule,
  option threads, Option.blurb/thumbnail_name, routers/board.py (7 routes),
  ProjectDetail.stages + OptionOut fields; tests/test_board.py (6). 235 green.
- Frontend: pack Stages + Prose (+css, README); StageView.tsx; InlineThread;
  ProjectOverview rebuilt; Viewer finish → board; hooks; easel.css journey block.
- League: thumbnails via google-chrome --headless from reconstructed bundles
  (cropped 1380×700), seeded with stage bodies + blurbs by a one-off script
  in the container.
- Concurrency: Adi committed v0.37.0/0.37.1 mid-build; two of my files rode
  in his commit. Version bumped to 0.38.0 on top.

## 2026-09-02 — v0.36.0: attachments (paste-a-screenshot) per the Interaction Standard addendum
- Found: the feature was standardised 2026-08-22 (punchlist reference); easel
  had only a per-comment stub; pack Composer had no paste path.
- Backend: `services/attachments.py`, two project-scoped routes in
  `routers/projects.py`, `AttachmentOut`; stub routes + test removed;
  `tests/test_attachments.py` (roundtrip, type/content discipline,
  visibility + traversal). Suite green.
- Pack: `Body.tsx` (renderBody/isOwnUrl/ownImages), Composer attach path,
  CSS (chip strip, clip button, bounded thumbnails), README section.
- easel: `uploadAttachment()` in easelApi; wired in DiscussionPanel (both
  composers, ThreadCard/ThreadView imagePrefix), StepComments, ProjectOverview
  screen-note bodies.
- Verified live (synthetic paste of a canvas PNG on a step comment): chip +
  thumbnail, markdown draft, rendered image from own URL, served image/png +
  nosniff; test comments soft-deleted after.

## 2026-09-02 — v0.35.0: tour outline in the panel, step comments, timeline dots
- Backend: step threads (`svc.step_thread/ensure_step_thread/comment_count`),
  rollup counts `("screen","step")`, `contents()` emits `beats[]` per entry,
  `StepOut.comment_count/thread_open`, two routes in `routers/walkthrough.py`;
  7 new tests, full suite green (155).
- Frontend: `WalkthroughPanel` rewritten as the full-tour outline
  (`PanelSection` × sections, live `Timeline` with `StepComments` in the
  current point); `Timeline` dots + `badges[]`; new pack piece
  `PanelSection`; `useStepThread/useCommentOnStep/useResolveStepThread`;
  Viewer: `onAsk`/resume plumbing removed, `?step=` re-armed on option change.
- Verified live (JS-driven): sections render with progress meta; nodes carry
  no text; comment post → badge "1 comment" + Resolve; tool section → 4
  points + "Take the tool tour"; outline jump → `/o/1?mode=play&step=1` with
  its section current/open. Test comment soft-deleted afterwards.
- Docs: CHANGELOG 0.35.0, pack README, caddie proposal (PanelSection row,
  Timeline points, subject rule).

## 2026-09-01 — /goal session: autonomous caddie rebuild begins
- P0: standards (coding/react/frontend) + kit internals read; orient workflow
  launched (punchlist precedent / Adi UX spec / league curation).
- Build plan written: .logs/planning/rebuild-build-plan.md (D1–D10).
- Judgment call D3: mockup auth = path-embedded signed bundle token
  unconditionally (goal §3.3 offered it as fallback) — cookie-into-sandbox
  behavior is browser-murky; the token variant is strictly stronger and makes
  relative subresources work with no cookie dependence.
- Judgment call D6: needs_attention = selection complete but open client pins
  (conflicting signals) — the least-arbitrary reading of §6.
- P1 done: managed-store swap green (73/73 incl. 62-test conformance), Alembic
  0001 exercised end-to-end on sqlite, compose+Dockerfile rewritten on the
  punchlist precedent (3-stage build w/ OpenAPI TS client), secrets in .app.env
  (660 easel-dev), CHANGELOG started, v0.2.0.
- Judgment call: adi seeded all_instances=True — kit semantics give an
  ungranted admin NO effective level per-instance; the manager side must ride
  app-wide.
- Orient results in: Adi's REAL walkthrough = story-note pins (gen-2), the
  pp_story_steps tour engine is dead code; option selection was never wired —
  easel builds it first. League curation: home_v1 (leading) / home_v3 /
  home_v5 + your-team-v6; bundles need img/ assets (not self-contained);
  Adi's designer-notes-pins-homepage.md will source the seed walkthrough.
- P2+P3 backend done: services (mockups/interaction/projects) + routers
  (projects/screens/options, mockups+/m/ serving, pins/threads/comments/
  attachments, walkthrough+approvals, notifications bell) + pydantic schemas.
  94 tests green (62 conformance + 11 rollup + 11 domain + 10 serving).
- Security tests pin the §3 controls: CSP sandbox on every /m/ response,
  dest=document 403, per-response bridge injection (disk pristine), token
  expiry/garbage 403, upload allow-list + traversal, attachment magic-byte
  sniff (HTML-as-PNG rejected).
- Judgment: /m/ auth = path token; middleware public-prefix documented as NOT
  an /api exemption. Mentions server-parsed; recipient authority enforced in
  interaction.notify (nate-mention test proves the drop).
- P4+P7a: SPA built (list/overview/viewer/bell, generated TS client, TanStack)
  — tsc clean first pass; deployed v0.3.0 with pg sidecar. Live verified:
  /api/meta v0.3.0, pg has 18 tables via boot-time alembic, adi admin/all_
  instances seeded, /m/ 403s garbage tokens WITH sandbox+nosniff+no-referrer
  through Caddy.
- P6-P8 done: league seeded in prod (idempotent, verified in pg), darren
  id-site-grant done, real-token serving verified through Caddy (200+bridge /
  403 top-level / sandbox header), conformance runner green (94), v0.3.1
  deployed + confirmed live, audit 0 critical / nothing new, caddie 05 §5
  appends + v8-feedback + ideas.md written, handoff written.
- Judgment: no browser connected -> TestClient bar per goal §8; easel-qa not
  created (only purpose was the browser pass); league left UNSENT so rian
  controls the client-facing moment.
- Live review with rian's browser found 3 defects; fixed in 0.4.0-0.4.2:
  bundle assets missing from seed, walkthrough rect guessing, min-width
  clipping. Root causes: a truncated grep that made me record "self-contained"
  wrongly; percentage rects instead of the target_selector column I had
  already modelled; and a []-deps effect attaching an observer before the node
  existed. All three verified in-browser, not just by tests.
- Learning recorded to caddie 05 §5 (the untrusted-HTML rendering family).
- Handover to Adi. Verified View As already works for him (admin +
  accounts.view_as + all_instances; darren a valid target, rank 41 vs 0) —
  nothing to grant. M2 brief written to .logs/planning/, handoff entry added,
  agents.md standing tasks replaced, brief.md ownership + reopened question
  updated.
- Added plan §0 (run the app as darren, write findings into the backlog) ahead
  of the four builds; flagged the View As read-only limit and the throwaway
  client-account workaround (owner-only command).
- Verified: easel has NO email of any kind — no Resend/SMTP wiring, and
  notifications.delivered_at is never written. Gateway holds a Resend key but
  its send endpoint is owner/hosting-peer only with its own recipient
  allowlist, so easel can't use it without a decision from rian.

## 2026-09-01 18:11 — adi

**Changed** (40 files, detected via mtime):
- `CHANGELOG.md`
- `bw-app.toml`
- `.bw-venv/lib/python3.12/site-packages/mako/runtime.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ast.py`
- `.bw-venv/lib/python3.12/site-packages/mako/parsetree.py`
- `.bw-venv/lib/python3.12/site-packages/mako/cache.py`
- `.bw-venv/lib/python3.12/site-packages/mako/lookup.py`
- `.bw-venv/lib/python3.12/site-packages/mako/util.py`
- `.bw-venv/lib/python3.12/site-packages/mako/pygen.py`
- `.bw-venv/lib/python3.12/site-packages/mako/pyparser.py`
- `.bw-venv/lib/python3.12/site-packages/mako/template.py`
- `.bw-venv/lib/python3.12/site-packages/mako/cmd.py`
- `.bw-venv/lib/python3.12/site-packages/mako/__init__.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/config.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/assertions.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/helpers.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/__init__.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/_config.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/exclusions.py`
- `.bw-venv/lib/python3.12/site-packages/mako/testing/fixtures.py`
- `.bw-venv/lib/python3.12/site-packages/mako/exceptions.py`
- `.bw-venv/lib/python3.12/site-packages/mako/codegen.py`
- `.bw-venv/lib/python3.12/site-packages/mako/lexer.py`
- `.bw-venv/lib/python3.12/site-packages/mako/_ast_util.py`
- `.bw-venv/lib/python3.12/site-packages/mako/filters.py`
- `.bw-venv/lib/python3.12/site-packages/mako/compat.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/babelplugin.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/beaker_cache.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/turbogears.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/linguaplugin.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/autohandler.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/preprocessors.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/__init__.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/extract.py`
- `.bw-venv/lib/python3.12/site-packages/mako/ext/pygmentplugin.py`
- `.bw-venv/lib/python3.12/site-packages/psycopg_binary-3.3.5.dist-info/top_level.txt`
- `.bw-venv/lib/python3.12/site-packages/psycopg_binary-3.3.5.dist-info/licenses/LICENSE.txt`
- `.bw-venv/lib/python3.12/site-packages/psycopg_binary-3.3.5.dist-info/METADATA`
- `.bw-venv/lib/python3.12/site-packages/psycopg_binary-3.3.5.dist-info/sboms/auditwheel.cdx.json`
- `.bw-venv/lib/python3.12/site-packages/psycopg_binary-3.3.5.dist-info/RECORD`
- Adi's four blockers triaged: QA client account created (link to 0600 file,
  no email sent), docker-exec scope corrected (only blocks league re-seeding;
  UI path is self-serve), browser corrected (Adi's own extension, not rian's),
  git initialised with secrets-safe .gitignore + baseline commit at v0.4.2.
- Verified db-query is MariaDB-only and easel has no db_name — Adi's Claude
  was right on that. Verified NO project on the server uses git and /srv/apps
  has no backup; filed both to /srv/.logs/ideas.md as fleet decisions.

## 2026-09-02 — adi

Session's work landed as 23 tagged versions (v0.5.0 → v0.27.0), each
committed with its own reasoning; the CHANGELOG carries the detail. Nothing
uncommitted at checkpoint.

## 2026-09-02 — Right-side padding on mockups (rian)
- Root cause: `.stage` filled its container (max-width 1600) while the scaler
  rendered at the mockup's natural width (1410) with scale capped at 1, so the
  190px difference sat as dead space on the right. Fix: measure the WRAPPER
  (measuring the stage was circular) and size the stage to naturalW*scale,
  centred; CSS max-width 1600 -> 100%.
- The padding was hiding a correctness bug: pin/spotlight coords are % of the
  mockup DOCUMENT but positioned against the STAGE box, so on any screen wider
  than the mockup every pin and highlight sat proportionally off. Verified the
  pin moved back onto its element after the fix.
- Second bug found while verifying: the SPA shell sent NO Cache-Control, only
  an ETag, so the browser heuristically cached index.html and kept loading the
  OLD hashed bundle — deploy green, /api/meta new, UI running old code. Shell
  now `no-cache` (ETag makes it a 304), hashed assets `immutable`. Regression
  test in test_domain.py.
- Verified live at 1758px: stage 1410 == scaler 1410, gaps 167/166, no
  horizontal overflow, plain reload (no cache-buster) picks up the new bundle.

## 2026-09-02 — caddie-alignment pass (rian)
- Built the caddie-ui pack (GlobalBar/AppBar/Locator, SidePanel, Bell,
  Thread*, Composer) and moved easel onto it; removed floating balloons +
  old drawer + three retired CSS blocks. App row simplified to controls.
- Judgment calls: docked (no-scrim) panel by default; pins remain anchors;
  Settings tab hosts VariantAuthor; `drawer=1` URL kept for old links.
- Wrote the caddie proposal; appended §5 pointer; added agents.md rule.
- Visual checks in the browser against review + caddie throughout.
- Walkthrough -> side panel timeline (v0.33.0). Pack: Timeline + panel
  below global row + cu-panel-push reflow. Ask-and-resume implemented.
  TourCard + its CSS retired; wt-* styles added.
- Committed Adi's pin-variants work + ignore fixes (a14eae6). Re-ran the
  scaffolder to pull the new View As pill (kit files byte-for-byte), 213
  green, deployed 0.33.1.
- Option-switch bug: frame birth-URL was per mount, now per option. Built
  Menu/CountChip (pack) + OptionNavigator (page -> direction dropdown).
  0.34.0 deployed.
