chore: expand frontend test coverage (276 new tests) - #36
Conversation
Covers previously untested hooks, stores, components, and utilities: - Web hooks: use-goal-queries, use-habit-queries, use-popover-menu, use-app-toast, use-tour-mock-data - Web stores: tour-store - Web lib: server-fetch, api-fetch i18n adapter - Web components: EmptyState, PillToggle, Skeleton*, SurfaceCard, Switch, OfflineUnavailableState, TodayFilters - Shared: tour-store, tour-steps, preferences, validation constants, error-utils extended, habit-schedule-match Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 768438ec67
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,108 @@ | |||
| import { describe, it, expect, vi, beforeEach } from 'vitest' | |||
There was a problem hiding this comment.
Add mobile tour tests to maintain required parity
The root AGENTS.md requires every change to be applied to both web and mobile, but this commit adds new tour coverage only on web (including use-tour-mock-data and web tour-store) without corresponding mobile tests for apps/mobile/hooks/use-tour-mock-data.ts and apps/mobile/stores/tour-store.ts. This leaves one platform unvalidated for the same tour logic path, so CI can pass while mobile-specific regressions in these flows remain undetected.
Useful? React with 👍 / 👎.
…rst ticket (#709) * feat: hand the manual step to the human, and talk before building a design ticket Two output surfaces the harness was missing. Neither is a new gate: a missing Rollout section is silence, and a conversation-first ticket under --sleep is a deferral with a reason, never a crash. Half 1, a manual step must survive the merge. orbit-tickets#81 closed Done on 2026-08-08 carrying "Rollout: merge, deploy to Render, then set PostHog:ApiKey in the Render env. The code path is inert until the key exists." The PR was perfect and the key was never set, so NoOpProductAnalytics discarded every signup_completed and subscription_* event for two days. Every gate here measures the pull request; that step is not in one. tools/lib/manual-steps.mjs extracts the step, expands it into concrete numbered actions, and quotes the ticket for the confirmation. complete-ticket.mjs posts it as a comment BEFORE it closes the issue, and returns it so /orchestrate step 14 and the /merge-prs report can print it. Only verified detail is expanded: the Render navigation labels come from Render's own documentation, and the __ to : mapping was proven by running .NET 10.0.204, not recalled. Half 2, a genuinely interactive ticket must talk first. ORB-30 (#36) names Pencil as the prototyping tool in one section and Claude Design in another while saying Pencil is retired, and its acceptance criteria carry a human grant no agent can satisfy. classifyConversationFirst detects that shape; plan-queue defers it NEEDS_CONVERSATION with its open questions under --sleep, and admits it with a warning when Thomas is awake. comment-ticket.mjs writes the answers back to the ticket, which /orchestrate already treats as part of the work order. Also fixes the wake-source gate test, which counted registered wake sources where the hook counts live ones, so a dead overnight process left it red on main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: carry rollout scope through subsections, keep a kill switch a reversal, drop the unused label write Three Codex findings on #709. P1, a standalone `## Kill switch` heading. Its bullets start straight in on the action with no label to strip, so they read as outstanding steps and the renderer expanded "Remove PostHog:ApiKey" into "Click + Add Environment Variable": the exact opposite of the intent. Reproduced, then fixed by classifying the whole section as reversal when its heading is a reversal heading. P2, a rollout organized under child headings. The flat heading filter selected the empty parent and dropped every child, so extraction returned nothing and completion closed the ticket with no comment at all. A matched heading now carries its scope through its descendants, the way inScopeSections already carries Out of scope through its own, and a sibling heading ends the region. P1, evidence for the label write. createLabel has no callers now that the two labels exist, so rather than record evidence for a write path nothing uses, it is deleted. CLAUDE.md standard 2. Also corrects the record. The premise that #81's key was never set is FALSE and is removed from every comment and contract that asserted it. Verified live 2026-08-10 against the PostHog project: posthog-dotnet 2.12.1 has delivered signup_completed and the four subscription_* events since 2026-07-25, 20 signups with distinct_id set to the user GUID, plan set on all 21 people, latest event today. Nothing was discarded. The step was never SURFACED, which is the actual gap and still worth closing; 13 of 166 open tickets carry one of the same shape. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rom the work (#724) * chore: /orchestrate asks instead of assuming, and manual steps come from the work Two behaviour changes Thomas asked for on 2026-08-13, plus the tool gap they exposed. Ask when unsure, at any step, in any mode. The worker prompt's "choose the reading a careful colleague would" made silent assumptions the instructed behaviour. Now ambiguity has two tiers: mechanical choices land in a PR-body ## Assumptions section the orchestrator adjudicates with Thomas at handover, and a decision that belongs to Thomas ends the worker with NEEDS_DECISION: <question> instead of a guess. Step 7 reads it from the worker log; attended runs ask and relaunch with the answer commented on the ticket, sleep runs defer with the question in the report. Step 2b states that the classifier and the needs:conversation label are detection aids, not the boundary of asking. Manual steps from three sources, not one ticket section. Step 10 merges the ticket's own sections (complete-ticket.mjs --preflight), the PR body's ## Manual steps section the prompt now requires, and the orchestrator's own diff read for new env keys, secrets and vendor-console dependencies, then comments the merged list on the ticket. tools/label-ticket.mjs is new: the only sanctioned label mutation on an existing ticket, validated against the live label list. Measured the same day: needs:conversation could not be applied to #36 through any tool, and the raw-mutation hook correctly blocked gh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: act on the Pullfrog review of the ask-not-assume PR The NEEDS_DECISION and ## Assumptions read now happens at EVERY worker exit, DELIVERED included, because verify-delivery.mjs reads artifacts and never the log, so a delivered branch proves nothing about an unanswered question. A delivered PR with an open question keeps its ticket In Progress under --sleep, never In Review. Assumptions are adjudicated at the step 7 worker-exit read, before the step 8 and 9 loops, so an invalidating answer is ordinary bounded-fixer work rather than a revoked READY; step 10 only prints outcomes. The gh issue edit --add-label/--remove-label contract is now proven by execution per code standard 8: three real invocations against gh 2.97.0 on orbit-tickets#316 (single add, combined add plus remove in one call, re-add), each confirmed by the follow-up label read; the evidence lives in the editLabels comment. The label-ticket success cases now consume a removePath marker on the stubbed edit command, so deleting the editLabels write fails the test instead of passing on a preloaded issue view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* chore: /orchestrate asks instead of assuming, and manual steps come from the work Two behaviour changes Thomas asked for on 2026-08-13, plus the tool gap they exposed. Ask when unsure, at any step, in any mode. The worker prompt's "choose the reading a careful colleague would" made silent assumptions the instructed behaviour. Now ambiguity has two tiers: mechanical choices land in a PR-body ## Assumptions section the orchestrator adjudicates with Thomas at handover, and a decision that belongs to Thomas ends the worker with NEEDS_DECISION: <question> instead of a guess. Step 7 reads it from the worker log; attended runs ask and relaunch with the answer commented on the ticket, sleep runs defer with the question in the report. Step 2b states that the classifier and the needs:conversation label are detection aids, not the boundary of asking. Manual steps from three sources, not one ticket section. Step 10 merges the ticket's own sections (complete-ticket.mjs --preflight), the PR body's ## Manual steps section the prompt now requires, and the orchestrator's own diff read for new env keys, secrets and vendor-console dependencies, then comments the merged list on the ticket. tools/label-ticket.mjs is new: the only sanctioned label mutation on an existing ticket, validated against the live label list. Measured the same day: needs:conversation could not be applied to #36 through any tool, and the raw-mutation hook correctly blocked gh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: act on the Pullfrog review of the ask-not-assume PR The NEEDS_DECISION and ## Assumptions read now happens at EVERY worker exit, DELIVERED included, because verify-delivery.mjs reads artifacts and never the log, so a delivered branch proves nothing about an unanswered question. A delivered PR with an open question keeps its ticket In Progress under --sleep, never In Review. Assumptions are adjudicated at the step 7 worker-exit read, before the step 8 and 9 loops, so an invalidating answer is ordinary bounded-fixer work rather than a revoked READY; step 10 only prints outcomes. The gh issue edit --add-label/--remove-label contract is now proven by execution per code standard 8: three real invocations against gh 2.97.0 on orbit-tickets#316 (single add, combined add plus remove in one call, re-add), each confirmed by the follow-up label read; the evidence lives in the editLabels comment. The label-ticket success cases now consume a removePath marker on the stubbed edit command, so deleting the editLabels write fails the test instead of passing on a preloaded issue view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: wire the intake findings into the harness Three edits from the 2026-08-13 intake session: - audit-security checklist, section J: name OpenAI in the processor list, demand plain-terms AI disclosure, and add four items - private object storage plus complete deletion, subscription cancel parity, a trial-charge reminder (arms when ORB-138 applies the price), and no invented social proof (landing JSON-LD verified clean 2026-08-13). - audit-security checklist, section F: a Tier 1 item requiring a crisis response to a self-harm disclosure (closed by orbit-tickets#319). - product-manager agent: the eight-category edge-case pass is now a mandatory output, in fixed order, with silent categories forbidden; the ticket skill sends back a PM return that lacks the pass. Both harnesses pass: node tools/test-tools.mjs and node .claude/hooks/test-hooks.mjs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: act on the Pullfrog review of the intake-findings PR All four findings, all in the audit checklist, all accepted: - SB 243 is conditional context, never the severity basis; the crisis item's Tier 1 stands on Orbit's own product-safety policy. - Cancellation tests obstruction, not step parity; the Play Subscription Center link the app already opens is compliant. - The trial notice tests the duty that applies (trial length, jurisdiction, billing channel); provider-side Stripe delivery counts. - Social proof is scoped to the supplied audit roots; the landing repo and the Play listing go to the Deferred ledger instead of being reported clean from an empty local grep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: the trial notice needs proof of customer delivery, not the webhook Pullfrog round 2: customer.subscription.trial_will_end is the trigger for a reminder email, not the notice itself. The check now demands an enabled and sent customer reminder (Stripe or Orbit-sent); receiving or handling the event alone never satisfies it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
From Thomas's first pass over design/reference.html.
Settled and now in the spec:
spacing scale A, 0 4 8 12 16 24 32 48 64 96, plus two padding roles
(card 24, row 16) because a card that hugs its text reads as cramped
however correct the gaps around it are;
no serif, so the direction ADR's ban on a display serif as a second
warmth source stands unamended and D66 decision 9 resolves to "cut";
XP and levels render as a row, not a ring.
New: a hover and transition canon, which the spec required as a STATE but
never specified mechanically. A clickable thing with no hover state is now
a defect. 180ms for a surface, 120ms for a control, one step per hover,
never transform (that is press), and the transition is declared on the base
rule rather than inside :hover, which is what makes a state arrive smoothly
and then snap away.
The accent is reopened rather than assumed. The reference page now renders
TEN hues against BOTH fill treatments on real components: violet, indigo,
azure, space blue, teal, emerald, gold, amber, orange, rose. Every one
clears all three floors. Amber and rose are flagged: they sit inside 15
degrees of the overdue and bad status hues, so accent and status would read
as one colour. The light-fill anchor lightness is hand-tuned per hue,
because a single lightness across the wheel turns yellow to mustard while
leaving violet correct.
Verified in a real browser: all 20 hue/fill pairs resolve to their derived
values, hover transitions ease in AND out, zero page errors, zero network
requests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three defects Thomas found by hovering the rendered page. 1. Hover spilled. A card lit up whenever the pointer was on a button inside it, so the pointer appeared to be in two places. A container now suppresses its own hover while an interactive descendant is hovered, and static cards lost their hover state entirely: a surface that lights up under the pointer but cannot be clicked is an invitation to a dead click. 2. Hover was invisible. Stepping --bg-card to --bg-elev measures 1.09:1, which on a near-black canvas reads as nothing. The mistake was borrowing from the elevation ladder, whose steps are sized for STACKING rather than for being seen against one particular resting surface. --bg-hover is now its own role at 1.31:1, and the spec sets a 1.25:1 floor for any hover step. The three demo cards also described their behaviour instead of showing it, which broke the spec's own show-do-not-label rule; they now contain live rows and buttons. 3. Hover was too fast. Durations move from 120/180 to 180/280, and the page carries a live speed control (fast, medium, slow) so the final value is set by feel rather than guessed again. Accent shortlist narrowed to three, presented as presets: emerald dark with white, rose dark with white, orange light with canvas ink. Rose moved from hue 15 to 350: at 15 it sat 10 degrees from the destructive red, inside the band where two hues read as one, so the primary CTA and the delete button looked alike. At 350 it clears by 35 and stays a pink-red. Verified in a real browser: all three presets resolve to their derived values, all three speeds apply, hover no longer spills, static cards are inert, zero page errors, zero network requests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… left (#36) Thomas's second pass. The page now carries a control only for what is still open, which is the accent hue and the light/dark mode. Settled and folded in: ONE fill treatment, a dark fill with white on it. The light fill carrying the canvas ink was rendered and rejected by looking, so orange moves from #FF8E59 to #C4530F; the slowest of the three rendered hover speeds, 240ms on a control and 380ms on a surface; spacing A, so the B alternative is deleted rather than left switchable. Removed from the page: the fill selector, the speed selector, the spacing selector, and the nine-hue explorer. Six candidate hues are deleted outright rather than left as dead tokens. Also fixes a contradiction all three design specialists found independently: the spec reopened the accent hue but still locked "deep space-blue" in four places, including a Working model step reading "Do not re-pick" and a scene-sentence test that every non-blue screen would fail by construction. Those four now name a rationed accent without naming its hue. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…date (#36) Two decisions Thomas delegated, plus the brand comparison he asked to wake up to. DECISION 1: --status-done is unbound from --primary. It was var(--primary), so the brand colour and the completed state were one byte. An adversarial pass found that this broke three rules already written in this document: derivation rule 6 requires every status to sit more than 15 degrees from the accent and this sat at zero; the accent note says a static element rendered in the accent is as misleading as an interactive one rendered neutral, and a done ring is static; and "fill exactly one action per view" was broken six times over by a six-habit list with four done. Done now renders as an --fg-1 disc with a filled check, and the accent is reserved for what is NEXT and what is interactive. The neutral status ranking is done --fg-1, frozen --fg-2, skip --fg-3, empty --fg-4, so three neutral statuses can share one column and stay apart. The first attempt used --fg-2 for done, which collided with frozen, the exact failure that retired the cyan. This also shrinks orange's squeeze: the highest-frequency accent-versus- overdue adjacency, a done ring beside an overdue chip, no longer exists. DECISION 2: the mark carries the accent on exactly one element, its body, and that is the only non-state use of the accent in the system. The rationing list is amended to name it rather than leaving the mark out of a list that closes with "that is the whole list". The reference page gains a brand section: three columns, one per candidate, each showing the store icon at 200, a thumbnail row, both mark treatments, the lockup, the mark down to a natively redrawn 16px favicon, Astra beside the logo, and a mini app in context. The mark is a tilted ellipse with one body on the path, which is deliberately not the ProgressRing shape, so a logo is never read as a completion percentage. Astra keeps its centre core: anything with a core is Astra, anything empty in the middle is Orbit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…36) A completeness critic and a design reviewer, both fresh, were pointed at the page. The reviewer's closing line is the reason this commit exists: the page made the accent decision HARDER, because it was over-using the accent it was supposed to be auditioning, so anyone judging "does this hue feel rationed" was judging a canvas that had already broken the ration. Blockers fixed: every PRO / BETA / ACCENT badge was painted in the accent. A static label is on none of the six sanctioned surfaces, and DESIGN.md names this as the rule most often broken. Badges are now neutral; the Astra glyph spent the accent on three elements. The "one element inside the logo mark" exception is scoped to the Orbit mark and was never granted to Astra. Astra is now neutral and separates from the mark by its core alone. Also fixed: a streak total took accent text, but a streak is a record of what is finished; --selection-bg was a hardcoded cyan left over from the retired frozen hue and never repointed when the hue changed, which is the literal "no hardcoded accent rgba" ban, so it now derives from --hue-rgb; frozen and overdue rings carried state by ring colour alone with the glyph relocated to body text, so the glyph is back on the ring; a routine list-screen action was full-bleed outside the Buttons allowlist and used the over-wordy label the spec's own example calls out. From the completeness critic: the page named nine states but rendered no error state and no Field at all, so Field, OTP and Switch are added with default, focus, error and disabled; the listing spec was missing its 8-to-20 tier; the num type role was never shown; two unexplained row heights became --row-h and --row-h-compact; and font-size:17px was written three times where --fs-md already existed. The store icon radius is now a named exception in the spec rather than three off-scale literals: a platform icon mask is set by iOS and Android, not by the UI radius scale. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nds (#36) The completeness critic's headline finding was that roughly 5 of the 19 primitives in the kit table had any representation at all. That matters for this decision specifically: the FAB, the active tab and the progress ring are three of the six sanctioned accent surfaces, and none of them was on the page, so the accent was being judged on a swatch rather than on the surfaces that carry it. Added: TabBar with the active tab, FAB, a true ProgressRing at 0, 62 and 100 percent, NavHeader, SectionTitle, SettingsGroup with the rule between rows and none after the last, StatTile, InfoCard in both tones, PlanCard with exactly one recommended, and the Satellite empty-state glyph. The ProgressRing being on the page also makes the mark's geometry argument visible rather than asserted: the ring is a true circle with a sweep over a track, which is why the logo is a tilted ellipse with a solid body and can never be read as a completion percentage. Verified across every hue and both modes: active-tab accent text clears 4.5 in all six combinations (4.52 to 4.58), and white on the FAB clears 4.5 in all six (4.52 to 4.57). Zero page errors, zero network requests, no literal outside the single :root block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thomas discarded emerald on 2026-08-15. Green binds the brand to a success verdict rather than to a state, and it places Orbit in the green-checkmark habit-tracker slot that BRAND.md names as a positioning failure. Two candidates remain, both as a dark fill with white on it: warm orange around #C4530F, and rose #BF4D8A at hue 350. Neighbouring warm-orange values are still open. The exact byte is no longer settled here. It moves to the Claude Design canvas, which carries its own colour controls, and is written back into DESIGN.md once chosen. design/reference.html has done its job: it narrowed ten hues and two fill treatments down to two candidates and one treatment, and it settled spacing, the serif, the hover canon and the done unbinding along the way. The page now opens on orange, and both the picker and the brand grid carry two columns rather than three. The habit-colour palette keeps its own emerald, which is a habit colour and unrelated to the accent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four findings, all real. P1, the drift workflow was inert. GitHub discovers push workflows from the TRIGGERING ref, and redesign-drift.yml existed only on redesign/main, so a push to main had no workflow on main to evaluate and the job could never start. It would have sat there looking like drift control while doing nothing for the whole redesign, which is exactly when it is needed. Removed from this PR; it needs its own pull request into main to ever fire. The expanded kit broke the accent allowlist two commits after that allowlist was written. A 100% ProgressRing was rendered entirely in the accent, which is precisely what "the accent never marks completion" forbids, and InfoCard, the selected PlanCard and the Satellite arc all painted the accent while sitting outside the closed six-item list. The list itself was the deeper defect: it enumerated COMPONENTS and then declared itself exhaustive, so it silently contradicted the primitives table, which separately assigns the accent to those same three. The rationing rule is now stated as four ROLES rather than six components: the next action, current position, progress toward something unfinished, and one element inside the logo mark. That is genuinely exhaustive, it covers the primitives table without special cases, and it stays restrictive. The 100% ring now goes neutral, and each specimen names the role it is exercising. The switch specimens published a broken accessibility contract: role=switch with no accessible name, and Space did nothing. They are real buttons now, named by aria-labelledby, with a handler that actually flips aria-checked. Also fixed a regex in my own repair that over-reached and neutralised all three progress rings instead of only the completed one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pullfrog's remaining finding, and it is right. The previous commit labelled the accent InfoCard a "selected state" so that it fit the four-role allowlist, but nothing is selected: it is a static informational callout. That is rationalising a specimen to fit the rule instead of obeying it, and the rule already says the accent is never decorative on a card. So the tone is deleted rather than a fifth role invented. InfoCard has one tone, --bg-elev with an fg-3 icon, in both the spec's primitive table and the rendered page. Removing a role keeps the accent nearer zero, which is the direction, and it means the four roles stay genuinely exhaustive rather than growing a carve-out every time a specimen does not fit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
) (#735) * feat: rewrite DESIGN.md against D66 and ship the rendered reference (#36) Ticket 0 of the redesign. Rewrites the spec against D66 and builds the page that outranks it (D42), so the direction can be approved by looking. Tokens are derived in OKLCH and measured, not picked: --bg #09090B, --primary #24B4E2 (ink on it 8.35:1, on canvas 8.24:1), --fg-4 raised to #5D5D60 so it clears the 3:1 non-text floor. Three findings the derivation forced: white on the new accent is 2.41:1, so --fg-on-primary is the canvas ink; --status-frozen sat 12 degrees from the accent, inside the 15 degree "same colour" band, so it is retired as a hue and rendered as a neutral chip plus the snowflake glyph; the surface ladder was too tight at the dark end and is widened. Overlay: the primitive is specified with a named library per platform. Web adopts @base-ui/react 1.7.0 Dialog; the hand-rolled app-overlay renders <dialog open> without showModal(), which is why it rebuilds the focus trap, the scroll lock and Escape by hand and needs a z-[9999] that breaks the repo's own ban. Mobile keeps TrueSheet: the library is not the defect, the wrapper is, because it stacks a ScrollView inside the native scrollable and defaults to fixed detents. Also corrects the icon spec to Tabler's native 24 grid (22 rendered soft), reserves the pill radius for interactive elements so a Badge stops collecting dead clicks, adds the listing spec, the ninth "at capacity" state, and an enumerated 25 entry banned-word set scoped per entry. design/handoff/ is deleted. Its README claimed to be the design source of truth and said "when in doubt, the files below win", which outranked the spec under D42 while encoding six schemes and the old direction. The eslint-rules re-derivation and the spacing codification follow grant 1, because D66 decision 12 sets the step values against the rendered page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: fold in the grant-1 decisions and add the hover canon (#36) From Thomas's first pass over design/reference.html. Settled and now in the spec: spacing scale A, 0 4 8 12 16 24 32 48 64 96, plus two padding roles (card 24, row 16) because a card that hugs its text reads as cramped however correct the gaps around it are; no serif, so the direction ADR's ban on a display serif as a second warmth source stands unamended and D66 decision 9 resolves to "cut"; XP and levels render as a row, not a ring. New: a hover and transition canon, which the spec required as a STATE but never specified mechanically. A clickable thing with no hover state is now a defect. 180ms for a surface, 120ms for a control, one step per hover, never transform (that is press), and the transition is declared on the base rule rather than inside :hover, which is what makes a state arrive smoothly and then snap away. The accent is reopened rather than assumed. The reference page now renders TEN hues against BOTH fill treatments on real components: violet, indigo, azure, space blue, teal, emerald, gold, amber, orange, rose. Every one clears all three floors. Amber and rose are flagged: they sit inside 15 degrees of the overdue and bad status hues, so accent and status would read as one colour. The light-fill anchor lightness is hand-tuned per hue, because a single lightness across the wheel turns yellow to mustard while leaving violet correct. Verified in a real browser: all 20 hue/fill pairs resolve to their derived values, hover transitions ease in AND out, zero page errors, zero network requests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: hover that reads, no spill, and the three-scheme shortlist (#36) Three defects Thomas found by hovering the rendered page. 1. Hover spilled. A card lit up whenever the pointer was on a button inside it, so the pointer appeared to be in two places. A container now suppresses its own hover while an interactive descendant is hovered, and static cards lost their hover state entirely: a surface that lights up under the pointer but cannot be clicked is an invitation to a dead click. 2. Hover was invisible. Stepping --bg-card to --bg-elev measures 1.09:1, which on a near-black canvas reads as nothing. The mistake was borrowing from the elevation ladder, whose steps are sized for STACKING rather than for being seen against one particular resting surface. --bg-hover is now its own role at 1.31:1, and the spec sets a 1.25:1 floor for any hover step. The three demo cards also described their behaviour instead of showing it, which broke the spec's own show-do-not-label rule; they now contain live rows and buttons. 3. Hover was too fast. Durations move from 120/180 to 180/280, and the page carries a live speed control (fast, medium, slow) so the final value is set by feel rather than guessed again. Accent shortlist narrowed to three, presented as presets: emerald dark with white, rose dark with white, orange light with canvas ink. Rose moved from hue 15 to 350: at 15 it sat 10 degrees from the destructive red, inside the band where two hues read as one, so the primary CTA and the delete button looked alike. At 350 it clears by 35 and stays a pink-red. Verified in a real browser: all three presets resolve to their derived values, all three speeds apply, hover no longer spills, static cards are inert, zero page errors, zero network requests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: settle the fill, the hover speed and the spacing; three accents left (#36) Thomas's second pass. The page now carries a control only for what is still open, which is the accent hue and the light/dark mode. Settled and folded in: ONE fill treatment, a dark fill with white on it. The light fill carrying the canvas ink was rendered and rejected by looking, so orange moves from #FF8E59 to #C4530F; the slowest of the three rendered hover speeds, 240ms on a control and 380ms on a surface; spacing A, so the B alternative is deleted rather than left switchable. Removed from the page: the fill selector, the speed selector, the spacing selector, and the nine-hue explorer. Six candidate hues are deleted outright rather than left as dead tokens. Also fixes a contradiction all three design specialists found independently: the spec reopened the accent hue but still locked "deep space-blue" in four places, including a Working model step reading "Do not re-pick" and a scene-sentence test that every non-blue screen would fail by construction. Those four now name a rationed accent without naming its hue. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: unbind done from the accent, and render the brand in each candidate (#36) Two decisions Thomas delegated, plus the brand comparison he asked to wake up to. DECISION 1: --status-done is unbound from --primary. It was var(--primary), so the brand colour and the completed state were one byte. An adversarial pass found that this broke three rules already written in this document: derivation rule 6 requires every status to sit more than 15 degrees from the accent and this sat at zero; the accent note says a static element rendered in the accent is as misleading as an interactive one rendered neutral, and a done ring is static; and "fill exactly one action per view" was broken six times over by a six-habit list with four done. Done now renders as an --fg-1 disc with a filled check, and the accent is reserved for what is NEXT and what is interactive. The neutral status ranking is done --fg-1, frozen --fg-2, skip --fg-3, empty --fg-4, so three neutral statuses can share one column and stay apart. The first attempt used --fg-2 for done, which collided with frozen, the exact failure that retired the cyan. This also shrinks orange's squeeze: the highest-frequency accent-versus- overdue adjacency, a done ring beside an overdue chip, no longer exists. DECISION 2: the mark carries the accent on exactly one element, its body, and that is the only non-state use of the accent in the system. The rationing list is amended to name it rather than leaving the mark out of a list that closes with "that is the whole list". The reference page gains a brand section: three columns, one per candidate, each showing the store icon at 200, a thumbnail row, both mark treatments, the lockup, the mark down to a natively redrawn 16px favicon, Astra beside the logo, and a mini app in context. The mark is a tilted ellipse with one body on the path, which is deliberately not the ProgressRing shape, so a logo is never read as a completion percentage. Astra keeps its centre core: anything with a core is Astra, anything empty in the middle is Orbit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: stop the reference page over-using the accent it is auditioning (#36) A completeness critic and a design reviewer, both fresh, were pointed at the page. The reviewer's closing line is the reason this commit exists: the page made the accent decision HARDER, because it was over-using the accent it was supposed to be auditioning, so anyone judging "does this hue feel rationed" was judging a canvas that had already broken the ration. Blockers fixed: every PRO / BETA / ACCENT badge was painted in the accent. A static label is on none of the six sanctioned surfaces, and DESIGN.md names this as the rule most often broken. Badges are now neutral; the Astra glyph spent the accent on three elements. The "one element inside the logo mark" exception is scoped to the Orbit mark and was never granted to Astra. Astra is now neutral and separates from the mark by its core alone. Also fixed: a streak total took accent text, but a streak is a record of what is finished; --selection-bg was a hardcoded cyan left over from the retired frozen hue and never repointed when the hue changed, which is the literal "no hardcoded accent rgba" ban, so it now derives from --hue-rgb; frozen and overdue rings carried state by ring colour alone with the glyph relocated to body text, so the glyph is back on the ring; a routine list-screen action was full-bleed outside the Buttons allowlist and used the over-wordy label the spec's own example calls out. From the completeness critic: the page named nine states but rendered no error state and no Field at all, so Field, OTP and Switch are added with default, focus, error and disabled; the listing spec was missing its 8-to-20 tier; the num type role was never shown; two unexplained row heights became --row-h and --row-h-compact; and font-size:17px was written three times where --fs-md already existed. The store icon radius is now a named exception in the spec rather than three off-scale literals: a platform icon mask is set by iOS and Android, not by the UI radius scale. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: render the rest of the kit, so the accent is judged where it lands (#36) The completeness critic's headline finding was that roughly 5 of the 19 primitives in the kit table had any representation at all. That matters for this decision specifically: the FAB, the active tab and the progress ring are three of the six sanctioned accent surfaces, and none of them was on the page, so the accent was being judged on a swatch rather than on the surfaces that carry it. Added: TabBar with the active tab, FAB, a true ProgressRing at 0, 62 and 100 percent, NavHeader, SectionTitle, SettingsGroup with the rule between rows and none after the last, StatTile, InfoCard in both tones, PlanCard with exactly one recommended, and the Satellite empty-state glyph. The ProgressRing being on the page also makes the mark's geometry argument visible rather than asserted: the ring is a true circle with a sweep over a track, which is why the logo is a tilted ellipse with a solid body and can never be read as a completion percentage. Verified across every hue and both modes: active-tab accent text clears 4.5 in all six combinations (4.52 to 4.58), and white on the FAB clears 4.5 in all six (4.52 to 4.57). Zero page errors, zero network requests, no literal outside the single :root block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: discard emerald, narrow the accent to warm orange or rose (#36) Thomas discarded emerald on 2026-08-15. Green binds the brand to a success verdict rather than to a state, and it places Orbit in the green-checkmark habit-tracker slot that BRAND.md names as a positioning failure. Two candidates remain, both as a dark fill with white on it: warm orange around #C4530F, and rose #BF4D8A at hue 350. Neighbouring warm-orange values are still open. The exact byte is no longer settled here. It moves to the Claude Design canvas, which carries its own colour controls, and is written back into DESIGN.md once chosen. design/reference.html has done its job: it narrowed ten hues and two fill treatments down to two candidates and one treatment, and it settled spacing, the serif, the hover canon and the done unbinding along the way. The page now opens on orange, and both the picker and the brand grid carry two columns rather than three. The habit-colour palette keeps its own emerald, which is a habit colour and unrelated to the accent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: clear the Pullfrog review on 8ec2b90 (#36) Four findings, all real. P1, the drift workflow was inert. GitHub discovers push workflows from the TRIGGERING ref, and redesign-drift.yml existed only on redesign/main, so a push to main had no workflow on main to evaluate and the job could never start. It would have sat there looking like drift control while doing nothing for the whole redesign, which is exactly when it is needed. Removed from this PR; it needs its own pull request into main to ever fire. The expanded kit broke the accent allowlist two commits after that allowlist was written. A 100% ProgressRing was rendered entirely in the accent, which is precisely what "the accent never marks completion" forbids, and InfoCard, the selected PlanCard and the Satellite arc all painted the accent while sitting outside the closed six-item list. The list itself was the deeper defect: it enumerated COMPONENTS and then declared itself exhaustive, so it silently contradicted the primitives table, which separately assigns the accent to those same three. The rationing rule is now stated as four ROLES rather than six components: the next action, current position, progress toward something unfinished, and one element inside the logo mark. That is genuinely exhaustive, it covers the primitives table without special cases, and it stays restrictive. The 100% ring now goes neutral, and each specimen names the role it is exercising. The switch specimens published a broken accessibility contract: role=switch with no accessible name, and Space did nothing. They are real buttons now, named by aria-labelledby, with a handler that actually flips aria-checked. Also fixed a regex in my own repair that over-reached and neutralised all three progress rings instead of only the completed one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: delete the InfoCard accent tone rather than rationalise it (#36) Pullfrog's remaining finding, and it is right. The previous commit labelled the accent InfoCard a "selected state" so that it fit the four-role allowlist, but nothing is selected: it is a static informational callout. That is rationalising a specimen to fit the rule instead of obeying it, and the rule already says the accent is never decorative on a card. So the tone is deleted rather than a fifth role invented. InfoCard has one tone, --bg-elev with an fg-3 icon, in both the spec's primitive table and the rendered page. Removing a role keeps the accent nearer zero, which is the direction, and it means the four roles stay genuinely exhaustive rather than growing a carve-out every time a specimen does not fit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Everything needed to build the Orbit design system inside Claude Design, plus the spec gap that work exposed. design/brand/SETUP.md carries the five setup fields filled and pasteable, and the by-hand import checklist. There is no documented token export from Claude Design, so that checklist is the only gate between the import and every screen built on it. design/brand/ extracts the mark, the 16 native redraw, the Astra glyph, the lockup and the platform icon out of design/reference.html as real files, value for value, in both accent candidates. The three type families come from github.com/google/fonts as full variable TTFs with their licences: the woff2 embedded in reference.html are subsets and would break pt-BR copy. The mark is a DRAFT. It exists so the accent is decidable against a real identity surface. Ticket #79 owns the real mark. DESIGN.md gains the light-mode status values, which existed only in reference.html. That omission was load-bearing: both status hues darken to clear the floor on #FAFAFA, which drags overdue to hue 54.5 and bad to 28.5 and fills the warm band. A hue 45 warm-orange accent lands 9.8 degrees from light overdue, inside derivation rule 6's 15-degree band, while rose at 350 clears by 64.6. The accent had only ever been measured against the dark status values. Also records --fg-on-overdue as #FFFFFF in light mode. reference.html never repoints it, so it inherits #020618 at 4.26:1 on #B45B00 and misses the 4.5 floor. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first screen of the canvas-first redesign is Today, the habit list, because it is the core loop. The prompt composes the exported design system rather than describing a look: it names the components to use, the frame set (412px shell and the wide layout, both modes), the nine states, the accent budget of three instances, the motion subtraction on a 100-per-day interaction, and the checks the canvas reports back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Batch 1 of Today shipped and works, so the twelve frame prompt shape is retired. The matrix now comes from one interactive document switched by a control bar, which is how design/reference.html already works. - wave0-design-system.prompt.md builds what every screen would otherwise hand roll: Shell412 with a real scroll container, ShellWide, a depth prop on HabitRow, the skeleton, error and capacity states, the canvas control bar, and guidelines/screen-contract.md, which carries the invariant canon so no screen prompt repeats it. - _screen-template.md is the five slot per screen prompt. - today.prompt.md becomes a batch 2 continuation: it repairs the growing frame, the scrolling FAB, the sub rows that reach into HabitRow's private classes and the inconsistent mock marking, then adds the wide layout, the four data states, the listing thresholds and the locale pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All six wave 0 items landed and the manifest shows zero tokens added. Five defects came with them: CanvasControls hardcodes its state options so no screen can add one, nothing in the system honours reduced motion so every screen patches it locally, Skeleton loops a pulse with no guard, Shell412 hardcodes the tab bar height in the FAB offset and applies its bottom padding per child, and ShellWide defaults the account prop to a real name and hardcodes the mac palette glyph. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Derived from tools/redesign-coverage.mjs groups rather than guessed, so the queue is checkable against the 195 surface inventory. The m- mirrored surfaces are covered by the width axis, so they are not separate documents. Five waves, one prompt block each, every block built on the five slot template and leaning on guidelines/screen-contract.md for the canon. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Records what was actually built and verified rather than what the canvas reported, plus the four operating facts learned live: the per project design system snapshot needs refreshing after a system change, a finished document can render blank until reload, the first click into the canvas only focuses the iframe, and a browser batch over about 60 seconds times out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eight documents now exist: Today, habit detail, habit form, calendar, goals, goal detail, insights, retrospective. Habit detail and calendar were checked by rendering them; the rest wait for the export pass. Adds the three operating facts learned since: the canvas builds one document per turn so a wave needs nudges, a send is confirmed by an empty composer rather than by the click, and switching documents in the app renders them without disturbing a running generation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Waves 1 to 4 landed: Today rebuilt on the real shells, habit detail, habit form, calendar, goals, goal detail, insights, retrospective, Astra conversation, Astra cards, onboarding and auth. Each is one interactive document carrying the mode, width, state and locale axes, per the screen contract in the design system. Wave 5's six documents are blocked on the account usage limit, which resets Thursday 20 August. The canvas has them queued as a todo list and reports the work so far as saved. Mechanically checked at export: no em or en dash, no raw hex outside a var() fallback, no gradient, no blur or glass, no transition all, no sparkle, no arbitrary z index, no off scale radius, and every document composes Shell412, ShellWide and CanvasControls and marks its numbers with data-mock. One real defect survives, a gap:2 in the onboarding document, recorded in the run log to be fixed on the canvas rather than in the export. The canvas runtime support.js is not committed: it is vendor code and the dash gate rejects it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#36) The twelve canvas documents came back token-correct and product-wrong: they reskin a habit tracker with a chat tab, which BRAND.md names as a failure of the positioning. The cause was the prompts, which described the content of each screen as it exists today. DESIGN.md gains a ## Information architecture section, read first, saying what each surface IS and outranking every other section on whether a surface should exist. It carries the six generative-block rules, the tenth "proposed" component state, and the ban on rendering the four internal schedule type names. The shell divergence list drops from four to three in DESIGN.md and CLAUDE.md: the desktop stats rail is deleted. design/prompts/waves.md is rewritten from scratch. Every prompt now states the job of the screen and what it must not become. Orbit Insights.dc.html is deleted, since the route is cut. The eleven survivors are kept as a record, not as a target. Decisions recorded in the brain vault as D67 and D68. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The information architecture makes a goal with linked habits derive its progress from real logs. That value is neither typed nor proposed: it is never accepted and never edited, it recomputes, so the proposed state would say the wrong thing about it. No eleventh state. A derived value renders like a typed one, its manual input is hidden rather than disabled, and the surface names what it is derived from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tools/redesign-coverage.mjs validates against the live surface manifest, so its 195 surfaces are correct today and go stale only as the deletions land. Editing it ahead of them would fail its own validation. Names the five groups that shrink, and that Progresso is absent because it does not exist yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Granted by Thomas 2026-08-16. Rose is retired, emerald was discarded earlier the same week, and there is no shortlist any more. Nine tokens read PENDING GRANT 1 and now carry bytes. primary-pressed, primary-hover and primary-dim are derived in OKLab from the fill and written as resolved hex, because React Native parses no oklch and no color-mix. Also takes the canvas design system's correction to light-mode overdue. DESIGN.md had #B45B00 at hue 54.5, which sits 9.8 degrees from a hue-45 accent, inside the 15-degree band its own derivation rule 6 forbids. It is now #946A00 at hue 81.2, clearing the accent by 36.4 degrees at 4.66:1. The spec was wrong and the canvas was right. Every enforcement row gated on grant 1 is unblocked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
design/reference.html was the artefact for choosing the accent and still rendered both candidates. Under D42 a rendered page outranks prose, so a page showing a retired option outranks the grant. It now carries a banner naming the granted accent and marking every rose swatch historical, plus the superseded light-mode overdue value. design/brand/SETUP.md defaulted to rose and told the reader to swap for orange. 23 references resolved. RUN-LOG.md carried a false operating fact: that the Claude Design usage limit is account wide and not per model. Read live from the usage panel on 2026-08-16 there are three buckets, session, all models and Fable, at 1, 72 and 96 percent. What stopped wave 5 was the session bucket. The four-day wait it implied is removed from waves.md too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The one open question wave 1 left for Thomas, answered 2026-08-16. The slot at the top of Hoje carries the single most relevant thing Astra noticed and advances when the person acts on it. No dismiss control, because the audience is already overwhelmed and a dismiss control makes the top of the busiest screen one more chore. No persistence, because a line whose action the person will not take parks there and stops being read. When Astra noticed nothing the slot is absent rather than empty. Wave 0 also landed on the Claude Design canvas in this session: Composer, Proposed and BlockFrame, with two specimen cards and an updated readme. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The DesignSync write API writes sources but never rebuilds _ds_bundle.js or _ds_manifest.json, which are the two artifacts the Design System pane reads. After wave 0 and wave 1 landed, three new cards were absent from the pane and two cards whose files were deleted still rendered "file not found". unregister_assets returned unregistered: 2 and changed nothing. build-ds-bundle.mjs emits both in the app's own output shape, read out of a project export rather than guessed: imports stripped, JSX transformed, the export keyword removed because each block runs in an IIFE, sibling references rewritten to __ds_scope.X, and the card index rebuilt from the first-line @DSCARD marker of every html that still exists. Verified by executing the emitted bundle in a vm context with a stubbed React and document: __errors empty, 35 components exported, Composer, BlockFrame and Shell412 all render. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first rebuild carried the old manifest's `tokens` array through unchanged, so the eight --p-hab-* habit-palette swatches survived in the Design System pane hours after they left tokens/colors.css. Colour-as-data being dead is one of this redesign's load-bearing decisions, and the pane was still showing the palette. The builder now drops any token whose own definedIn file no longer defines it. 165 to 157, the eight --p-hab-* entries and nothing else. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#36) Drawn on the canvas, the composer in the web sidebar is wrong twice over. A 232px rail cannot hold an input, 3 to 6 chips and a send control without every one of them dropping under its own minimum, and putting Astra's front door somewhere the mobile build has no equivalent for manufactures a divergence the parity contract does not allow. The sidebar keeps navigation and identity only. Also records that focus is not an affordance: the Astra glyph at the head of the composer becomes a real button, 44px, hover and focus ring, labelled Abrir conversa, so a person who never types into the bar can still find the way in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#36) Wave 1 put a whole Hoje screen and a whole conversation, with named habits and real messages, into a design system card. That is a product documenting itself rather than a kit, and it made the system unreadable as a system. The rule, written here so it is not learned twice: the design system holds the reusable parts only, and a specimen shows a component and its states, never the app. A shell stays a legitimate system member but is documented as structural anatomy, every region a labelled empty slot with its name and size rule. The same rule kills invented product data anywhere in the system, including the fake Perfil screen that sat behind the sheet specimen's scrim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#36) Two derivation defects, both found by drawing the system rather than reading it. 1. --primary-hover was the fill mixed 12 percent toward white, #CD6939. White on #CD6939 measures 3.70:1, under the 4.5 text floor, on the primary button, which always carries a white label. The fill itself is only 4.57:1 with white, so there is no headroom to lighten it by any amount. Hover now darkens instead, #B74E12, and the ladder reads monotonically: rest 4.57, hover 5.11, pressed 6.09. The one-step hover rule had been applied without measuring the step. 2. Light mode never repointed --primary-dim, so it inherited #261611, the dark mode value, which is a near-black wash painted onto a white card. That is what a selected PlanCard rendered in light. Light now carries #F4DDD3, the fill at 18 percent over #FAFAFA, with fg-1 on it at 13.34:1. Recorded alongside which accent tokens are mode-independent and why: the fill is dark and its label white in both variants, so only the mix WITH the canvas moves. Values computed in OKLab; the implementation reproduces the three published dark values exactly, which is what makes the new ones trustworthy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The palette was only ever measured against the canvas, and every miss is on the surfaces above it. Measured against the whole ladder, with two independent implementations agreeing on every number. Closed by rule: --primary-soft reads 4.28 on a card, 3.91 in an overlay and 3.26 on a hovered surface. That is not a colour to change, it is the token used outside its own scope, since it is already defined as accent text on the canvas. Accent text is now explicitly canvas only, and emphasis on a raised surface is a weight step rather than a hue. Left open, with numbers, because each trades against a rule set elsewhere in the document and the call is Thomas's: fg-3 at 4.40 on a hovered row, fg-4 between 2.16 and 2.84 as a graphic above the canvas, and light fg-4 at 2.94 on hover. fg-4 is specified at exactly the 3:1 non-text floor and only reaches it on the canvas alone, so the empty StatusRing is under the floor everywhere it actually renders. Both cheap fixes cost something real: one moves the surface ladder, the other collapses the four-step neutral status ranking to three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six prompt rounds on the canvas, the defects they found, the two colour derivation bugs that only appeared once light mode was actually drawn, and the operating facts worth keeping: the write API never rebuilds the two artifacts the Design System pane reads, a browser-sent prompt can be silently truncated, and a reload fixes a wedged viewport. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
) Thomas reported misalignments. Read from the export: all 12 rewritten cards put marginBottom:16 on the mode label, which is the sibling margin DESIGN.md bans outright, and it is the misalignment because it does not participate in the column's gap. The panel is now a flex column with gap:16. gap:20 in the generative card became 24, since 20 is not on the scale and the system's own Spacing scale card says so in words. The shell diagram's two margin insets became padding on a wrapper, and its 14px padding became 12. Every rewritten card re-parsed through Babel: 12 checked, 0 failing. Also recorded: check_design_system passed clean through all of this, because it validates tokens and manifest sync and never reads the specimen harness. And hover is correct in the source, 21 hover media blocks compile into the bundle with the only pointer-events:none sitting on a decorative pseudo-element, so it cannot be judged in a pane that overlays each card to make it clickable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
design/prompts/screens.md holds the operating file: a standing brief and seven screen briefs written to be copied into the screens project one per turn, with nothing left to compose. waves.md keeps the reasoning and now points at it. Each brief states what the screen is FOR and what it must not become, and never enumerates what is on the screen today, which is the failure that produced twelve faithful reskins the first time. Every settled decision is carried in the standing brief: the composer at the bottom of the content column on both platforms, the Astra glyph as a real front door, the proactive line replacing itself, hover darkening the accent fill, primary-soft canvas only, the deleted surfaces, the daily AI allowance, the live prices, and the three contrast limits it must not try to fix. Verified against the screens project rather than assumed: its pinned copy of the design system is current, carrying the 2026-08-17 tokens, so no refresh is needed before drawing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
screens.md was written from DESIGN.md alone, without checking one claim against the API, and it told the canvas to draw a habit in a frozen state. StreakFreeze is (UserId, UsedOnDate), a user-day; Habit has no freeze or pause member at all. So the prompt asked for a state the product cannot produce and the canvas drew it. NEXT-SESSION.md makes that class of mistake structural rather than lucky: research both repos, the vault decision register, and all 32 open redesign tickets with their comments; build a per-surface state inventory where every state names the code that produces it; ask Thomas everything in one batch; only then rewrite screens.md. A state that cannot be traced to code does not go in a prompt. It also carries the four defects found on the first Hoje, diagnosed and unfixed: Icon is not a self-centring square so tab bar glyphs read as shifted left, StatusRing uses emoji and punctuation as iconography against the Tabler-only rule, frozen is offered on habit-shaped components, and EmptyState still hand-draws the tilted-ellipse mark that was retired on 2026-08-16. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…36) Phase 1 is 32 tickets with comments plus two repos plus the decision register. Read inline that fills the context and reproduces the bloat that ended the last session; a workflow returns conclusions and leaves the dumps outside. It also gives the research an adversarial stage whose only job is to falsify each traced state, which is the failure mode this handoff exists to fix. Phase 3 stays solo and single-voiced. Eight prompts written by eight agents give eight registers, and the consistent register is most of that file's value. Effort stays at high: the previous session reasoned plenty and simply did not run the grep, so depth was never the bottleneck. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…at lied (#36) The first composed screen drew a habit in a "frozen" state. No such thing exists: StreakFreeze is (UserId, UsedOnDate), so a freeze marks a DAY for a USER, and Habit carries no freeze member. The prompt was written from a document instead of from source, so the canvas drew a state the product cannot produce. A 14 agent research pass traced every renderable state to the code that produces it, then tried to falsify each one. It found two more states of the same shape already sitting in screens.md, and it found the documents that authorised them. screens.md, rewritten: - every state named is traced to code, and three that are not are banned by name: a frozen habit, a skipped habit row, an archived habit with a restore - a standing rule that the canvas must stop rather than invent a state - a decided-versus-shipping convention, so a prompt draws the decided target and names the live value instead of silently mixing them - twelve pastes, up from eight, covering the surfaces that had none: bulk select, notifications, the upgrade manage view, search, step up, Wrapped, the static screens and the Android widget - paste D, which repairs the four design system defects found on the first Hoje DESIGN.md, corrected where it contradicted source or itself: - the HabitRow primitive and the habit list rules no longer specify frozen or skip as row statuses, and say why - the /insights figures fold into Progresso, not into a streak surface that is no longer a destination - the open contrast limit named --p-hover, which is not a token. It is --bg-hover - the mark and glyph are described from the final assets, and are told apart by silhouette rather than by the retired draft geometry's core - the empty state is the real OrbitMark, not a generic Satellite glyph, and accent role 1 no longer names an arc that does not exist Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
) The canvas run is complete. The screens project holds 20 documents and 21 pages, all new canon, and no document survives from the first run. Screens built: Hoje, habit create, habit detail, Calendario, Progresso, the Astra conversation, onboarding, auth, Perfil, notifications, the Pro pitch, the subscription manage view, the surviving celebration, the error and static surfaces, offline, search, step up, the overlay primitives, Wrapped, the four static screens, and the Android widget. Three design system rounds ran alongside the screens, each from gaps a build reported rather than from a guess: - D the four defects found on the first Hoje: icon centring, emoji used as iconography, frozen and skip as habit statuses, and a hand drawn mark - D2 DayStrip, Checkbox, a shell header slot, TimeField, StatTile states, Menu - D3 BlockFrame's control, proposed and irreversible rows plus a risk slot, Composer's busy state and its discriminated atLimit, and an authorable conversation slot Two of those are enforced by contract rather than by prose, because the same bug came back twice under different disguises. DayStrip discriminates on scope, so passing frozen to a habit strip is a type error. Composer discriminates on state, so atLimit cannot render without its message and no other state can pass one. Six open questions were put to Thomas and answered: a rejected preview collapses to one line, step up is a hand off, a partially failed bulk create keeps what it created, the day arc is the exact fraction, Calendario pages on an empty account, and the month rate has a definition. DESIGN.md gains that definition, because Calendario computed it and correctly flagged that no endpoint states the window, which means the next surface would compute a different number from the same data. Four API tickets were filed from what the screens reported they needed: #331 the streak repair endpoint, #332 the achievements payload, #333 the Astra metrics schema, #334 the notification urls. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cut (#36) The canvas built Wrapped without a share action and justified it: "the social layer is deleted, and a share sheet would be the one affordance here that leads outside a product that has nowhere outside to lead." That conflates two different things. The social layer is internal, meaning friends, cheers, challenges and accountability pairs, and D69 deleted it. Sharing outward was never deleted, and it is the whole reason Wrapped was kept: the growth research of 2026-06-18 calls the shareable recap card the minimum viable viral loop, and the debloat plan of 2026-08-05 keeps Wrapped for exactly that word. It also already ships on both platforms, which settles the argument against the code rather than against the vault. use-share-card.ts renders the card client side with toBlob at pixelRatio 3 into orbit-recap.png, calls navigator.share, and fires card_shared, which AchievementEventMap whitelists and which grants the show_off achievement. There is somewhere outside to lead, and the path is built. Wrapped now carries ten states: the five pages, reduced motion, a thin period, and the three share states. The shared artefact is one composed 9 by 16 image that carries no controls, because it becomes a PNG. How Wrapped is reached is settled with it: the notification for the closed period is the primary way in, matching how the periodic retrospective is already delivered, and Progresso carries an entry for the period that just closed as the fallback. Neither is drawn as a nav row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he five missing surfaces (#36) The canvas is the design of record now, and nothing said so where a worker would read it. /orchestrate reads the ticket, not the project. Twenty one existing tickets gained a comment naming the document that defines their surface. Their bodies were already corrected against D69 on 2026-08-16, so the comment adds the drawing rather than restating the job, and says plainly that the document is the later artefact where the two differ. Five surfaces gained a document in this run and had no ticket at all: #335 the notification bell and its list Orbit Avisos #336 search results, including which matched Orbit Busca #337 the step up code screen Orbit Verificacao #338 the error and static surfaces Orbit Estados #339 offline, and the dropped change Orbit Offline #335 is ordered behind #334, because rewriting the client while the server still writes /streak leaves the two disagreeing. #336 needs no API work at all: the server already returns searchMatches and computeHabitMatchBadges has zero consumers, so the app has been throwing away a better result than it draws. D4, the fourth system round, landed five of nine. The canvas read the whole brief, judged it lacked the headroom to build nine components plus their cards without leaving the system half edited, and wrote nothing that round rather than risk it. Sheet's mount contract, CapacityNotice's body, the Skeleton grid variant, the ListRow read only variant and EventRow are done. DayCell and MonthGrid, OtpInput, Pager and Columns stay on its todo list. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The canvas is complete at 20 documents and Thomas has not reviewed it screen by screen. The weekly Claude Design limit reached 90 percent before he could, and the canvas itself refused to start a nine component build at that level. The handoff carries three phases. Finish the four components D4 could not afford (DayCell and MonthGrid, OtpInput, Pager, Columns) and rewire the two screens still composing them by hand. Then run the review as a loop, one screen per round, reading each document through the MCP rather than spending a canvas turn, telling Thomas the calls he might disagree with, and turning his answer into one corrective paste. Then the API tickets, which block what the screens drew. It also carries the operational traps this session paid for: the canvas page has two contenteditable elements and pasting into the wrong one edits the project readme; the composer sends on Enter so multi line text needs a synthetic paste event; anything over about 5,000 characters becomes an attachment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… it does not hold (#36) Two additions to the handoff. Phase 0 lists the exact notes to read before touching anything: D69, D70, the price ADR, direction 3, the register, D54, plus the growth research, the debloat plan, the Engineering MOC and hot.md. The growth research earns its place because it is the only note that says WHY Wrapped exists, and this session shipped Wrapped without its share action by reasoning from the deleted social layer instead of from that note. Phase 0b records that the vault does not know about 2026-08-18 at all: a grep for that date over the MOC and hot.md returns nothing, so a session reading only the vault would not know the redesign is drawn. It names the five things to write back: the run completing, the six product questions answered that today live only in screens.md, the two route decisions, the Wrapped correction, and an ADR for enforcing a design rule in the contract rather than in prose, which is the only thing that stopped `frozen` coming back a third time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…36) The four components the canvas could not afford are built: DayCell, MonthGrid, Pager, Columns, plus OtpInput gaining a required onChange. None needed a new token. DayCell shipped as a discriminated union on loggable, so a read-only cell takes onPress?: never, which is D71 applied rather than stated. Both shells now discriminate on nav. Onboarding had been hiding the desktop sidebar with a CSS rule reaching into the shell's markup; with the sidebar off, every sidebar prop is now a type error instead of a prop silently dropped. Four review rounds ran with Thomas. The one that matters: the canvas drew a seven-item habit menu and dropped Select, which both platforms ship at habit-row-menu.tsx:57 and :95. Nothing removed it, so the drawing removed a capability the app has. Restored. Habit creation now reads the sentence on the device first, with Astra as an explicit fallback. The canvas had called the model on every keystroke, which would have spent one of D70's five daily messages per habit created, and which is not what ships either: create-habit-modal.tsx:256 only calls on a press. Four false claims caught, all one shape, a sentence naming something the code does not do: voice spending an Astra message twice, the end date and description being unwritable, and an unreachable composer atLimit branch. XP and levels survive. The design-direction ADR deferred that call on 2026-08-05 pending a render, and nothing had rendered it until Progresso did. Thomas looked and kept them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The four components are built and rewired. Eleven screens are read, four are corrected with Thomas, and the rest carry their findings here with the file and line each was traced to, so the next session does not re-derive them. Three API tickets came out of the review. #341 is the one that matters: Wrapped has no endpoint at all. The retrospective is Pro-gated, every window is rolling and ends today, and "month" is 30 rolling days rather than a calendar month. A Pro-gated viral loop is not a viral loop. Also records the traps that cost time: the model picker's silent confirm dialog, the real paste-to-attachment threshold, and the iframe scroller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The second review session read the ten documents the first had not, against the code. Ten more defects, one systemic: every screen still called a Composer contract that D2 and D3 replaced, and the closed i18n audit widened that to thirteen components whose words are now the caller's. Two of the ten are the class Thomas caught on Hoje, a capability the app ships that the drawing dropped: Avisos declared mark-all-read impossible when NotificationController.cs:44 serves it and both platforms render it, and Sobreposicoes drew the same overflow menu without select. Also records the six decisions taken this session, the three design system gaps closed (Toast, Sheet.open as a literal, the shells' notice slot), and why --status-skip is kept rather than deleted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sides (#36) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…742) * docs: raise twelve more screens to what the app does (#36) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: add the handoff prompt for the last nine screens (#36) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: record the design system rounds and Wrapped (#36) Two design system contracts the screens needed: Input gained multiline, rows, marks with a required marksLabel, and maxLength; both shells typed their pinned bottom slot so a destination takes the composer and a flow takes its one forward action. Wrapped gained its cover with the period picker and four load states, the two real pages the app builds and the drawing had dropped, the second streak number, the tap zones and the keyboard, the save beside the share, and the referral link. Its false Pro gate sentence is corrected, so it carries no plan axis. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: finish the pass over the last nine screens (#36) Onboarding, Wrapped, Entrar, Verificacao, Sobre, Estados, Celebracao, Offline and Widget Android all raised to what the app does, so all 21 screen documents have now had the pass. Two design system contracts landed first: Input gained multiline, rows, marks with a required marksLabel and maxLength, and both shells typed their pinned bottom slot so a destination takes the composer and a flow takes its one forward action. Six more claims that the code cannot do something were false and were found by opening the file: Wrapped's Pro gate, Entrar's per device lock, Verificacao's unanswerable attempt window, Sobre's D69 deletion of the feature guide, Celebracao's unordered event stream and Offline's unbounded queue. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
use-goal-queries,use-habit-queries,use-popover-menu,use-app-toast,use-tour-mock-data), stores (tour-store), lib (server-fetch,api-fetchi18n adapter), and UI components (EmptyState,PillToggle,Skeleton*,SurfaceCard,Switch,OfflineUnavailableState,TodayFilters)tour-store,tour-steps,preferences,validation-constants,error-utilsextended coverage, andhabit-schedule-matchTest plan
Generated with Claude Code