BoRoKnowledge baseGitHub

Build state

How the simulator is put together, the rules that must not break, and every ratified design decision.

LivingUpdated 2026-08-24

Field Value
Version 3.7
Updated 2026-08-22
Owner CJ Oltman
Target file the simulator44 screens: 23 stages + 12 proposed + 9 forms
Source of truth for lifecycle chat-lifecycle-stage-simulator.html — Brad’s original, in the same folder, never edited
Open items Open items v2.0 — every blocker and question, by owner. Source of truth.
Team handoffs Handoffs v1.0 — LIVING. What Brad and Jay owe, phrased to be answered without opening the file.
Work order Build order v1.8 — the current ordered build pass. Start there.
Call record Call record v1.2 — the 2026-08-21 review, quoted and attributed
Companion doc Review method v2.2 — context for a chat reviewing a call recording
Predecessor doc BoRo_ChatLifecycle_BuildList_Internal.md v1.3 — superseded by this doc; its §0 is withdrawn
Verify with ./tools/lifecycle/verify-all.sh — runs all three deploy checks. Tooling lives in the repo now, not in a session scratchpad.
Verified at handoff SMOKE PASS 368 + 74 form + 76 dock + 4 arrival + 96 chain + 96 chat + 13 live + 35 walk + 3 all + 7 panel + 35 spec + 7 offsite · master all three identical · token drift clean

The 368 is the constant. It is Brad’s 23 stages × 2 roles × 2 themes × 2 devices × 2 lanes and it has not moved since the lane axis landed, so a regression is always attributable. The suffixed suites move with the feature set and are expected to. This pass took form 12 → 74 (six new forms, plus a role-identity sweep and a lane check), dock 12 → 72 (it stopped comparing two placements and started enforcing one, across every stage at both roles), and added chat 96 for B-5. If a suffix shrinks without a feature being withdrawn, that is the regression.


BLUF

The post-review build list is finished except for two items, both of which are blocked on somebody else. As of 2026-08-22 the file carries 42 screens — Brad’s 23 stages, ten proposed chain stages, and nine form screens — on a cumulative thread with conversation woven between the milestones, gated menus, real photography and a working pairing code.

Done since the 2026-08-21 review: the ship gate (G-1 initiating actions + forms, G-2 real item images), then B-1, B-2, B-4, B-6, B-7, B-8, B-9, B-10, menu gating, both check-in chains, and D-1 built to a reading CJ ratified. This pass (2026-08-22) added: D-3 settled, B-5, and the six remaining forms.

  • D-3 is SETTLED — the action dock wins, decided by CJ. The in-thread alternative and the capture-bar toggle that carried both are withdrawn. This is the second reversal on that control and §5 records both.
  • B-5 is built, as a presentation-layer FILLER array on the same phase spine as EVENTS(). Brad’s master was not edited — see §5 for why that was the choice rather than the old prohibition. The Chat and Timeline tabs now genuinely differ.
  • All nine forms exist, and formHtml() was rewritten declaratively on the way. Adding a tenth is a data entry, not a code change.

What is left: two items, neither of them unblocked. Refresh Tap waits on R-4 (MAX_TOTAL_RENTAL_DAYS is 12 in the shipped constants against 30 in FSD §13.1) and the all-stages view. B-11 waits on D-4. After those it is decisions, not building.

Open items live in Open items — 27 of them, by owner. Two still block more than one thing: Q9 (no semantic tokens in boro-shared — the real integration blocker, and unscheduled work rather than a question anyone can answer) and D-4 + Q3 (the no-show chip, a data-model question only Brad can settle).

Four things a new session must not do. Do not act on §0 of the build list (§3 has the evidence). Do not edit STAGES[], SAMPLE_CHAT or ALWAYS without CJ’s say-so and sending Brad the diff (§2 rule 1 — that rule changed character on 2026-08-21, it did not disappear). Do not trust getComputedStyle across a theme switch on an element render() did not rebuild (§2). And do not resolve anything tagged D-1…D-7 without CJ — build the comparison and leave the decision. D-3 is now settled and D-1 is built as a proposal; the rest still stand.


1 / What the file is

A single self-contained HTML document — a 42-screen clickable simulation of the BoRo rental conversation, reviewed with Brad Larson and going to a workshop with Jaydan Nguyen. It is a specification, not a prototype — but it is walkable. As of 2026-08-22 the lifecycle controls navigate: every action on a card declares go (the stage it produces), form (the form it composes) or inert (no destination here, with the reason on hover), and the harness fails a button that declares none. A reviewer can click from Interest to a settled rental in seventeen presses, and the harness asserts they can.

What stays inert, and why it is not a loophole: four buttons have no destination in this document — two Contact Support, one Message Support, and Request BoRo Recovery — because support and dispatch leave the rental conversation. A fifth, Decline at the pickup decision, is inert because FSD §10.4 carries that control and STAGES[] does not. Each names its reason on hover and each is listed in the Styles panel.

Three transitions have no button on purpose. From a confirmed meetup into its check-in, at all three meetups, the window opens on a clock rather than a tap — so the card says so, TIME_OPENED declares them, and the capture bar’s Next carries the reviewer across.

Where the 42 comes from: 23 stages in STAGES[] (Brad’s master) + 10 proposed chain stages in EXTRA_STAGES + 9 form screens in FORMS[]. Only the first 23 are his.

Two layers, kept visually distinct on purpose:

  • Capture wrapper.sim-bar (top) and .notes / .styles (bottom). Blue-slate tooling chrome. Never follows the product theme. Carries role / stage / device / theme / lane / panel controls; the lane toggle appears only on the stages it affects and carries its own ⓘ explainer.
  • Product preview.preview > .phone > .shell. Real BoRo tokens, light as the base with a [data-theme="dark"] remap scoped to .preview — the same hook the app consumes through boro-shared.

The Styles panel (header → “Styles”) is the design handoff. It carries a live token map read from computed styles, component geometry, and ~50 flags recording every ratified decision, deviation and open question. Keep it current — it is what the developer reads, and it is where a decision from a call has to land.


