Skip to content

chore: expand frontend test coverage (276 new tests) - #36

Merged
thomasluizon merged 1 commit into
mainfrom
chore/test-coverage-frontend-2
Apr 11, 2026
Merged

chore: expand frontend test coverage (276 new tests)#36
thomasluizon merged 1 commit into
mainfrom
chore/test-coverage-frontend-2

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Summary

  • Adds 276 new unit tests across 21 new test files covering previously untested frontend code
  • Covers hooks (use-goal-queries, use-habit-queries, use-popover-menu, use-app-toast, use-tour-mock-data), stores (tour-store), lib (server-fetch, api-fetch i18n adapter), and UI components (EmptyState, PillToggle, Skeleton*, SurfaceCard, Switch, OfflineUnavailableState, TodayFilters)
  • Adds shared package tests for tour-store, tour-steps, preferences, validation-constants, error-utils extended coverage, and habit-schedule-match

Test plan

  • All 21 new test files pass locally (276/276 tests green)
  • All pre-existing tests remain green (1515 web + 737 shared passing)
  • No source code was modified -- only test files added
  • CI passes

Generated with Claude Code

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>
@vercel

vercel Bot commented Apr 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Apr 11, 2026 2:45am

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

@thomasluizon
thomasluizon merged commit 9a90b69 into main Apr 11, 2026
4 of 6 checks passed
@thomasluizon
thomasluizon deleted the chore/test-coverage-frontend-2 branch April 11, 2026 13:39
thomasluizon added a commit that referenced this pull request Aug 10, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 14, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 14, 2026
* 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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
… 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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
) (#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>
thomasluizon added a commit that referenced this pull request Aug 15, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
…#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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 16, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
…#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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
…#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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
#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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
)

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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 17, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 19, 2026
)

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>
thomasluizon added a commit that referenced this pull request Aug 19, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 19, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 19, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 19, 2026
… 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>
thomasluizon added a commit that referenced this pull request Aug 20, 2026
…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>
thomasluizon added a commit that referenced this pull request Aug 20, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 20, 2026
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>
thomasluizon added a commit that referenced this pull request Aug 21, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Aug 21, 2026
…sides (#36)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Aug 21, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
thomasluizon added a commit that referenced this pull request Aug 22, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant