refactor(ui): finish the Astryx frontend migration - #1853
Merged
Conversation
Astro-Han
force-pushed
the
refactor/1565-finish-astryx-migration
branch
from
August 1, 2026 15:56
ecf74df to
2557601
Compare
Astro-Han
marked this pull request as draft
August 1, 2026 15:59
Astro-Han
force-pushed
the
refactor/1565-finish-astryx-migration
branch
from
August 1, 2026 16:29
2557601 to
a2d5719
Compare
Astro-Han
marked this pull request as ready for review
August 1, 2026 16:37
Astro-Han
added a commit
that referenced
this pull request
Aug 1, 2026
#1853 asserted the automation-origin marker's line height inside a story `play` function. Play functions never execute in this Storybook — no test addon is configured, and a deliberately throwing `play` still ships green through scripts/storybook-visual-smoke.mjs — so that contract never ran. Keep the automation-triggered turn as visual coverage by folding it into the transcript scaffold, and move the contract into the seam that runs: the existing origin-presentation test, widened from Goal alone to all three Host origins, each of which is a separate branch with its own copy.
Astro-Han
added a commit
that referenced
this pull request
Aug 1, 2026
#1853 asserted the automation-origin marker's line height inside a story `play` function. Play functions never execute in this Storybook — no test addon is configured, and a deliberately throwing `play` still ships green through scripts/storybook-visual-smoke.mjs — so that contract never ran. Keep the automation-triggered turn as visual coverage by folding it into the transcript scaffold, and move the contract into the seam that runs: the existing origin-presentation test, widened from Goal alone to all three Host origins, each of which is a separate branch with its own copy.
Astro-Han
added a commit
that referenced
this pull request
Aug 1, 2026
…scaffolding (#1849) * refactor(stories): derive bot brand icons from BOT_BRAND The catalog kept its own list of the eight IM channels, so the story was satisfied by any subset of the registry BotBrandLogo actually reads — a newly supported channel would simply never render. Read the keys instead, matching how LucideIcons already tracks the icon barrel, and drop the empty omit-list and the always-true component guard it filtered through. * refactor(stories): drop the ask-user-question review scaffold The story built its own two-column board with a hand-written fake transcript and 60 lines of story-only CSS, then hardcoded 760px and 390px chat columns. The prompt already caps itself at --maka-chat-measure and its production slot is display:contents, so those widths were the story's invention rather than the product's — the retyped chain FIDELITY.md warns about, and the transcript above it was a screen no user reaches. Mount the prompt the way its sibling sandbox-boundary-prompt story does, and give the manifest the narrow column back as compact and floor viewports, where it is the real thing instead of a fixed div. * refactor(stories): fold the plan-reminder variants into one list Plan reminders held five of the manifest's surfaces while every neighbouring page held one, and two of the five paid for it without showing anything new. The attention state is a single blocked row, which is exactly the kind of thing a scanning page only proves it surfaces when the row has healthy neighbours — so it joins the configured list, the way ExtensionsMcpConfigured already pairs one healthy server with one failed one. Keep-awake renders nothing at all: plan-reminder-panel.test.tsx asserts the enabled page stays visually identical and keeps the state inside the settings menu, so the story smoked pixels identical to the configured one and its play function re-tested a prop the unit test already covers. Also drop the "~130 stories" figure from the catalog-pass comment; the count has been wrong since #1783 cut it, and a live number there earns nothing. * refactor(stories): host the chat transcript in the real shell Product/Chat Surface mounted ChatView and Composer inside a 960x760 centred card with a border and a radius — a frame the product does not have. In the app the transcript lives in the full-height maka-panel-detail pane that app-shell.stories.tsx already composes from the real AstryxAppShell, so every geometry comparison against the old story was measuring its own scaffolding. Two of its six stories were also the shell stories' states in that wrong frame: StreamingResponse against StreamingTurn, and the two empty chats against EmptyHome, which is the only empty home the app renders. Move the states the shell did not have — the reasoning-and-tool-call transcript and the session context layer — onto ComposedShell, and delete the parallel file with its second copy of the session, message and composer fixtures. Widening the session overlay to Partial<SessionSummary> keeps the one-active-session rule while letting a story set the name and labels the context layer reads. SessionContextNarrow is not carried over: it faked a narrow column with a 360px div, and the manifest now renders the same story at compact and floor. * test: assert the keep-awake state on its settings menu item The panel renders identically whether keep-awake is on or off, so the settings checkbox is the only thing carrying the state — and the existing test only checked that the trigger exists. Deleting the story that opened the menu therefore left aria-checked unverified. Astryx renders the menu in static markup, so the contract lands in the same suite rather than needing a browser. * refactor(stories): derive the shell's chat state from production helpers Review found the migrated stories still short of what this PR claimed. The detail pane retyped AppShellDetailPanel's classes and dropped the two wrappers below it, so .mainColumn never applied and the composer measured its own box; the composer omitted onModelChange, which renders ChatModelSwitcher disabled; and NativeConversation lost the footer actions the old file hardcoded. Mount the real AppShellDetailPanel with the production wrappers, and derive what the renderer derives: deriveAppShellTurnViewModel for footer actions, deriveBranchBanner and deriveSessionRevisionNavigation for lineage. Deriving is the point — SessionContextLayer previously hand-wrote a banner and a revision counter for a session catalog containing neither a parent nor a revision family, and asserted fromAbortedTurn, which app-shell.tsx documents it deliberately never passes. The story now supplies the relatives and gets the banner only if the real rules still produce one. A story that renders no lineage while claiming to show it is the failure this should catch, so sessionContext gets a surface check for the breadcrumbs, the revision counter and horizontal overflow; removing the fixture makes it fail at all three viewports. Excluding id from the session overlay closes the one field that would have desynchronized the sidebar from the chat rather than projecting onto it. * fix(stories): make the shell fixtures states the app can actually produce Second review round found the lineage fixture from the first one was itself impossible. The store's isValidRevisionLineage accepts the five revision fields together or not at all and rejects any index below 2; the fixture set revisionRootSessionId and revisionIndex alone, and made the root its own revision. reviseBeforeTurn shows the real shape: the root carries no revision fields and each revision carries all five, keeping the branch lineage it was revised from. The family is now built that way, and the sidebar runs through projectRevisionLinkedSessionTree, because production collapses a family to one row and the story was about to show three. WaitingForPermission forced disabled: true, but app-shell.tsx never passes disabled to ChatComposerRegion — the textarea stays usable while a tool waits, so the story was inventing a locked input. Restore the no-active-session composer as NewChatComposer. Deleting EmptyChat took the only coverage of Composer's other branch with it: without an active session it renders NewChatModelPicker instead of ChatModelSwitcher and drops the thinking selector. EmptyHome's comment claimed that state differed only in the sidebar, which was wrong. The ask-user-question decorator now reproduces the two wrappers around the composer slot. The prompt's root carries the composer class and .mainColumn zeroes its top padding, so the bare canvas rendered it 8px low; measured at 0px in the built story afterwards. Guards, all three verified by making them fail: - sessionContext joins REQUIRED_PRODUCT_SURFACES, so deleting its manifest entry now fails the validator instead of silently retiring its check. - The check reads every OverflowList copy rather than the first, which is the hidden measurement clone, and asserts goal and revision are visible or in the overflow menu — not merely present in the DOM. - The countdown spacing loop ran zero times if countdowns disappeared; it now requires one when a scheduled row exists. - check-story-annotations skipped `export` followed by a newline, so a story in that shape passed unread while the check reported success. * test(ui): cover every Host origin marker where the assertion runs #1853 asserted the automation-origin marker's line height inside a story `play` function. Play functions never execute in this Storybook — no test addon is configured, and a deliberately throwing `play` still ships green through scripts/storybook-visual-smoke.mjs — so that contract never ran. Keep the automation-triggered turn as visual coverage by folding it into the transcript scaffold, and move the contract into the seam that runs: the existing origin-presentation test, widened from Goal alone to all three Host origins, each of which is a separate branch with its own copy. * docs(stories): record that play functions do not run, and stop restating the checker Two edits to the fidelity convention: - Add the rule the commit before this one discovered: `play` is inert here, so behavioural and computed-style contracts belong in a packages/ui test or the smoke script's checks, not in a story. - Cut three paragraphs that re-derive check-story-annotations.mjs's design — its fail-closed rationale, #1724's history, and why a source scanner only approximates the story set. All of it already lives in that script's header, which is where someone editing the guard will read it. The doc keeps the rule authors need: annotate every story, and write it as `export const Name: Story = …`. - Drop the trailing `# Sandbox boundary prompt` section. It described one story's two variants, in an H1 that broke the document's structure, and those variants carry the same description as their own `// Real path:`.
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #1565.
@theme/@sourcebridge, stale configuration, and regenerated notice entriescomponents.json; update frontend governance to describe the final ownership model[hidden]semantics against the Astryx 0.2.0 reset, restore exact onboarding spacing and chat-marker typography, and lock those contracts with Electron and Storybook testsNo product behavior or intentional visual redesign is introduced. Exact former 4px-ruler values are preserved in semantic classes, while interaction chrome remains owned by Astryx primitives.
Verification
npm run format:checknpm run lintnpm run buildnpm run typechecknpm run check:releasenpm test -w @maka/ui— 235 passednpm test -w @maka/desktop— 1340 passed on the rebased headnpm run build-storybook -w @maka/desktopnpm run smoke:storybook -w @maka/desktop— 51 manifest checks and 53 catalog renders across wide/compact/floor and both themesnpx playwright test --config e2e/playwright.config.ts e2e/mcp.spec.ts e2e/chat-chrome-style.spec.ts e2e/disclosure-output.spec.ts e2e/settings.spec.ts --workers=1— 18 passedReview focus
Three narrow compatibility rules remain because the published Astryx package is still 0.2.0:
[hidden]invariant overrides the zero-specificity Astryx reset until Astryx preserves native hidden semanticsThey are isolated, documented product CSS overrides with explicit upstream removal conditions; none preserves a parallel Maka primitive or styling system.