2 / Rules that must not break

  1. STAGES[], SAMPLE_CHAT and ALWAYS are Brad’s master — and as of 2026-08-21 they may be edited with CJ’s authorization. The rule changed character rather than disappearing. It was never really “these bytes are sacred”; it was “Brad must never find a silent change to his lifecycle data.” So the deploy check is now a delta report: it prints a unified diff of any block that differs from his original, ready to hand to him. Default remains derive-in-the-presentation-layer — chipLabel(), rowDates(), FORMS[] all exist because that was the honest way to do those jobs. Edit the master only where the presentation layer cannot do the job truthfully, and send Brad the diff when you do. Slice precisely — from const STAGES = [ to its closing \n ];, not up to const SAMPLE_CHAT, because a screening comment sits between them and a naive slice reports a false positive.
  2. Check token drift on every deploy, next to the STAGES[] hash. The embedded token block is a copy of boro-ops/brand/boro-tokens.css, and the DS moves. This file sat on v2.8 while the DS shipped v2.9 on 2026-08-20, and the one drifted value was an AA failure across the entire caption layer. Compare the .preview and .preview[data-theme="dark"] blocks against the token file, normalising whitespace and leading zeros0.10 and .10 are the same value, and a check that cries wolf gets ignored.
  3. Single self-contained document. Only external requests are fonts: General Sans from Fontshare (product) and IBM Plex Sans + JetBrains Mono from Google (wrapper).
  4. Where the FSD and a built screen disagree, the FSD governs and the screen is the defect.
  5. Measure and look — neither substitutes for the other. Contrast from rendered pixels; geometry from getBoundingClientRect(). Several decisions in this project reversed once measured. And then look anyway: measurement catches a value one step off or a state that never renders, looking catches a semantic contradiction nobody thought to assert, and neither class is reachable from the other (Universal_SOP §3). Three defects here passed the suite and were caught by a screenshot in seconds.

The smoke harness — use it on every change

The tooling is in the repo now. tools/lifecycle/ holds the harness and the three checks; it used to live in a session scratchpad, which is why a new chat could not run it.

cd "…/App/Chat Lifecycle" && ./tools/lifecycle/verify-all.sh

It injects tools/lifecycle/smoke-harness.js into a throwaway copy and runs headless Chrome — the source file is never modified, which is the point. A clean run today reads:

SMOKE PASS 368 (184 per lane) + 74 form + 72 dock + 4 arrival + 80 chain + 96 chat

368 is the lifecycle grid — Brad’s 23 stages × 2 roles × 2 themes × 2 devices × 2 lanes. It has been 368 since the lane axis landed and is deliberately held constant, so a regression is always attributable. The suffixed suites are the things that do not belong on that grid: the nine form screens, the settled dock placement, both arrival orders, the ten proposed chain stages, and B-5’s conversation. They move with the feature set — see the note under the header for what moved in the 2026-08-22 pass and why.

Current assertions, all measured rather than asserted from the source:

  • every referenced CSS var resolves in both themes
  • chips are a 25–50% wash in BOTH themes, the ink is never --chip-on, and the ratio is measured against the composite (fill over ground), not the fill alone
  • card rails, discs, the timer bar and the current segment pip are opaque — never a wash
  • one visible avatar per message run, sitting on the bubble baseline
  • reading column is 760, left-aligned; the composer column matches
  • map plate: xMidYMid slice, the geofence measured circular in rendered pixels, the pin’s bounding-box midpoint on the ring centre and no more than 45% of its diameter, and the pin/ring ink identical across every card tone (the plate carries no status)
  • card rhythm: every adjacent pair of blocks inside a card is 12px apart
  • timer numeral and body text meet AA at the right threshold for their size (3:1 for display, 4.5:1 for body)
  • only one live display timer per thread, and only one card offering actions
  • badges come from the closed vocabulary, and “Approved” never lands on a card whose title says complete, arrived, returned, active or closed
  • date separators never repeat, never lead the thread, never trail it, and never walk backwards in time
  • history never shrinks along the canonical spine as the lifecycle advances
  • the segment counter is five pips with exactly one current, and never appears on Lane A
  • the lane toggle is visible exactly where it changes something — and byte-identical output between lanes everywhere it is hidden
  • every menu item that is disabled carries a reason, and no menu leads with the destructive item
  • exactly one header chip slot is visible
  • .action-row is asserted absent from the DOM, not merely hidden

When it fails, check the assertion before the file. Several failures so far were bad assertions rather than defects — a flat 4.5:1 applied to a 28px numeral where AA is 3:1; a control count that was counting a popover’s contents. But the harness has also caught real bugs that no amount of looking would have: a finished rental still counting down, a plate ring whose lift silently changed, and an exception stage whose phase sat below its own event so its card never rendered at all. Update the assertion when a decision is reversed — several in §5 have been, and an assertion enforcing a superseded rule is worse than none.

Do not trust getComputedStyle on an element whose own attributes did not change while an ancestor’s data-theme did. The tabs are the case: render() only classList.toggles them, and when the toggle is a no-op Chrome keeps the cached color even though the paint is correct. A sweeping in-page contrast audit built on it produced eight false failures — it reported the light tabs at 1.00:1 while the screenshot showed them near-black and the element’s own --content-primary read #1C1917.

The guard: measure through a probe element you insert yourself (el.appendChild a <span> with style.color = "var(--token)"), or re-create the subtree, or skip the DOM and do the arithmetic on the token values — arithmetic is immune and it is what caught the real defect. Elements render() rebuilds via innerHTML (bubbles, cards, chips, the composer) are fine.


3 / §0 of the build list is WRONG — do not act on it

The build list calls prohibited sample items a blocker, citing FSD §14.3. §14.3 contains no item schedule — it states three governing principles and defers the list to Terms of Service §8.

Checked against the published Terms (v2.5, effective 2026-08-06, boro-landingpage/src/data/legal.ts):

  • 8.4.4 H-4“Pressure washers — all types, electric and gas-powered, at any pressure rating: PERMITTED”
  • 8.4.1 H-1 — chainsaws, table saws, wet tile saws, chippers PROHIBITED; miter saws, circular saws, nail guns TIER 2; angle grinders, jackhammers, reciprocating saws, wood routers PERMITTED. A drill appears nowhere in the schedule.
  • 8.4.6 H-6 — ladders PERMITTED. Nothing in 8.4.7 H-7 covers a hair styler.

All three sample items are compliant. Acting on §0 would have churned the hero item, the SVGs, all sample copy and three inbox rows for nothing — and §0.4 would have written a false rule into the file for future passes to follow. A screening comment is already in place above SAMPLE_CHAT, pointing at Terms §8.4 with the real prohibited list.


4 / FSD verification already done — do not redo

Read from boro-ops/product/BoRo_MasterFSD.md (mounted; no upload needed).

⚠ These are V-tags, not D-tags — corrected 2026-08-22

