# Changelog

Newest first. The version matches `main/app/version.py`, surfaces at
`/api/meta`, and is how you confirm a deploy landed.

## 0.15.1 — 2026-09-10

- Re-vendored the BW Auth pack. Admin, People: the owner can give a member a new
  password (the "New password" button), shown once with a copy button for your own
  email; nothing is emailed and they are signed out everywhere. It works for
  accounts used only in this app; broader accounts are reset from
  auth.bowden.works/admin. Adding a person with no BW account can also create them
  with a generated password instead of an invite email. Refusals from the auth
  service now say why instead of a bare HTTP 400.

## 0.15.0 — 2026-08-23

- **Each item now gets the control that fits it.** A step declares `control`
  — `button` (the default), `input`, `upload` or `link` — plus `states` for a
  status list. The control exists to COLLECT something or to SEND you
  somewhere; the row's checkbox is what marks the item done, so a step that
  needs neither declares nothing and shows only a checkbox.
- **Matched across the Keith workflows:** the deposit invoice is a status the
  client moves along (Waiting on invoice → Invoice received → Paid, and only
  the last one finishes the step); the Google Ads customer ID is an inline
  field; the brand guide is a file upload; the photos item is a link to the
  shared Drive folder with the checkbox recording it; hosting is a choice
  between our plan and their own host.
- **A status list is the one new engine concept**: intermediate states are
  recorded and stay put — "where is this up to" is worth knowing on its own —
  and reaching the LAST state advances the step. It can be moved backwards.
  The state reads downstream as `{{fields.<step>.state}}`, the same way a
  choice does.
- Guards, all with pointed errors: `input`/`upload` take exactly one field
  (they render in the row, not in a panel); `link` collects nothing; a status
  list can't also collect fields or fan out; states and choices are two
  different controls and a step has one or the other.
- The Google Ads customer-ID pattern now accepts the ID with or without
  dashes. People paste it both ways, and refusing a correct ID over
  punctuation is the kind of thing that stalls an item for a day.

## 0.14.0 — 2026-08-23

- **The checklist shape, from the controls lab.** Every row now leads with a
  checkbox, and the item's "⋯" menu lives on it rather than on the control —
  the outs belong beside the thing that says whether the item is done, not
  attached to whichever control happens to answer it.
- **Items whose whole answer is a claim have no button.** If a step has no
  fields, no targets and no choices, the button was repeating what the
  checkbox already says; ticking the checkbox performs the action. Rows with
  something to enter, choose or fan out keep their control on the right.
- **The menu grows out of the checkbox** rather than sliding: the same tab as
  the alternatives segment, in a third orientation, revealed by clipping
  rightwards over the label. Clipping rather than resizing because animating
  width re-lays out every frame and rounds to whole pixels — over a few px
  that reads as dropped frames. It peeks for the CHECKBOX, not the row.
- **An unanswered choice toggle is an outline.** A filled accent means
  "answered" everywhere else, so an unanswered control must not wear it, and
  two filled halves read as two buttons rather than one question.

## 0.13.2 — 2026-08-21

- Reveal durations halved (760ms / 1100ms). With constant-speed motion the
  same distance reads far slower than it did on the old ease-out, so the long
  durations that were compensating for the curve are no longer needed.

## 0.13.1 — 2026-08-21

- **Found what "it disappeared" actually was.** Answering an item didn't just
  hand it to the team — the list sorted globally (demoting anything waiting on
  us) and only grouped into sections afterwards. Section grouping works on
  consecutive runs, so the demoted item was torn out of its neighbours and
  rendered under a SECOND "Decisions" heading at the very bottom of the page.
  Grouping now happens first and ordering happens inside each group, and
  acting on an item no longer reorders it at all — only a raised flag moves a
  row. You watch your answer land instead of hunting for the row.

## 0.13.0 — 2026-08-21

- **The reveal curve was the problem, not the duration.** Everything moved on
  an ease-OUT, which covers most of the distance immediately and then crawls —
  "whooshed out, then a slow creep at the end", exactly as rian described, and
  no amount of extra duration fixes it. Reveals now use a new `--glide` token:
  near-constant speed with only slight smoothing at the ends. `--soft` stays
  for fades and hovers, which should still land softly. The segment's bump
  takes the same curve. Two alternatives are noted in the CSS and are one line
  each: creep-then-accelerate, or dead linear.
