Skip to content

refactor(ui): align chat chrome with Astryx message composition - #1768

Merged
Astro-Han merged 9 commits into
mainfrom
refactor/astryx-chat-structure-align
Aug 1, 2026
Merged

refactor(ui): align chat chrome with Astryx message composition#1768
Astro-Han merged 9 commits into
mainfrom
refactor/astryx-chat-structure-align

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Aligns chat turn composition with Astryx so reasoning, tool calls, and message actions share one disclosure dialect instead of stacked Maka chrome.

  • User/assistant shells use Astryx ChatMessage / bubble / metadata; thinking is ejected ChatReasoning.
  • Ordinary tools render through ChatToolCalls; product trow only covers permission, sandbox, and interrupted.
  • Tool detail wells use Astryx CodeBlock (neutral card); ordinary failures drop the Maka "工具调用失败" Alert, nested raw disclosure, and "失败 · 退出码" footer.
  • One chat trigger dialect: supporting type, 14px chevron, overlay-hover wash, default cursor (hand reserved for links).
  • Row detail open motion matches group/reasoning (grid 0fr→1fr).

Verification

  • npm --workspace @maka/ui run test — 288 pass
  • node --test apps/desktop/dist/main/__tests__/tool-error-collapse-contract.test.js apps/desktop/dist/main/__tests__/tool-activity-result-preview-contract.test.js — pass
  • Storybook Product/Tool Activity + chat surface reviewed for failure expand, hover wash, and no pointer

Not run: full desktop E2E suite; Playwright browser smoke.