Until this version these rows were labelled D-2, D-4, D-5 and D-6, which are four live IDs in a completely different register. In BUILD and REGISTER, D-1 is the QR model, D-4 is the no-show tone and D-5 is the extension decline window — none of which is what the rows below say. A session told “do not resolve anything tagged D-1 through D-7” and reading this table first would have protected the wrong things and resolved the right ones.

The only D-register is Open items §1–§3, mirrored in BUILD §Do not resolve. These rows are verification results — questions already answered by canon — so they carry V- and nothing here is a decision anybody still owes.

Verified Ruling Source
V-1 session length 45 minutes. “exactly one 45-minute session timer. There is no separate 15-minute timer.” The cards saying “30 min window” are the defect §10.2
V-2 QR model One code, tied to the booking, displayable only inside the 100-ft geofence, valid only when both devices are inside. Not mutual scan. (The open decision about this is D-1.) §10.1
V-3 condition capture Lender captures up to 5 photos + 1 video, timestamped; borrower confirms before taking possession. Built as the capture-condition form, 2026-08-22 §10.3
V-4 cancellation pricing cancellation.ts implements >24h none / 12–24h 50% / <12h 100% of the commitment hold, to the counterparty. Don’t block cancelling — price it in the confirm modal. Built as the confirm-cancel form, 2026-08-22 §6.1 + boro-shared/src/cancellation.ts
V-5 max term 30 days both lanes. Lane A (≤$100, no Shield) has no mid-rental events. Lane B ($101–$1,000) is five six-day segments, four Refresh Taps §13.1, §13.4, §6.2
V-6 double-hold Disclosure before the tap is mandatory, plain language, on a $500 item the borrower may briefly see $1,000 held §13.6
V-7 extension increments Commit up front to +6 / +12 / +18 / +24 days, to a 30-day total; the lender sees the total resulting duration and is not re-asked. §13.2 names no lender response window — that gap is D-5 §13.2, §13.3
V-8 report-a-problem Freezes the transaction; fault follows the inspection, not the accusation. Genuine fault: lender bears $25.00 dispatch + $0.75/mile. False report: it flips to the borrower §10.6

Correction to the build list §4.3: it says the return submissions are blind, “neither party sees the other’s.” §11.2 is narrower — “The borrower photographs the item at handback; the images are shown to the lender… Neither party sees the other’s submission.” There are two artifacts: the handback photos are shared, the condition tickets are blind. Building it as “everything hidden” breaks the lender’s ability to see what they are being handed.

Live conflict already logged in the FSD’s own register: MAX_TOTAL_RENTAL_DAYS is 12 in the shipped constants against 30 in §13.1; MAX_RENTAL_DAYS = 6 is correct only as the Lane B segment ceiling.


5 / Decisions locked — do not re-open

Thirteen labs, all ratified by CJ. The labs are the decision record.

Colour and chips

  • Stage chip tone derives from rental status via TONE_BY_STATUS, never a hand-picked hue. needsViewerAction / amberFor outranks status — whoever owes a decision sees warning.
  • disputed → error, not info. A state where something has gone wrong must not wear routine blue.
  • Both themes are a tint (ratified 2026-08-21, Lab 14 — light was solid until then). Light is the hue at 34% over the ground with the label pushed 55% toward black; dark is the same wash with the label lifted 35% toward white. The two percentages differ because they must: luminance is not symmetric about the middle of the range, so the literal mirror of dark measures 3.30–4.29:1 and fails AA on every tone. At 55% the identical wash clears everywhere — worst 5.65:1, better than the 5.0:1 dark tier it mirrors.
  • Rails, discs and bars take the opaque --hue-* set, never the chip fill — a 3px rail in a 34% wash disappears on dark.
  • Every chip and badge carries a 1px hairline; neutral takes border-strong.
  • Card badge uses chip geometry (22 · 3/9 · 11px/600), not standalone Badge’s 28.
  • Warning timer numeral: #C2410C orange-700 on light (5.18:1), bright amber unchanged on dark. --status-warning’s brown reads muddy at display size. Orange-700 as a single value fails dark at 3.38:1 — it is light-only.

Layout

  • Reading column 760px, left-aligned; tabs and composer match.
  • Desktop and mobile share one shape. Utilities: info visible in the header, ⋮ holds help / report / close. Composer is +, field, Send — three controls.
  • Send is muted on an empty field, .ready is the brand fill.
  • No brand header. Built and then removed by decision.
  • The action dock is SETTLED — docked — and it carries TWO reversals (D-3, decided by CJ 2026-08-22). Reversal 1, 2026-08-21: the dock was built and then removed by decision. Reversal 2, 2026-08-22: it is restored as the standard. Brad asked for it back on the review call — “I don’t want the next step to be lost in the thing. That’s why I docked it above the message.” CJ’s in-thread alternative was built, carried in parallel behind a capture-bar toggle so the two could be compared live, and withdrawn on the decision along with the toggle. CJ’s objection is not refuted and carries forward: the dock “covers up so much of the chat”, so it stays two buttons and must never grow back into the nine-control .action-row deleted on 2026-08-21. Scope is narrower than it reads: Brad blessed card-borne next steps (“the next step can be like on that card… but to initiate … there needs to be like a button for that”), so only the two initiating actions were ever in question. dockMode is gone; threadHtml() emits nothing and composerHtml() always emits nextStepHtml(). The harness asserts .next-actions is absent from #thread at every stage and both roles — absent from the DOM, not hidden, the rule .action-row is held to. Do not restore the in-thread branch, and do not re-remove the dock.
  • Mobile header is two rows — nav (back · relation centred · ⋮) over an identity band on surface-base with a 1px hairline seam, 52px thumbnail, chip holding the right edge, name clamping at two lines.
  • Lifecycle cards are elevated documents — white ground, shadow-md, 3px status rail, icon in a filled disc, terms in an inset panel with its own border.
  • My chat bubble is surface-inverse, not brand red. Messages group into runs: one avatar and one tail on the last bubble of a run, pointing at each other.
  • Conversation rows carry a message preview; an unread third conversation exists to show the §07 variant (content-primary preview + 8px trailing dot).
  • Approve is Primary, Deny is Neutral (Ghost’s label is content-brand, which would put two reds on one card).
  • Card blocks are spaced by one 12px gap on the card’s flex column, not per-block top margins.
  • A running session is “In process”, amber with a clock — never green/Approved.
  • The rental carries the display timer, same as the inspection session, and settles to a line once the thread moves past it. Line weight is reserved for dates that qualify something else.
  • Both sheets are drawn open and gated — “+” for lifecycle actions, “⋮” for utilities, disabled items shown with their reason. That is §7 item 7, brought forward.
  • Relation toggle full width under the headline, with a working filter beside it.
  • Badges are a closed vocabulary — Approved · Completed · Active · Pending · In process · Ready to pair · Under review · Flagged · Denied · Overdue · Expired · Closed · No show · Not returned. Fourteen entries (twelve until 2026-08-22, when the two terminal cards went red and “Closed” stopped being the right word for either). “Approved” is only a proposal that has not yet happened. (Corrected 2026-08-21: this list was missing Ready to pair, which is live on the pairing card. A harness rule regenerated from the short list fails that card.)
  • Fact labels and the terms lead-in are bold — the label column carries the contract vocabulary.
  • Sample items carry real make-and-model names, re-screened against Terms v2.5 §8.4.
  • Card actions run full width — side by side when their own labels fit, stacked when they don’t. The decision is made by the wording, not a breakpoint.
  • The thread is cumulative. Every event at or before the current phase renders; only the event at the current phase is live. A completed rental carries 12 cards where it carried 3.
  • The chip dictionaryCheck-in · Waiting / You're here / Pair to start, plus Inspection · Concluding and Return · Scheduled. Labels derive; STAGES[] is untouched.
  • Initiating actions are full width at the foot of the thread, on three renter screens only. Placement is D-3, under test — Brad’s dock against CJ’s in-thread, Jay breaks the tie.
  • Try-on vs inspection derives from the ITEM’s worn flag, not the stage.
  • Form screens live in FORMS[], never STAGES[], with form: addressing in the picker. Form and pending card read from one PROPOSAL object so the card is the preview.
  • Sample items carry real photography from BoRo’s own library, embedded as data URIs, screened by make and model against Terms v2.5 §8.4.
  • Cards have three shapes — expanded, folded, flat. The newest is expanded; a meetup card stays expanded until both parties have checked in, because it carries directions somebody is still using; a card with nothing behind the fold renders flat with no chevron.
  • A no-show and an unreturned item read red, card and action. The chip half is a named override pending D-4 — see the Styles panel.
  • Card tone has four kinds and amber is reserved (2026-08-22, CJ). wait · info is the default for unfinished — waiting on somebody, or a step in progress with nothing at stake. pend · warning is a closed list: a clock the viewer is standing inside (the two 45-minute sessions, the 9-hour grace window) or money about to move (the authorization disclosure, the take-it-or-not decision). no · error is the only place the hazard triangle appears. An amber chip over a blue card is not the mismatch Brad caught — he caught a green card under an amber chip, two statements that contradict. These do not: the chip says who owes the next move, the card says whether anything is wrong. Chip tone still derives from TONE_BY_STATUS.
  • One info affordance per layout (2026-08-22, CJ — closes CR-12). Desktop keeps the ⓘ in the header and drops it from ⋮; mobile drops it from the nav row and keeps it in ⋮. It was duplicated in both layouts. This is the amendment to the mobile header line above.
  • Send lights up on input. .ready was styled from the first build and nothing ever set it — this section described a state the document could not reach.
  • The thread pins after the composer is built, not before. The old order measured against the previous composer’s height, so the newest card sat clipped exactly when the dock appeared.
  • The active tab rule is 3px, strictly heavier than the 1px tab bar it sits on.
  • The lifecycle buttons navigate (2026-08-22). Three declarations — go, form, inert — and navigation must go forward; a control landing the viewer earlier than the card they pressed it on is the cumulative thread eating its own history. One declared exception: denying a pickup routes to schedule-denied, which sits at phase 1.5, so it rewinds past the whole inspection chain. That is a finding for Brad, not a bug in the wiring — his schedule-denied serves both the denied inspection and the denied pickup, and a stage has one position on the spine. The rewind is declared on that one button and the harness permits exactly it.
  • Renting / Lending is live, and so is the filter. Both painted state and did nothing; the sidebar toggle sets role and stays in step with the capture bar’s Renter / Lender pair — one state, two ways to reach it.
  • Cancelled and Terminated are two filter rows (2026-08-22). It does not settle Q3 / D-4, which is about what a no-show is called and what tone it wears; both statuses already existed separately. One inference is flagged: not_returned sits in Terminated, a system end rather than a user quit — Brad’s to confirm.
  • A sheet reads as choices — the label carries its own weight, the reason sits lighter underneath.
  • The reference panel is DOCKED RIGHT, full height, with its own scroll (W-23, 2026-08-22 workshop — Brad’s ask: “better suited to be on the right instead of the bottom… make it like scrolling and like bigger”). It was a 42vh bar across the foot. Under 1180px it overlays the preview row rather than pushing — and rather than the viewport, which would cover the capture bar including Next. The second half of W-23, a detached pop-out window, is deliberately not built.
  • The panel still defaults to Hidden (2026-08-21) — placement moved, the default did not — the product preview is what a reviewer opens this file to look at, not the capture chrome.

Built in polish batch 2, 2026-08-21 — listed here because these are the ratified decisions they implement

  • Timers: two weights, not four — a clock you are standing in gets the display numeral and a bar; a date you are not gets a line. Format by magnitude: date over a week, days inside a week, minutes inside a day, seconds only in the last hour.
  • Date separators in the thread.
  • Map plate: preserveAspectRatio="xMidYMid slice" so the geofence stays circular at any width, and no venue label — the card body already names it. Revised 2026-08-21: the pin is centred in the ring and ~40% smaller (~36% of ring diameter), reversing “anchored at the tip, ring raised 9px” — the ring is a 100-foot zone, not a point. The plate carries no status: pin and geofence were taking the card’s tone, so one venue was green on one card and amber on the next. Composition is constrained by the crop — only about y 26–106 of the 132-tall viewBox survives at desktop widths.
  • Segment counter on the rental-active card for Lane B (five pips, current one amber) rather than five duplicate stages, with a Lane A / Lane B toggle in the capture bar. Refined 2026-08-21: the toggle appears only from phase 9 (the pickup proposal) and activeLane() forces Lane A below that, so hidden also means inert. It carries its own definition on hover and keyboard focus.

6 / What is built

The batch log below is kept for its reasoning. The short version: full DS restyle on boro-tokens.css v2.9 / DS v2.21; a cumulative thread whose cards fold to one line behind the newest; conversation woven between the milestones, so the Chat and Timeline tabs differ; timers in two weights; a map plate; date separators; the chip dictionary and a closed fourteen-word badge vocabulary; gated ⋮ / “+” / filter menus that open the forms they name; real item photography and team headshots; two initiating actions, docked, and nine form screens; a Lane A/B model with accruing segments; a symmetric pairing code; and both check-in chains as proposed stages. Forty-two screens.

6b / How it got there, batch by batch

Full DS restyle: tokens (boro-tokens.css v2.9 / DS v2.21), General Sans with Inter fallback, §07 components, §04 mobile band framing, focus rings, aria labels, the Styles panel. Plus batch 1 of the call notes:

  • Dark wash chips + the opaque --hue-* split
  • 760 left-aligned reading column
  • Bubble runs
  • The tone mismatch Brad caught — the GPS card was lifeCard("ok", …), a green card under an amber chip. Now pend, titled Check-in · One arrived
  • “Schedule a meetup” in the composer (the renter has two legal paths; the button opens a chooser)
  • The Terms v2.5 §8.4 screening comment

Plus batch 2 of the call notes (2026-08-21):

  • Timers, both weights, with one magnitude formatter. Display timers on the inspection session and the late grace window; line timers on the meetup cards, the rental-active card, the extension and the review window.
  • SESSION_MINUTES = 45. Both meetup cards said “30 minutes”; FSD §10.2 says exactly one 45-minute session. §2 rule 3 applies. The field is now labelled Session, the FSD’s own name. STAGES[] untouched, so Brad’s gap note still carries the live question.
  • Map plate on the five location cards, xMidYMid slice. (Pin geometry was revised in batch 4 — see below.)
  • Date separators, emitted only where the day changes.
  • Segment counter and a Lane A / Lane B toggle in the capture bar. rowDates() substitutes the Lane B range only where stage.dates is the Lane A rental range, so the try-on’s own dates and STAGES[] are safe.
  • A late card — it previously wore a red chip over a green “Return meetup” card and nothing else. Carries the 9-hour grace countdown (FSD §12.1) and the two resolution paths (§12.3, §12.4). New copy, needs Brad.
  • .action-row deleted, not hidden. composerState() kept; the “+” now carries its list in data-menu.
  • Card foot: the last block in a card clears 16px off the bottom edge. It was sitting flush.

And batch 3 (2026-08-21), all ratified by CJ:

  • Light chips became a tint, matching dark’s construction with a deeper ink shift. See §5.
  • The chip dictionary, three renames across five stages, derived through chipLabel().
  • Full-width card actions.
  • A cumulative thread. The single biggest change in the file: milestones are now EVENTS on one timeline and a stage is a position on it. Adding a stage means giving it a phase, not editing a chain of if (stage.id === …). Card counts along the spine: 1 · 1 · 2 · 2 · 3 · 4 · 5 · 6 · 7 · 7 · 9 · 10 · 10 · 11 · 12 · 12.

Batch 4 — a review round, and where several earlier decisions were reversed:

  • Map pin centred in the geofence and ~40% smaller, reversing “anchored at the tip, ring raised 9px”. The ring is a 100-foot zone, not a point. The offset is derived, not eyeballed.
  • The plate carries no status colour. Pin and ring were taking the card’s tone, so one venue was green on one card and amber on the next.
  • The map redrawn to read as a place — road casings at two widths, a diagonal, parkland, water. Composition is constrained by the crop: only about y 26–106 of the 132-tall viewBox survives at desktop widths.
  • One 12px gap between every card block, replacing per-block top margins. The facts panel had none, so it sat flush against whatever preceded it.
  • “Check-in · One arrived” is live-only — once both have arrived it is the same check-in in an earlier state, not history, and the “Both arrived” card carries both timestamps.
  • The inspection card is “In process” — amber, clock glyph — where it was green with an “Approved” badge on a session still running.
  • Short button labelsComplete & Rent Item, Finish & Return Item.
  • An entry point for extensions. extension-requested existed as a stage with no way to reach it, and Brad’s own renterCan lists both “Request 1-day extension” and “Return early” — neither had a control.
  • Real make-and-model sample items, re-screened against Terms v2.5 §8.4.

Batch 5:

  • Badge vocabulary closed. “Approved” was on eleven of twelve cards in a finished thread, including a check-in, a return meetup and the rental itself.
  • Bolding — the fact label column, the Commitment hold: lead-in, and the display-timer labels.

Batch 6:

  • The rental carries the display timer, same as the inspection session — it is the longest clock the borrower is standing in. It settles to a line once the thread moves past it.
  • Sidebar restructured — relation toggle full width under the headline, with a working filter beside it (All · Needs your action · In process · Completed · Cancelled & terminated). It replaces the old “show completed & cancelled” checkbox, which could only do one of those five things.
  • ⋮, “+” and the filter all drawn open and gated, disabled items showing their reason. That is §7 item 7.
  • On desktop the chip moved to the relation line under the product name; mobile keeps the right edge.

Batch 7 — 2026-08-22, this pass:

  • D-3 SETTLED: the dock wins. dockMode, the #dock-group toggle and the in-thread branch are all gone; the harness stopped comparing placements and started enforcing one across every stage at both roles. Two reversals recorded as reversals — see §5.
  • B-5 · conversation between the milestones. A FILLER array keyed by at on the same phase spine EVENTS() uses; messages and cards merge into one sorted stream. Three of the four items carry their own copy; the lens stays empty on purpose as the document’s empty-thread sample, and the harness asserts it. The Timeline tab renders milestones only, which is what finally makes the two tabs differ.
  • SAMPLE_CHAT was not edited — and that was the choice, not the old prohibition. See §5 and the Styles panel: making his drill-specific five item-keyed changes the shape of his data rather than adding to it. Master check still reports all three blocks identical.
  • The conversation row previews the thread it is showing. It read the last SAMPLE_CHAT entry unconditionally, so after B-5 it said “EXACTLY.” while the thread ended four days later. Preview and timestamp both derive now.
  • The six remaining forms, and formHtml() rewritten declaratively on the way. See §7 item 4.
  • A defect a screenshot caught and the harness had passed: the cancellation form’s two buttons both read Cancel and meant opposite things. Fixed, and a rule now fails any form whose two actions start on the same word.

368 combinations pass (184 per lane). STAGES[] byte-identical at 26,286 characters. Token drift clean.


7 / What is left, in order