- **Yes / No**, not "Yes, add it / No, skip it".
- **Your answer stays on the row.** Choosing used to pass the item to the team
  and leave the client with nothing to show for it — the row appeared to
  vanish. The same control now stays put with your answer lit and the
  road-not-taken dimmed, read-only, and the answer follows the item into Done
  as a chip. The team still sees their own step ("Noted") as before.

## 0.12.4 — 2026-08-21

- The authoring standard teaches `choices` (with the "Submit choice — what
  choice?" rationale), and its self-check now classifies the doc's JSON blocks
  by shape rather than position — adding a snippet used to silently retarget
  what the test validated.

## 0.12.3 — 2026-08-21

- The pull-down chevron moved to its own few-pixel strip below the hint.
  Centred on the text it punched a hole through the middle of a sentence and
  read as a rendering glitch; with clear space it can stay soft and still be
  seen. The peek grew 6px to make room.

## 0.12.2 — 2026-08-21

- The peek's pull-down chevron was invisible: border-grey over running text
  disappears. It now sits in a small clearing of the card surface, so it can
  stay soft without vanishing.

## 0.12.1 — 2026-08-21

- Fixed: the choice toggle's last option was clipped by the alternatives caret
  tucked beneath it — the toggle needed the same paint-above-the-runway
  treatment the single primary button already had.

## 0.12.0 — 2026-08-21

- **Actions can be an answer, not a button.** A step now takes EITHER a
  `primary` button OR `choices` — two to four mutually exclusive answers
  rendered inline as one segmented control, fused to the same alternatives
  caret. A decision is a single tap on the row: no opening the item to find
  out what the choices even are, and no second submit button underneath.
  A choice can branch (each carries its own `to`), and the chosen LABEL is
  readable downstream as `{{fields.<step>.choice}}` through the existing token
  machinery — so the team's step can say what was picked.
  Guarded: button XOR choices, 2–4 answers, no fields or targets on a choice
  step, no key that shadows an engine action or an alternative.
- **"Decide on age verification" is the first one converted** — "Submit
  decision" (hiding a select) became **Yes, add it / No, skip it** on the row,
  and it gained a short guide explaining what age verification actually does
  so the answer is an easy one.
- **Everything reveals at half speed again** (`--reveal-ms` 1520ms,
  `--expand-ms` 2200ms). Still two lines to tune.
- **The peek's chevron is centred** like a pull-down handle rather than tucked
  into the corner.
- **The guide moved into the alternatives dropdown**, leading it above the
  "Can't do this right now?" outs. The standalone row icon and its linger glow
  are gone — the caret already breathes on dwell, so a second attention signal
  on the same control would compete with it.

## 0.11.1 — 2026-08-21

- Removed the now-dead `.pl-how` styles (the text-and-icon tutorial nod the
  guide icon replaced).

## 0.11.0 — 2026-08-21

- **Everything reveals at half speed.** The peek, the in-flow open, the target
  fan-out and its stagger all roughly doubled in duration, and they now read
  from `--reveal-ms` / `--expand-ms` / their fade partners in `:root` — the
  pace of the whole app is two lines to tune. The alternatives segment is
  deliberately excluded: those timings are hand-tuned and stay put.
- **The guide is an icon now, not a sentence.** "Show me how — 3 steps" is
  gone from both places it appeared; a quiet question mark sits at the end of
  the row instead, in the slot the expand caret used to occupy. It's nearly
  invisible at rest, surfaces on row hover, and after four seconds of
  lingering on an OPEN row it blooms gently and rests, repeating — a reminder
  it's there, not a demand. The peek no longer advertises step counts either.
- **The peek shows a light double chevron** at its right edge saying it opens
  further. It sits over the hint's fade, so it costs no extra height.
- **The grey expand caret is gone.** Clicking the label or the peek opens the
  item and the double chevron advertises it, so the row gets that space back.
  The label became a real keyboard control (role, tabindex, Enter/Space,
  aria-expanded) — it was the caret that carried keyboard access before.

## 0.10.3 — 2026-08-21

- **The real peek-seam bug, finally.** Every row was wrapped in its own
  unclassed `<div>` (an anchor target for `?item=` deep links), which made
  EVERY row the `:last-child` of its own wrapper — so every peek got the
  leaving-the-card treatment (border + 12px rounded bottom), and the rule
  meant to square off the card's own corners never matched at all. That is
  why it showed on every item and worst on a section's last one. Rows are now
  direct children of the list card, with the anchor id and arrival flash on
  the row itself. Mid-card peeks are borderless square sheets again; only a
  true last row carries the card's edge, and the card hands its rounding over.

## 0.10.2 — 2026-08-21

- The guide dialog is anchored near the top instead of centred — a centred
  dialog re-centres itself whenever a step is taller than the last, sliding
  the header and the text you're reading under the cursor. Now only its
  bottom edge moves.

## 0.10.1 — 2026-08-21

- Guide polish: the progress rail was being squeezed to a sliver (the dialog
  is a flex column and the rail was shrinking), and the body's min-height
  left short steps floating in empty space.

## 0.10.0 — 2026-08-21

- **Step-by-step tutorials are real.** `tutorial` accepts a structured guide
  — `{title, intro, steps: [{text, note, image}]}` — and "Show me how" opens
  it one step at a time: a progress rail you can also navigate, a numbered
  step, the gotcha in a quieter voice underneath, arrow-key/Escape support,
  and a fixed body height so stepping never resizes the dialog. Guide prose
  interpolates `{{variables}}` like any other prose, so a step reads "Enter
  rian@plusroi.com and support@bowden.cc", not a placeholder. A bare-string
  tutorial still renders — published specs are immutable, so the old shape
  must work forever.
- **15 guides authored** across the workflows that have a real click-path:
  GA4 (account-level, the property-level fallback, and granting there),
  Google Ads (finding your customer ID, accepting our request), Merchant
  Center, Facebook Business, Mailchimp (create + invite), Wix (contributor +
  contact export), Search Console, Google Business Profile, and the domain
  registrar (finding who it is, then GoDaddy delegate access). Written for
  someone doing the task in another tab: one action per step, "look for X"
  rather than pixel positions, and the gotcha that actually trips people.
- **Moving live work onto a newer workflow version is now possible, and
  guarded.** `POST /api/workflows/sync-seeds` publishes a new version of any
  shipped workflow whose file moved on (never mutating a published spec), and
  `adopt-latest-template` (per item, or per punchlist) re-points live items —
  but only when every step the item has actually visited still exists, so its
  event history can still replay. Incompatible items are reported, never
  silently skipped.
- Fixed: a section's LAST row made its peek look detached — the card's own
  rounded bottom corners still curved above it, so you saw two rounded shapes
  stacked. The card now hands its bottom rounding to the peek while it's out.

## 0.9.0 — 2026-08-21

- **Comment attachments.** Paste an image into the composer (or use the
  paperclip) and it uploads and rides along in the comment — the answer to
  "how does the client actually hand us the brand guide / photos /
  screenshots". Images render inline in the thread (click to open full
  size); PDFs become links. Files live in the mounted data volume under the
  owning item, so access always follows the punchlist.
- Security posture on the new surface: images + PDF only (nothing
  script-capable, no SVG), server-generated filenames, magic-byte
  verification against the extension, 10 MB cap, per-item file cap,
  nosniff + our own content-type on serving, and visibility re-checked
  through the owning item on every read. Covered by tests
  (tests/test_attachments.py).

## 0.8.5 — 2026-08-21

- **The builder's standard exists**: `notes/authoring-workflows.md` — the
  complete instructions for an AI builder (a small local LLM included)
  converting a client email into workflows + a set. Golden rules
  (goal-titles, verb-first buttons, outs on every client step, team-verify
  before $done, targets fan-out, shared variable names), the exact spec
  shape, the email→punchlist recipe, and the API calls. A test extracts the
  doc's json examples and runs them through the real validator, so the doc
  cannot drift from the code.
- Queue polish: an opened queue row no longer shows its label twice (the
  header keeps chip + provenance while the card is open).

## 0.8.4 — 2026-08-21

- Fixed: clicking an item's comment-count chip also expanded the row (the
  chip lives inside the label, and its click bubbled to the label's toggle).
- Unmatched paths now say "Not here" with a way home instead of rendering a
  silent empty shell.

## 0.8.3 — 2026-08-21

- **Peek seam fix (the button's rule, applied to rows).** Mid-card, the peek
  is now a borderless square-edged SHEET of the card's surface sliding over
  the next row — its old border + rounded corners made it read as a second
  container floating inside the card. Only the last row's peek, which leaves
  the card entirely, carries the card's edge with it (side borders + the
  card's 12px bottom radius, bleeding 1px to cover the card's own corners).
- **Dwell shows the peek; click expands.** Resting on the label 0.5s slides
  the peek out; clicking the label (or the peek itself) opens the item. The
  1.5s dwell-to-open is gone — it collided with reading (dwell opened the
  row, then your click closed it).
- **Multi-target steps collapse to one line.** Instead of three "Granted —
  email" buttons up front, the row shows a single chip — "Granted · 1/3"
  with a folded chevron — that expands DOWNWARD into the per-target buttons
  (same slow push as the full open, buttons rising in a small stagger; the
  chevron unfolds). Done targets show ticked; the chip counts progress.

## 0.8.2 — 2026-08-21

- **The item peek no longer moves the list.** Hovering used to expand a hint
  in flow, shoving every row below it down. The peek is now absolutely
  positioned — it OVERLAYS the row beneath (opaque surface, soft shadow, the
  row's own separator dropping away so it reads as the row continuing) and
  costs the layout nothing.
- **Opening still pushes, but slowly**: click the label or rest on it ~1.5s
  and the item expands in flow over 560ms in one continuous motion
  (grid-template-rows animating an auto height). The dwell is bound to the
  LABEL, never the actions — reaching for a button must not spring the row
  open under the cursor.

## 0.8.1 — 2026-08-21

- **Fixed both reveal jolts, and they had one cause.** The breath was a
  keyframe animation whose selector (4 classes) out-specified the hover rule
  (class + pseudo), so `animation: none` never applied — the breath kept
  running while the segment was hovered, snapping it out mid-cycle and
  dragging it back on its own beat. Everything is now transition-driven
  (transitions interpolate from wherever the element currently sits, so
  mid-flight state changes are seamless) with an explicit state machine:
  rest → bump → swell → open. The segment now holds open until the cursor
  genuinely leaves it, and returns at the same pace it left.
- **Breaths come one at a time**: one breath, then a 3s pause, then another —
  not 3s of silence followed by a continuous loop.
- **The chevron unfolds** as the segment opens (folded -90° while tucked,
  rotating upright on the open glide).
- Rian's tuned timings shipped as the defaults: 1000ms bump rise, 16% bump,
  3s dwell, 3s breath, 8% swell, 700ms open glide.

## 0.8.0 — 2026-08-21

- **The alternatives choreography, v5 ("Calm" — rian's spec).** Row hover
  raises the bump SLOWLY (700ms); the button's right corners square off and a
  hairline appears at the seam, so button + segment read as ONE control rather
  than two shapes side by side (the rounded-inner-edge problem). Two seconds
  of resting starts the bump breathing gently. Hovering the bump glides it
  fully open with a small overshoot that settles. Transform-only throughout —
  the button and its text never move. Touch devices rest in the split form.
- Lab round three: one tunable choreography with live sliders (bump rise/size,
  breathe delay/length/swell, open glide, overshoot), four presets, a CSS
  readout, and hold-state pins.

## 0.7.2 — 2026-08-21

- **Alternatives affordance v4 (lab round two, rian's spec).** The caret
  segment lives in reserved runway behind the action: row hover peeks a
  sliver, button hover / ~0.7s dwell / sliver hover glides it fully open —
  transform-only, 450ms soft easing, the button never moves a pixel. Touch
  devices get the segment always out. Lab rebuilt around the reveal family
  (A1-A4 + pinnable states), row-grow with the tutorial nod inside, and the
  counted button that splits into three staggered target actions.

## 0.7.1 — 2026-08-21

- Polish-loop refinement from live browser passes: target rows hint with
  their INSTRUCTION instead of re-listing emails already shown as buttons
  (the hint had tripled the same addresses on one row).

## 0.7.0 — 2026-08-21

- **Alternatives affordance v3**: the caret is FUSED into the action button
  (one visual unit, inner hairline; target groups get a small solo caret) —
  no more "or" word. **Hesitation pulse**: dwell on an action ~1s without
  clicking and the caret breathes once — first-need discovery, zero standing
  noise. Six candidate designs compared in the Or-Affordance Lab artifact.
- **Hover hint peek** (desktop): rows grow a fading one-line preview of what
  acting involves — field names, "3 to add: …", or the instruction's first
  line — plus "step-by-step guide inside" when a tutorial exists.
- **The tutorial nod**: "Show me how" (with a ? icon) inside the act area and
  the expand, opening a How-to modal (graceful placeholder until guides are
  authored via bw-guide-shot).
- **Click-away closes things**: the or-menu, the bell panel, and any EMPTY
  composer/act area dismiss on outside click (typed text is never discarded).

## 0.6.1 — 2026-08-21

- **The comments drawer** (review's notes-sheet, translated): conversation
  moves off the rows into a right slide-over — thread bubbles, composer pinned
  at the bottom, Esc/backdrop close, full-width on phones. Rows carry only a
  small comment-count chip; "Add a comment" in the or-menu opens the drawer.
  New GET /api/items/{id}/comments.
- **Visible version + refresh prompt**: footer shows the running version; when
  the server moves on mid-session, a "Punchlist was updated — Refresh" bar
  appears (60s check). Old labels in a stale tab now announce themselves.

## 0.6.0 — 2026-08-20

- **Comments + notifications (the Interaction Standard's first real
  implementation, modelled on review).** One thread per item; the workflow's
  flag/reply messages and freeform comments are a single stream; server-side
  @mentions; turn notifications to the side whose court the ball landed in;
  dedupe-keyed, never-notify-self. THE BELL: idle / unread / needs-you states,
  30s poll, deep links that open the punchlist scrolled to the exact item.
- **The "or" affordance** replaces the faint "...": a soft chip attached to the
  action button, menu headed "Can't do this right now?", with Something else…
  and Add a comment; sentence-length labels live in the menu, short verbs on
  buttons (label pass over all 16 seeds, e.g. "Upload brand guide").
- **Sections**: items carry an optional section (from the set) rendered as
  quiet uppercase headers that group without re-sorting — the email's
  Billing/Design/Barnet/Decisions/Access/Nice-to-have/Content structure.
- Punchlist archive (PATCH state), archived hidden from home.
- Migration 0003; 97 tests.

## 0.5.0 — 2026-08-20

- **Template sets (M2 core).** Publish a set of workflows with shared
  variables; fill once -> every item lands in order (`POST
  /api/punchlists/{id}/set-runs`). Each item takes only the variables its own
  template declares. Set library + publish endpoints; append-only set seeds.
- **The Keith email, converted.** 14 new seed workflows authored from a real
  onboarding email (deposit invoice, design direction, Barnet API, three
  decisions, Merchant Center, Facebook, Mailchimp setup+access, Wix contacts,
  domain registrar, Search Console, Business Profile, content gathering) +
  the 16-item `website_onboarding` set that strings them together.
- **Manager-hat Add panel**: run a set / add one workflow / QUICK ITEM — a
  label + button one-off (optional team-confirm step), never entering the
  library.
- 91 tests.

## 0.4.2 — 2026-08-20

- Internal viewers get a board-shaped header ("2 with the client · 1 with us")
  instead of the client's "waiting on you" copy.
- A done item with a conversation keeps it reachable: chevron on the done row
  expands the last messages (the reply that unblocked it no longer vanishes
  into History).

## 0.4.1 — 2026-08-20

- Queue rows show the interpolated goal label (raw {{tokens}} were leaking).
- The "..." menu no longer clips at the list container edge (overflow fix).
- Both found by the full in-browser walkthrough (View As democlient, act mode).

## 0.4.0 — 2026-08-20

- **Checklist compaction + the goal-label rule** (rian's feedback). One line
  per item: the goal-phrased LABEL left, the context-aware action right;
  fields open on demand under the row; History moved into the "..." menu;
  instruction/detail/tutorial all behind the chevron. The label comes from the
  workflow title (now interpolates {{variables}}, never fields) and NEVER
  changes with the step — "Grant GA4 admin access to ..." stays put while the
  button evolves to "Added at the property level".
- Spec: step `headline` renamed `instruction` (optional; legacy alias kept
  forever — published specs are immutable). Seeds rewritten goal-labeled.

## 0.3.1 — 2026-08-20

- Field validation errors never show a client the raw regex: FieldDef grows an
  optional human `hint` ("it usually looks like 123-456-7890"); generic
  fallback otherwise. Found driving the live UI.

## 0.3.0 — 2026-08-20

- **The frontend.** Client punchlist view with the full item anatomy: headline
  + context-aware primary button (per-target when the step declares targets),
  the quiet "..." alternatives menu (jumps, ack/attention flags with a message
  composer, implicit Other), chevron-expand for detail with the tutorial
  inside, message thread ("From us: ..."), struck-through waiting-on-us, the
  confirmed pop-out pulse into the Done bucket (reduced-motion aware), and a
  needs-you-first sort under a "N things waiting on you" header.
- **Team surfaces**: /team queue (attention pinned, expand to act inline,
  resolve-with-reply), per-item History timeline, Add-item panel (library
  picker + template-declared variables), People (kit InstanceMembers).
- **Home**: punchlist cards with waiting counts; create for holders of the kit
  create permission.
- Hand-kept TS API types for M1 (backend has no response models yet);
  OpenAPI-generated client planned with the authoring API (agents.md).

## 0.2.0 — 2026-08-20

- **The engine.** WorkflowSpec validation (every error at once, step-named,
  near-match suggestions — the AI-builder contract), the event-sourced item
  engine (per-target completion with fresh-ask-on-jump-back semantics,
  alternatives: jumps / ack + attention flags / implicit Other, team
  resolve-with-reply, set_step override), status derivation with a pure
  replay oracle proving the caches honest, and text interpolation
  ({{vars}} + {{fields.step.key}}, email lists as prose).
- **The API.** /api/punchlists (create couples the kit instance, slugified
  ids, 404-over-403 discipline), items instantiate (library XOR inline
  one-off), /api/items/{id}/actions, team queue, event timeline (team-only),
  /api/workflows + /validate (dry-run) + publish (versioned, immutable).
- **Persona permissions** registered with the kit: items.act_team,
  items.compose, workflows.author — grantable to levels in the admin UI.
- **Seeds**: google_ads_access (full turn-taking loop) and ga4_admin_access
  (per-target grants + the "no admin option" conditional unfold into
  property-level fallback), loaded append-only at startup.
- 86 tests passing (engine, spec, API, conformance pack on the managed store).

## 0.1.1 — 2026-08-20

- Postgres sidecar from day one (compose `db` service, internal network, no
  published port) and the auth kit's store moved into it via
  `bw_store_sqlalchemy` **managed mode** — one database, Alembic owns every
  table (kit + domain).
- Domain schema per plan §7: `workflow_templates` (immutable once published —
  code guard + Postgres trigger), `template_sets`, `punchlists` (product row
  1:1 with the kit instance), `set_runs`, `items` (library XOR inline spec,
  CHECK-enforced), append-only `item_events` (with per-target and message
  columns), `punchlist_seen`.
- Migration 0001 carries the PACKS.md kit warnings and append-only level
  seeding; proven on SQLite (tests) and Postgres (deploy).
- Conformance pack green on the new store (62/62) via an app-owned
  `tests/app_conftest.py` wipe fixture.

## 0.1.0 — 2026-08-20

- Scaffolded from the BW app template (instance noun: punchlist); BW client
  registered; first deploy.