Remove product chrome around ChatReasoning (no reasoning copy button) and
route user/assistant meta through ChatMessageMetadata while keeping product
actions and hover-gate behavior.
Reasoning, tool calls, and product trow fallbacks now share one trigger
language in the turn body: supporting size, body family, medium weight,
14px/0.75rem chevron, overlay-hover, and trailing chevron alignment.
Row-level tool resultDetail mounts without Astryx's group grid motion;
wrap it (and product trow bodies) so expand matches ChatReasoning.
Ordinary text/diff/terminal wells render through CodeBlock per Astryx
ChatToolCalls guidance; live stream, PTY, and rich cards stay product-owned.
Align failed tool detail with Astryx ChatToolCalls: status=error +
errorMessage on the row, CodeBlock/panel as resultDetail. Keep the
product sandbox-denial warning banner only.
Drop the terminal "失败 · 退出码" footer and destructive CodeBlock border;
keep Astryx neutral CodeBlock. Pin chat tool/reasoning/trow triggers to
default cursor with no overlay hover wash.
Chat tool/reasoning/trow triggers restore overlay-hover feedback while
cursor stays default (native-cursor convention).
Remove narrative and superseding-PR comments from the Astryx chat
alignment path; keep only non-obvious constraints (redaction, tokenizer,
cursor convention).
…o ToolTrow
Remove ToolErrorDetails, ordinary Alert banner branch, unused copy/CSS, and
assert production ToolTrow + CodeBlock wells instead of the boxed ToolActivity path.
Resolve chat chrome conflicts: keep ToolTrow failure contracts and pure
ChatReasoning; use Astryx Banner for sandbox-only denials.
@Astro-Han
Astro-Han merged commit 131e31f into mainAug 1, 2026
1 check passed
@Astro-Han
Astro-Han deleted the refactor/astryx-chat-structure-align branch August 1, 2026 07:57
Astro-Han added a commit that referenced this pull request Aug 1, 2026
The smoke this branch wired into CI verified 12 stories out of 141. The
manifest is curated on purpose — its 51 checks assert layout geometry across
three viewports and both colour schemes, which is expensive and only worth
paying for where layout actually varies. But that left ~130 stories verified by
nothing: build-storybook bundles a story without ever mounting it, so a render
that throws, a play function that rejects, or a console error all ship green.
"Storybook can no longer rot unobserved" was not true as written.
The catalog pass renders every unmanifested story once at wide/light and asks
one question: does it mount and finish its play function without errors. It
reuses smokeStory, so it already catches storyThrewException,
playFunctionThrewException, unhandledErrorsWhilePlaying, console errors and
empty roots. Four pages run concurrently: 124 renders in ~35s, against 217s
serial. The manifest jobs stay serial and unbaselined — they measure geometry,
which is why they pin a viewport.
The first run found seven already-broken stories, which is the argument for the
pass. Each is a play function whose selector went stale during the Astryx
migration with nothing executing it — model-picker looks for the exact
placeholder '搜索模型' while shared-ui-copy.ts now reads '搜索模型…';
ask-user-question wants an accessible name user-question-prompt no longer
exposes; tool-activity predates the #1768 disclosure dialect. They span four
components and each needs that component's current DOM to fix, which is a
different change from wiring up the guard.
So they go in storybook-catalog-baseline.json, keyed by story id with the reason
— the same shape as check-dead-css-baseline.json. Unlisted failures fail the
build. A listed story that starts passing ALSO fails, demanding its removal;
without that a baseline quietly turns into a permanent exemption, and the list
stops being a to-do.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
check-dead-css was red on main: `.astryx-collapsible-trigger` is rendered by
Astryx's own Collapsible and targeted by chat-message.css to give reasoning and
tool disclosures one trigger dialect (#1768), but it never appears as a
className literal, so the scanner counted it dead. It joins the astryx-button /
astryx-badge entries already in DYNAMIC_STYLE_HOOKS.
The regression stayed invisible because the guard only ran under
`check:release`. It now runs in the typecheck job alongside knip, which is the
same class of source-scanning governance.
Storybook had no CI coverage of rendering at all. Story typechecking already
rides along in `typecheck` via tsconfig.storybook.json, but the static build and
the 51-check render/play smoke ran only when someone remembered them locally.
Both now run at the end of the e2e job, next to the alignment audit.
That job drives Electron through `_electron.launch`, which uses Electron's own
binary and never downloads a browser; the smoke calls `chromium.launch()`, so
Chromium is installed explicitly. Locally the build takes 7s and the smoke 17s
cold, which is noise next to the Electron suite.
The e2e route also had to widen. It keys on direct workspace changes so backend
work does not drag the renderer suites along, and that stays. But
.storybook/preview.tsx reads THEME_PALETTES straight out of packages/core, so a
core change could break the Storybook build while routing away from the only job
that builds it.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
The smoke this branch wired into CI verified 12 stories out of 141. The
manifest is curated on purpose — its 51 checks assert layout geometry across
three viewports and both colour schemes, which is expensive and only worth
paying for where layout actually varies. But that left ~130 stories verified by
nothing: build-storybook bundles a story without ever mounting it, so a render
that throws, a play function that rejects, or a console error all ship green.
"Storybook can no longer rot unobserved" was not true as written.
The catalog pass renders every unmanifested story once at wide/light and asks
one question: does it mount and finish its play function without errors. It
reuses smokeStory, so it already catches storyThrewException,
playFunctionThrewException, unhandledErrorsWhilePlaying, console errors and
empty roots. Four pages run concurrently: 124 renders in ~35s, against 217s
serial. The manifest jobs stay serial and unbaselined — they measure geometry,
which is why they pin a viewport.
The first run found seven already-broken stories, which is the argument for the
pass. Each is a play function whose selector went stale during the Astryx
migration with nothing executing it — model-picker looks for the exact
placeholder '搜索模型' while shared-ui-copy.ts now reads '搜索模型…';
ask-user-question wants an accessible name user-question-prompt no longer
exposes; tool-activity predates the #1768 disclosure dialect. They span four
components and each needs that component's current DOM to fix, which is a
different change from wiring up the guard.
So they go in storybook-catalog-baseline.json, keyed by story id with the reason
— the same shape as check-dead-css-baseline.json. Unlisted failures fail the
build. A listed story that starts passing ALSO fails, demanding its removal;
without that a baseline quietly turns into a permanent exemption, and the list
stops being a to-do.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
check-dead-css was red on main: `.astryx-collapsible-trigger` is rendered by
Astryx's own Collapsible and targeted by chat-message.css to give reasoning and
tool disclosures one trigger dialect (#1768), but it never appears as a
className literal, so the scanner counted it dead. It joins the astryx-button /
astryx-badge entries already in DYNAMIC_STYLE_HOOKS.
The regression stayed invisible because the guard only ran under
`check:release`. It now runs in the typecheck job alongside knip, which is the
same class of source-scanning governance.
Storybook had no CI coverage of rendering at all. Story typechecking already
rides along in `typecheck` via tsconfig.storybook.json, but the static build and
the 51-check render/play smoke ran only when someone remembered them locally.
Both now run at the end of the e2e job, next to the alignment audit.
That job drives Electron through `_electron.launch`, which uses Electron's own
binary and never downloads a browser; the smoke calls `chromium.launch()`, so
Chromium is installed explicitly. Locally the build takes 7s and the smoke 17s
cold, which is noise next to the Electron suite.
The e2e route also had to widen. It keys on direct workspace changes so backend
work does not drag the renderer suites along, and that stays. But
.storybook/preview.tsx reads THEME_PALETTES straight out of packages/core, so a
core change could break the Storybook build while routing away from the only job
that builds it.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
The smoke this branch wired into CI verified 12 stories out of 141. The
manifest is curated on purpose — its 51 checks assert layout geometry across
three viewports and both colour schemes, which is expensive and only worth
paying for where layout actually varies. But that left ~130 stories verified by
nothing: build-storybook bundles a story without ever mounting it, so a render
that throws, a play function that rejects, or a console error all ship green.
"Storybook can no longer rot unobserved" was not true as written.
The catalog pass renders every unmanifested story once at wide/light and asks
one question: does it mount and finish its play function without errors. It
reuses smokeStory, so it already catches storyThrewException,
playFunctionThrewException, unhandledErrorsWhilePlaying, console errors and
empty roots. Four pages run concurrently: 124 renders in ~35s, against 217s
serial. The manifest jobs stay serial and unbaselined — they measure geometry,
which is why they pin a viewport.
The first run found seven already-broken stories, which is the argument for the
pass. Each is a play function whose selector went stale during the Astryx
migration with nothing executing it — model-picker looks for the exact
placeholder '搜索模型' while shared-ui-copy.ts now reads '搜索模型…';
ask-user-question wants an accessible name user-question-prompt no longer
exposes; tool-activity predates the #1768 disclosure dialect. They span four
components and each needs that component's current DOM to fix, which is a
different change from wiring up the guard.
So they go in storybook-catalog-baseline.json, keyed by story id with the reason
— the same shape as check-dead-css-baseline.json. Unlisted failures fail the
build. A listed story that starts passing ALSO fails, demanding its removal;
without that a baseline quietly turns into a permanent exemption, and the list
stops being a to-do.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
check-dead-css was red on main: `.astryx-collapsible-trigger` is rendered by
Astryx's own Collapsible and targeted by chat-message.css to give reasoning and
tool disclosures one trigger dialect (#1768), but it never appears as a
className literal, so the scanner counted it dead. It joins the astryx-button /
astryx-badge entries already in DYNAMIC_STYLE_HOOKS.
The regression stayed invisible because the guard only ran under
`check:release`. It now runs in the typecheck job alongside knip, which is the
same class of source-scanning governance.
Storybook had no CI coverage of rendering at all. Story typechecking already
rides along in `typecheck` via tsconfig.storybook.json, but the static build and
the 51-check render/play smoke ran only when someone remembered them locally.
Both now run at the end of the e2e job, next to the alignment audit.
That job drives Electron through `_electron.launch`, which uses Electron's own
binary and never downloads a browser; the smoke calls `chromium.launch()`, so
Chromium is installed explicitly. Locally the build takes 7s and the smoke 17s
cold, which is noise next to the Electron suite.
The e2e route also had to widen. It keys on direct workspace changes so backend
work does not drag the renderer suites along, and that stays. But
.storybook/preview.tsx reads THEME_PALETTES straight out of packages/core, so a
core change could break the Storybook build while routing away from the only job
that builds it.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
The smoke this branch wired into CI verified 12 stories out of 141. The
manifest is curated on purpose — its 51 checks assert layout geometry across
three viewports and both colour schemes, which is expensive and only worth
paying for where layout actually varies. But that left ~130 stories verified by
nothing: build-storybook bundles a story without ever mounting it, so a render
that throws, a play function that rejects, or a console error all ship green.
"Storybook can no longer rot unobserved" was not true as written.
The catalog pass renders every unmanifested story once at wide/light and asks
one question: does it mount and finish its play function without errors. It
reuses smokeStory, so it already catches storyThrewException,
playFunctionThrewException, unhandledErrorsWhilePlaying, console errors and
empty roots. Four pages run concurrently: 124 renders in ~35s, against 217s
serial. The manifest jobs stay serial and unbaselined — they measure geometry,
which is why they pin a viewport.
The first run found seven already-broken stories, which is the argument for the
pass. Each is a play function whose selector went stale during the Astryx
migration with nothing executing it — model-picker looks for the exact
placeholder '搜索模型' while shared-ui-copy.ts now reads '搜索模型…';
ask-user-question wants an accessible name user-question-prompt no longer
exposes; tool-activity predates the #1768 disclosure dialect. They span four
components and each needs that component's current DOM to fix, which is a
different change from wiring up the guard.
So they go in storybook-catalog-baseline.json, keyed by story id with the reason
— the same shape as check-dead-css-baseline.json. Unlisted failures fail the
build. A listed story that starts passing ALSO fails, demanding its removal;
without that a baseline quietly turns into a permanent exemption, and the list
stops being a to-do.
Astro-Han added a commit that referenced this pull request Aug 1, 2026
…ory per surface (#1783)
* ci: guard dead CSS and Storybook rendering
check-dead-css was red on main: `.astryx-collapsible-trigger` is rendered by
Astryx's own Collapsible and targeted by chat-message.css to give reasoning and
tool disclosures one trigger dialect (#1768), but it never appears as a
className literal, so the scanner counted it dead. It joins the astryx-button /
astryx-badge entries already in DYNAMIC_STYLE_HOOKS.
The regression stayed invisible because the guard only ran under
`check:release`. It now runs in the typecheck job alongside knip, which is the
same class of source-scanning governance.
Storybook had no CI coverage of rendering at all. Story typechecking already
rides along in `typecheck` via tsconfig.storybook.json, but the static build and
the 51-check render/play smoke ran only when someone remembered them locally.
Both now run at the end of the e2e job, next to the alignment audit.
That job drives Electron through `_electron.launch`, which uses Electron's own
binary and never downloads a browser; the smoke calls `chromium.launch()`, so
Chromium is installed explicitly. Locally the build takes 7s and the smoke 17s
cold, which is noise next to the Electron suite.
The e2e route also had to widen. It keys on direct workspace changes so backend
work does not drag the renderer suites along, and that stays. But
.storybook/preview.tsx reads THEME_PALETTES straight out of packages/core, so a
core change could break the Storybook build while routing away from the only job
that builds it.
* docs(storybook): drop the Astryx showcase and legacy-token stories
Eleven stories documented something other than this product.
Five were straight Astryx pass-throughs. Button, Badge and Astryx Atoms render
re-exports from packages/ui/src/index.ts; Menu and Dialog import
@astryxdesign/core/DropdownMenu, /Dialog and /Layout directly. Astryx publishes
its own Storybook for all of them, so ours could only ever be a staler copy.
Six were catalogues of the token layer #1565 is deleting. Typography, Elevation,
Layering, Palette Matrix and Spacing were last touched before the first Astryx
commit; Design System/Tokens is newer but documents the same set. All of them
hand-render --shadow-minimal, --border-strong and --font-sans through inline
styles, under a namespace that presents them as current truth. Rewriting them
against Astryx tokens now would mean writing them twice, and once the token
contract is settled the useful artifact is one Maka/Astryx integration story
that can act as an oracle for the migration — not six generic catalogues that
duplicate upstream.
Kept: Primitives/Toast wraps Maka's own toast.tsx (the toast.confirm() queue)
and product-smoke-manifest.json pins primitives-toast--confirm-queued;
Primitives/StatTile covers a Maka primitive. Design System/Icons, Animation
Catalog and Interaction States also reference legacy variables, but what they
document — the Maka icon set, Maka motion tokens, interaction states — outlives
the token rename, so they move with it rather than out.
Storybook still builds and the smoke still passes its 51 render/play checks.
* docs(storybook): stop claiming a retired test enforces the fidelity convention
FIDELITY.md described `story-annotation-contract.test.ts` as the thing that
checks every product story carries a `// Real path:` sentence, and spent a
paragraph on how it derives its file list and refuses to skip exports it cannot
classify. #1724 deleted that test with the rest of the source-scanning contract
suite. A document that promises enforcement nobody runs is worse than one that
admits there is none — it tells reviewers the check already happened.
The convention itself is worth keeping, so it now says plainly that it is a
review convention and that it decays like one.
It had already decayed: AstryxNativeConversation was the one product story with
no annotation. It is reachable — a long session accumulates reasoning, tool
calls and prose, and an image can sit staged in the composer — but it stacks
those states deliberately, which FIDELITY.md requires a story to say out loud.
Its comment now says both. Every `Product/*` story carries the sentence again;
the remaining gaps are all `Primitives/*` and `Design System/*`, which the
document exempts.
* docs(renderer): correct the Astryx tailwind-theme.css note
The header said tailwind-theme.css stays out "until Tailwind leaves", which has
it backwards. That file's own header calls it a Tailwind Bridge: it maps Astryx
tokens onto Tailwind v4 @theme variables so utilities resolve against the theme.
It is only useful while Tailwind is present, and the slice in question deletes
Tailwind — so it should never be imported at all. Anyone reading the old note
while planning that slice would go looking for an import that must not happen.
Also corrects the slice number: Tailwind removal is Slice 13, not 12, and #1565
asks that "PR N" never be used as a synonym for a slice.
* docs(storybook): apply the deletion rule to the retained motion and state stories
The earlier cut used the wrong test. It asked "is this Maka's own?" and kept
whatever answered yes. The question that matters is whether Astryx already owns
the domain — if it does, a Maka catalogue of the same thing is a parallel
vocabulary being documented as the contract, which is exactly what the six
deleted token stories were.
Animation Catalog failed that test. Its DurationScale and EasingScale presented
--duration-quick/base/emphasized/large and the four --ease-* tokens as the motion
scale, while the Astryx theme already ships --duration-fast/medium/slow. Both are
gone. What remains is the spinner and the streaming shimmer — load-bearing, not
decorative, and they outlive the token rename — so the file is now
Design System/Functional Motion, which is what it actually shows.
Interaction States failed it too, in the part nobody flagged: ButtonStates,
NeutralButtonStates and SolidButtonStates are state matrices for Astryx's Button,
the same component whose Primitives/Button story this branch already deleted.
ListRowStates stays, and it is the reason the file stays: it compares hover and
focus across two DIFFERENT row components — Astryx's side-nav item and its list
item — as the sidebar composes them. session-list-panel.stories.tsx owns
SessionListPanel's own states; nothing else covers the seam between the two. The
file is now Design System/Composite Row States.
Design System/* is left holding Icons and Functional Motion — the Maka icon set,
the brand mark, and two functional animations — plus that one cross-component
seam. Nothing in it catalogues a token scale any more.
* ci: enforce the mechanical half of the story fidelity convention
The previous commit had FIDELITY.md admit that nothing checks the `// Real path:`
annotations. That was honest but it was the wrong repair: presence of the
sentence is mechanically decidable, and this branch had just proved review does
not hold that line — chat-surface.stories.tsx reached thirteen stories with
twelve annotations and nobody noticed until a script counted them.
So the convention now splits along what a machine can decide. Existence is
checked by scripts/check-story-annotations.mjs in CI; truth stays with the
reviewer, where it has to stay — a schema is satisfied by a plausible lie just
as easily.
This is not a revival of story-annotation-contract.test.ts. #1724 was right to
delete 149 tests that read source text and charged every refactor a rewrite of
its own guards, and it explicitly kept the fast scripts/check-*.mjs commands for
non-cosmetic invariants. This is one of those: ~110 lines, no build, runs beside
check-dead-css in the typecheck job.
It keeps the one property of the retired test that mattered: it fails on any
top-level export it cannot classify rather than skipping it, because a guard
that ignores what it cannot parse passes *because* it did not understand. It
also fails if .storybook/main.ts stops loading a directory it scans, so a story
tree cannot drift out of coverage.
Eight tests cover both, including the case that caught a real bug while writing
this: both story roots end in `stories`, so the config check originally matched
only the last path segment and stayed green when either root was removed.
* ci(storybook): render the whole catalog, not just the manifest
The smoke this branch wired into CI verified 12 stories out of 141. The
manifest is curated on purpose — its 51 checks assert layout geometry across
three viewports and both colour schemes, which is expensive and only worth
paying for where layout actually varies. But that left ~130 stories verified by
nothing: build-storybook bundles a story without ever mounting it, so a render
that throws, a play function that rejects, or a console error all ship green.
"Storybook can no longer rot unobserved" was not true as written.
The catalog pass renders every unmanifested story once at wide/light and asks
one question: does it mount and finish its play function without errors. It
reuses smokeStory, so it already catches storyThrewException,
playFunctionThrewException, unhandledErrorsWhilePlaying, console errors and
empty roots. Four pages run concurrently: 124 renders in ~35s, against 217s
serial. The manifest jobs stay serial and unbaselined — they measure geometry,
which is why they pin a viewport.
The first run found seven already-broken stories, which is the argument for the
pass. Each is a play function whose selector went stale during the Astryx
migration with nothing executing it — model-picker looks for the exact
placeholder '搜索模型' while shared-ui-copy.ts now reads '搜索模型…';
ask-user-question wants an accessible name user-question-prompt no longer
exposes; tool-activity predates the #1768 disclosure dialect. They span four
components and each needs that component's current DOM to fix, which is a
different change from wiring up the guard.
So they go in storybook-catalog-baseline.json, keyed by story id with the reason
— the same shape as check-dead-css-baseline.json. Unlisted failures fail the
build. A listed story that starts passing ALSO fails, demanding its removal;
without that a baseline quietly turns into a permanent exemption, and the list
stops being a to-do.
* test(storybook): delete the broken stories instead of baselining them
The catalog pass found seven stories whose play functions had gone stale during
the Astryx migration. The previous commit recorded them in a baseline so the
guard could land without also fixing four components. Deleting them is the
better answer: a story that has not verified anything since the migration is not
coverage waiting to be restored, and the states they claimed to cover are either
already covered elsewhere or were asserting behaviour rather than appearance.
model-picker Search / NoResults filtering the catalog; the empty result is
EmptyCatalog's job
tool-activity DisclosureInteraction aria-expanded, computed display, hit-area
width and chevron centring — geometry that
audit-alignment.mjs and e2e own
ask-user-question OtherAnswerSelected typing into the free-text answer
settings-pages WebSearch/Voice ×3 provider round-trips reaching a status text
Three more went with them under the same rule — a story earns its place only if
someone opens it to make a visual judgement that no other story supports:
model-picker Pending Default with the control disabled
tool-activity CopyFeedback same fixture as ErrorsAndPermissionDenied
plus a transient label
tool-activity FileDiffAndWebSearch denseMixedResultItems already contains all
three of its items, expanded
So storybook-catalog-baseline.json and the reconcile step are gone with them.
Without known-broken entries the catalog pass is just "every story renders", and
a future breakage has two honest options — fix it or delete it — rather than a
third that defers both.
136 stories → 126; the smoke now runs 51 manifest checks and 114 catalog renders
in ~64s with nothing exempted.
* test(storybook): keep one story per surface, drop the state enumerations
126 stories down to 58. The rule: a story earns its place only if someone opens
it to make a visual judgement no other story supports. In practice that leaves,
per surface, the densest reachable state — plus an empty state where the layout
genuinely differs, plus whatever product-smoke-manifest.json pins.
What went:
state enumerations SearchModal's Loading / NoResults / Error / Blocked next
to Results; onboarding's four gate variants of one hero;
provider-settings' Loading / LoadError / Empty
behaviour, not paint CommandPaletteKeyboardFocusedSelection, RowActions,
RowMenuOpen — interaction the E2E suite owns
states contained by Usage and UsageEmpty under UsageRequestsPopulated;
a denser sibling Memory under MemoryPopulated; app-shell's PlanModeActive
and SwarmModeActive; tool-activity's StatusOverview,
TerminalAndLiveOutput and SubagentAndExplore, all inside
denseMixedResultItems
duplicated elsewhere app-shell's MarkdownCore against markdown.stories.tsx
Every story the manifest pins survives, including the eight module-hubs entries
and primitives-toast--confirm-queued.
Deleting stories orphans their fixtures, and tsconfig.storybook.json does not
set noUnusedLocals, so nothing would have reported them. Compiling once with the
flag on found fourteen — bridges, settings fixtures, voice capture helpers — and
they go too.
Two guards caught mistakes made while doing this, which is the argument for
having wired them up: validateCoverageManifest rejected a build after a regex
with a shared name prefix took ExtensionsSkillsInstalled out along with
ExtensionsSkills, and check-story-annotations rejected the next one after a
cleanup pass ate the `// Real path:` line above Models.
The catalog pass is now 47 renders instead of 114, and the whole smoke runs in
34s instead of 64s.
* fix(storybook): restore the three states the prune should not have cut
Three of the cuts failed the retention rule they were made under — "keep the
state no other story supports" — and an independent review of the branch caught
all three.
Palette Matrix was dropped as a token catalog Astryx now owns. It is not one:
THEME_PALETTES is a product feature, the palettes a user picks in 设置 → 外观,
and the catalog pass renders wide/light/default only. Ten of the eleven had no
oracle at all. The file's header now records why the other five token stories
still went, since "Astryx already owns this domain" was the wrong reason for
all of them — maka-tokens.css is still the single authority and dies with
Slice 13; a table of var names is simply low review value with a short life.
Permission Center kept the collapsed state and cut the expanded one, which is
backwards: the capability layers grid and guidance block only exist once
diagnostics are expanded, and the deleted story's own comment says that is
where the last overflow bug was hiding. Everything the collapsed story showed
is still on screen in the expanded one, so this stays one story.
VoicePermissionDenied comes back because the surviving Voice story is idle and
the page's only error surface had no coverage anywhere — the E2E suite checks
fields and persistence, not capture outcomes. It comes back without the
120-line MediaRecorder decorator the old story carried: permissionSnapshot
already reports microphone `denied` on darwin and runCaptureSmoke returns on
that snapshot before it ever reaches getUserMedia, so the real path needs no
browser mocking. The catalog pass then failed the restored story immediately,
for the same reason the originals rotted: the result lives in one of six
`[role="status"]` regions and `querySelector` had picked an empty one.
Also drops two doc comments the prune orphaned, which now described stories
that no longer exist.
* fix(ci): close the holes in the story-annotation guard
The guard claimed to fail on anything it could not classify. Review found five
inputs where it did not, each now covered by a test:
- `.storybook/main.ts` loads `*.stories.@(ts|tsx)`; the scanner only opened
`.stories.tsx`, so a `.ts` story was never read at all.
- `export { Story }` and `export async function Story()` reach the browser as
stories and matched nothing, so they passed in silence — the exact failure
the "fail on what you cannot parse" rule exists to prevent.
- `export const X: Story =` wrapping onto the next line was reported as a
malformed export. A guard that reddens on formatting teaches people to
ignore it.
- The title regex took the first `title:` in the file, not meta's. A fixture
literal above meta decided the file's namespace, and a `Design System/…` one
exempted every story in it.
- An empty `// Real path:` counted as an annotation.
checkStorybookRoots claimed to catch a root added to main.ts but not scanned
here; it only checked that the two known roots were still present. It now
compares both directions, and the test covers the addition case and the
accept-the-real-config case — the latter previously asserted `[] deepEqual []`
against a function that returns undefined, so it verified nothing.
FIDELITY.md now also says what the check is: a convention guard that must fail
in seconds inside typecheck, approximating a story set only Storybook's index
truly knows. Every gap above was closed by widening what it fails on, never by
widening what it skips.
* ci: run the Storybook smoke when its own runner changes
`scripts/**` only sets scriptMode, so a change to the smoke runner was
verified by nothing but the run it never triggered — the one file most
able to make the guard silently stop guarding.
Sign up for freeto 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

@Astro-Han