THE POST-REVIEW BUILD LIST IS FINISHED. Every item on it is built. What follows is kept as the record of what each one was and where it landed; the only thing still waiting is B-11, on D-4 + Q3.

  1. Polish batch 2 — done 2026-08-21, see §6.
  2. Chip dictionary — the three safe renames are done. A real Late chip and an extension chip stay open and only Brad can settle them; his own gap notes rule both out. chipLabel() makes each one line.
  3. The two check-in chainsDONE 2026-08-22 as proposed stages in EXTRA_STAGES. Register R-7 is Brad’s adoption.
  4. Form stagesALL NINE DONE, and formHtml() rewritten declaratively so a tenth is a data entry.
  5. Refresh TapDONE 2026-08-22, unblocked by R-4 settling at 30 days. refresh-tap-due and refresh-tap-failed, both in EXTRA_STAGES, both Lane B only — Lane A has no Shield to refresh, so activeLane() forces B and the toggle hides. §13.6’s double-hold disclosure sits on the same screen as the button because the FSD says before the tap and calls it not optional; §13.4’s ordering is stated as the safety property it is; §13.5’s “the rental does not cancel” is the whole message of the declined screen. Four of those are asserted by name.
  6. All-stages viewDONE 2026-08-22, and built last on purpose: it inherits whatever the thread does, so every earlier decision shows up in it without the view knowing about any of them. Every screen rendered side by side as a real thread, pinned to the bottom of its own history, proposed stages marked.
  7. Menu gating — done 2026-08-21, extended 2026-08-22 so three items open the form they name.
  8. B-11 · no-show reads as a consequence, not a filingthe one item still blocked, on D-4 + Q3.

8 / Still open — needs Brad

Superseded as the working list by Open items v1.6, which consolidates these with BUILD’s D-tags and the call notes’ conflicts into one register, keyed by owner and by what each blocks. IDs are preserved on both sides. What follows is kept for its evidence and reasoning; the register is what you work from and what goes in front of Brad or Jay.

  • The seven gap: notes in his own file: Cancelled vs Terminated on no-show; close-while-accepted; the 45-vs-30 minute field; missing chips; composer behaviour in the review window and in a dispute; hold amount on the pending card; the GPS one-arrived copy swap.
  • Repo/branch currency, Figma link and edit access, CSS-variables vs Tailwind for the handoff, and whether the full DS token set lands in boro-shared.
  • boro-shared/tailwind/boro-theme.css carries no semantic tokens — only the brand ramp, two surface steps, spacing and radii. There is currently nothing in the app to build this against. This is the real integration blocker.
  • RentalStageChip.tsx fails WCAG AA on five of twelve states, worst 2.00:1 on the chip meaning a lender owes a decision. Live in the app today, independent of this file.

Waiting on CJ, not Brad

  • The line timer treatment. Lab 15 round 2 recommends icon + sentence (“Meetup starts in 1 day”). Round 1’s pill was rejected as underwhelming — a chip means state everywhere else in this document, and a countdown is not a state. This is the one unratified lab.
  • Past segment pips drawn in success, where the ratified decision named only “current one amber”.
  • Whether the Lane A / Lane B toggle earns its place before Refresh Tap gives Lane B more to differ on. It currently changes the rental term, the facts row and the segment counter, and nothing else.
  • The badge spread on a long thread — a finished rental shows a column of Completed badges. Raised, not settled.

Settled since — kept visible so they are not re-opened

  • D-3, the dock placement — SETTLED 2026-08-22 by CJ: docked. See §5 for both reversals and their reasons. Jay no longer chooses this; the toggle that would have let him compare the two live is withdrawn with it. Worth saying at the workshop rather than letting him find it, since the register named D-3 as the decision the workshop existed to make.

Added by the 2026-08-21 review — see BUILD §Do not resolve

  • The QR model (D-1). (Built as a proposal 2026-08-22 to CJ’s ratified reading; Brad and Jay still react.) Three live positions on one mechanic: FSD §10.1 (one booking code, geofence-gated, valid only with both devices inside), Brad on the call (“they should both have a QR code or scan the other person’s… whoever wants to do it” — his second call running asking for mutual scan), and a lender-side rendered code. It is the substrate for both check-in chains, so a wrong guess gets built on twice.
  • The blue own-bubble (D-2). Reverses surface-inverse, hedged three times, and there is no blue in the token set — a new semantic token and a ninth entry on §9, where none of the eight are applied. Also unresolved: CJ argued the counterparty’s bubble should carry the emphasis; the point was never answered.
  • No-show tone (D-4). Brad wants red, not grey. Tone derives from TONE_BY_STATUS and terminated is neutral, so this needs either a platform-wide move or a new status — the same question as Cancelled vs Terminated. Settle both together.
  • Extension decline window (D-5). CJ referenced “back within that one day”; FSD §13.2 names no lender response window. Either it exists somewhere unread or it is an assumption that will get built.
  • “Make these thicker — the active one” (D-7). CJ, unanswered on the call. Unclear whether it means the segment pips (which would fold into the Q15 pip question) or the display timer’s progress bar.

Three things from batch 2–4 that want Brad’s eye specifically

  • The late card is new, and BoRo Recovery is not in his renterCan list — it comes from FSD §12.4.
  • SESSION_MINUTES moved 30 → 45 per FSD §10.2; his gap note still carries the question.
  • The meetup cards no longer print a weekday — his data calls the same date Sat on the try-on and Tue on the pickup, which was invisible until the thread became cumulative and put both in one view.

9 / Design System amendments this work has accumulated

The register is Design-system amendments. Twelve, none applied. Doc first, then regenerate boro-tokens.css, boro-typography.css and the Tailwind preset together — never one alone.

This section used to hold the list, and so did two other places. They disagreed: this one numbered eight, R-5 said ten and named .badge.info which this one had missed, and this one named the component specs — the largest item — which R-5 had missed. The union is twelve. Nobody was wrong on purpose; each list was written in a different pass and none of them was the one place. It is a dataset now, and this section points at it.

The one rule that survives here, because it was learned here: --content-tertiary sat on the list until someone looked, and the DS had already fixed it at v2.21 with the stone-550 half-step — the list was simply a version behind. Check the DS changelog before proposing an amendment.


10 / Lab index — the decision record

Lab URL
Stage Chip Lab https://claude.ai/code/artifact/e1fc9e38-da5f-4769-a221-b979501cba4c
Bubble & Button Lab https://claude.ai/code/artifact/94937b52-d5fc-42c6-aa18-a8ac145f9541
Chat Surface Lab https://claude.ai/code/artifact/c650ce9f-c744-45df-9e5d-2fbe5f10d7c8
Lifecycle Card Lab https://claude.ai/code/artifact/c9f13cac-facc-41e6-b537-e5c4e668d7a7
Chip System Lab https://claude.ai/code/artifact/9aec2a4f-28dd-402d-9732-523b31cbbaf5
Mobile Screen Lab https://claude.ai/code/artifact/cd37df4f-d7fa-4797-819e-f0d9e56550be
Desktop Layout Lab https://claude.ai/code/artifact/d410a59d-6ecf-491f-9828-e8c8d8afc28f
Mobile Header Lab https://claude.ai/code/artifact/59fa5fca-2ee2-4bbb-b8f6-444a7cc01280
Header Surface Lab https://claude.ai/code/artifact/7ec7de7a-849f-4257-808e-35c8c9a7949d
Dark Chip Tier https://claude.ai/code/artifact/9dddbad1-5c0e-42c7-83d2-d1056ceb8763
Thread Surface Lab https://claude.ai/code/artifact/7050a64d-4b6e-495a-8d22-c5838a37805c
Timer & Map Fixes https://claude.ai/code/artifact/c58bbff8-f599-498d-ba4f-69af6d325ff1
Long Rentals https://claude.ai/code/artifact/ca71cbb5-a61b-41bd-a62e-edd1f1a5e76a
Light Chip Tier https://claude.ai/code/artifact/58c7255a-f363-4b25-9d8c-4b467481653a
Line Timer Lab https://claude.ai/code/artifact/0f9e02f3-8c54-4a2d-9a1e-455647509d6a

11 / Working pattern that produced good results

  • Propose options in a lab before implementing anything visual. Build the options on real tokens, measure contrast and geometry live in the page, recommend, then let CJ pick. Several recommendations reversed once measured — the Soft chip ceiling, the orange numeral on dark, the surface-lift row on dark.
  • Render and look. ./_tools/shot.sh '<js state>' renders any screen to a PNG without touching the source — then crop with sips and read it. Bugs that pass a smoke test are visible in a render, and this session had two of them: a form whose heading was scrolled off by the webfont callback, and a merge branch that never fired because a renderer read window.stage.
  • Name your own mistakes plainly and fix them. Several defects in this project were in the labs, not the product — a .preview class collision, a .m span rule catching chips, a nested .spec resetting dark tokens.
  • Check the DS changelog before proposing a DS amendment. --content-tertiary was re-derived from rendered pixels as a “new” AA finding; the DS had already fixed it the day before with a stone-550 half-step. The file was simply a token version behind. Anything holding a copy of the tokens drifts.
  • Reversals are normal — record them as reversals. Four ratified decisions were overturned in one day (light chips solid → tint, pin anchored → centred, plate tone → monochrome, rental line → display timer). Each is written into the Styles panel as a reversal with its reason, so nobody “fixes” it back.
  • Build the assertion with the decision. Every new component in this session shipped with a harness rule. That is what caught the finished rental still counting down, and the exception stage whose card never rendered because its phase sat below its own event.
  • BLUF every response, detail below a ---.

12 / Changelog

v3.7 — 2026-08-22. W-18 and W-2’s interim copy. Off-site testing is built as an axis on the pickup decision — Decline disabled with its reason rather than removed, because §10.4 disables it and a gated control shows its reason. The card carries the locked sentence the rule depends on: the trigger is the ACT of taking the item, not the passage of time — a rewrite that loses it turns the rule into an arbitrary timeout, so the harness fails the card if it stops saying it. W-2: both terminal cards were pricing a case §6.1 does not define, and were incoherent on their own terms — “neither side… forfeited to the other party” has no other party. They now state the event and name what is and is not settled. The rule is still owed; this is a holding position.

v3.6 — 2026-08-22. The agreed, unambiguous batch off the workshop, and a new living document. Handoffs is the targeted pass R-6 has been asking for since 2026-08-21 — the things waiting on Brad and Jay, phrased so they can be answered without opening the file, with an answered section that keeps closed items visible. W-22 gives every stage a Building it block — the governing FSD sections, what the screen has to do, and a Watch out line — which is the clearest thing Brad asked for in either call; it lives in SPEC_NOTES in the presentation layer, in its own block, because why: is his copy in his master and this is ours. W-20 (Lane A never reads “None”), W-15 (the capture is mandatory and has a control), W-10 (the badge column lines up — a folded card’s chevron had been pushing its badge 30px inboard of an expanded card’s).

W-20 exposed an assertion that was passing on a keyword rather than on a fact — N-3’s folded-card money rule was exempting anything that said “Shield”, so a lane threshold that had always been there failed the moment the wording changed. Tightened to what N-3 actually prohibits.

v3.5 — 2026-08-22. The 2026-08-22 Jay workshop, and the first item off it. W-23 built — the reference panel is docked right, full height, with its own scroll and a close control inside it; under 1180px it overlays the preview row rather than the viewport, which the first version did and which covered Next on the capture bar. The pop-out window half of W-23 is deliberately not built and is flagged. §5’s panel line is amended: placement moved, the Hidden default did not. The workshop’s 27 W- items are in REGISTER v2.0 §5b with the evidence in Workshop record. C6 closed; three canon conflicts opened (W-1 against FSD §10.2, W-2 against a case §6.1 does not define, W-3 against LOCKED §8). A recurring measurement trap became a shared harness helper — noTransitions(), for reading a computed style through a CSS transition.

v3.4 — 2026-08-22. The post-review build list is finished. Refresh Tap (§7 item 5) and the all-stages view (§7 item 6) both landed, so nothing on the list is outstanding and B-11 is the only item still waiting on anybody. Refresh Tap is two Lane-B-only proposed stages carrying §13.6’s mandatory disclosure, §13.4’s ordering property and §13.5’s the rental does not cancel. The contact sheet renders all 44 screens as real threads. Two findings and one latent bug came out of the pair: R-11 (a Lane B term has four taps and the spine has one position for the rental — the tap is built as an interruption, not a step); a tile could not be a <button> because the threads inside carry buttons, and the harness passed the mangled DOM while a render showed it instantly; and activeLane() had existed since the toggle landed with only rowDates() calling it, so “hidden also means inert” was true of exactly one thing.

v3.3 — 2026-08-22. Six changes from CJ, and R-4 settled at 30 days, which unblocks Refresh Tap and leaves no build item waiting on anybody. Card tone gained a fourth kind so amber could be reserved for a real clock or real money, and the hazard triangle for a real error. CR-12 closed — one info affordance per layout; it had been duplicated in both, not just on desktop. Send lights up on input, the thread pins after the composer exists, the active tab rule is 3px, and the sidebar filter split earlier in the day gained its missing .badge.info tone. Three of these were things this panel already claimed were true.ready, the pin, and the info rule — which is the pattern worth noting: a Styles entry is a claim, and until there is an assertion behind it, it is only a claim.

v3.2 — 2026-08-22. The document became walkable. Card actions navigate (go / form / inert, forward only); the end-to-end walk is asserted — seventeen presses, Interest to settled — with the try-on branch asserted separately. Renting / Lending and the filter are live controls rather than painted state. Cancelled and Terminated split into two filter rows; menu labels carry their own weight. Four findings came out of the wiring and are recorded rather than papered over: schedule-denied is one stage doing two jobs (Brad); Decline at pickup exists in FSD §10.4 and not in STAGES[]; "I'm Here" carried a marker nothing read, and the harness had been asserting the marker rather than the behaviour; and the cancelled sample row became unreachable when its bucket was split under it.

v3.1 — 2026-08-22. This pass: D-3 settled, B-5 built, the six remaining forms built, and five defects in these documents fixed.

  • Header — version, date, screen count (42, with the arithmetic shown), companion versions corrected to REGISTER v1.6 and BUILD v1.8, new verification line, and a note on which baseline numbers are constant and which are expected to move.
  • §1 and §6 — the file is 42 screens, not 23 and not 33. “33 screens — 23 stages, ten proposed, and three form screens” did not add up in either direction; the arithmetic is now written out.
  • §4 — the D-tags in this section were a collision and are now V-tags. They were labelled D-2/D-4/D-5/D-6, which are four live IDs in a different register meaning different things. A session told not to resolve D-1…D-7 and reading §4 first would have protected the wrong things. Two rows added (V-7 extension increments, V-8 report-a-problem) from the FSD reading this pass needed.
  • §5 — the dock rewritten from reversed and under test to settled, with both reversals recorded.
  • §7 — item 4 corrected (it said two forms were done when three were, and all nine are now); items 5 and 8 marked blocked on R-4 and D-4; item 7 extended.
  • §8 — a Settled since block, so D-3 is not re-opened.
  • §13 — starter prompt corrected: it still said 33 screens and still told the next session the dock was undecided.

v3.0 — 2026-08-22. Shipped without a changelog entry; reconstructed here from §6b and §7 for traceability rather than left blank. The check-in chains as ten proposed stages, B-4’s collapsible past cards and merged completion summary, D-1 built as a proposal, and the register’s first consolidation.

v2.1 — 2026-08-21. Folded in the 2026-08-21 review call. Nothing was removed; four changes and two additions:

  • Header now points at Build order as the work order and Call record as the call record. The predecessor build list is at v1.3 and superseded, not v1.1.
  • §5 — the action dock is a reversal under test, not a settled removal. The old line would have had a future session restore the removal Brad asked to undo.
  • §5 — the badge vocabulary was missing Ready to pair, which is live on the pairing card. Twelve entries, not eleven. A harness rule generated from the short list fails that card.
  • §7 — ordering superseded by BUILD’s ship gate. G-1 and G-2 come first; two of the eight forms pull forward.
  • §8 — five decisions added from the call (D-1, D-2, D-4, D-5, D-7 in BUILD’s numbering).

v2.0 — 2026-08-21. Six batches of build, the DS restyle, cumulative thread and gated menus.


13 / Starting the next chat

Paste this, then let it read.

Read these four documents before doing anything, in this order:

1. .../App/Chat Lifecycle/HANDOFF_ChatLifecycleSimulator.md      (build state)
2. .../App/Chat Lifecycle/REGISTER_OpenItems_ChatLifecycle.md    (open blockers, by owner)
3. .../App/Chat Lifecycle/BUILD_ChatLifecycle_PostReview.md      (the ordered work list)
4. .../App/Chat Lifecycle/NOTES_CallReview_ChatLifecycle.md      (the 2026-08-21 call, quoted)

The target file is in that same folder: chat-lifecycle-stage-simulator-v2.html.
It is a 42-screen clickable specification of the BoRo rental conversation - not a
prototype - reviewed with Brad Larson and shown to Jaydan Nguyen.

Start by running ./tools/lifecycle/verify-all.sh and reporting the three baselines, so any
regression from this pass is attributable to it. Then give me your plan before
you edit anything.

Constraints, all of which the docs expand on:
- STAGES[], SAMPLE_CHAT and ALWAYS are Brad's master. They MAY be edited with my
  say-so, but the change goes to Brad with the diff the master check prints.
  Default to deriving in the presentation layer - chipLabel(), rowDates(),
  FORMS[] and EXTRA_STAGES all exist because that was the honest way.
- Check token drift against boro-ops/brand/boro-tokens.css on every deploy.
- The document stays self-contained apart from the two font hosts, so images
  embed as data URIs.
- Where the FSD and a built screen disagree on transaction behaviour, the FSD
  governs and the screen is the defect.
- Item eligibility is screened against Terms of Service v2.5 section 8.4 by make
  and model. FSD section 14 sets tiers and defers to it. No summary of either is
  screening authority.
- Measure AND look. Neither substitutes for the other - the harness has passed
  things a screenshot caught immediately, and the eye has missed values only the
  harness could hold.
- Do not resolve anything tagged D-1 through D-7. Build the comparison, leave the
  decision, and say in your summary which ones you hit. D-3 is settled (docked)
  and D-1 is built as a proposal; the rest still stand. The only D-register is
  REGISTER §1-3 and BUILD's "Do not resolve" table - HANDOFF §4 carries V-tags,
  which are verification results, not decisions anybody owes.
- Every decision lands in the Styles panel, and a reversal is recorded AS a
  reversal with its reason. Build the assertion alongside the decision.

What to pick up first, unless CJ says otherwise: the all-stages view (§7 item 6) — it is the last unblocked build item in the file. Everything else that is left is waiting on somebody: Refresh Tap needs R-4 (MAX_TOTAL_RENTAL_DAYS is 12 in the shipped constants against 30 in FSD §13.1) and B-11 needs D-4 + Q3, which are one question and settle together.

Then it is decisions, not building. The register’s §7 names who owes what.

Two traps this project has already fallen into, both in §2 and worth re-reading before touching anything: getComputedStyle returning stale values across a theme switch, and an element id silently becoming a global so a renderer referenced window.stage instead of its own parameter. Neither threw; both passed the harness.