Repository navigation
v1.221-developer-rails — modules/capabilities/sessions rail rebuild + residual closeout - #445
Conversation
…-000058) The refusal folds case and per-segment trailing dots AND spaces, but only the dot half had a regression tripwire. Add `/v1%20/y` to the HTTP fold matrix (404 not_found, non-INDEX body, nosniff) and `v1 /x` to the unit-seam traversal array. Verified as a tripwire: narrowing the fold regex to `[.]+$` turns exactly these two cases red and leaves the existing fold cases green.
…Electron token (I-000058)
Remove the Settings-modal ownership of the modules section: drop the section id/descriptor/aliases, the develop nav item and route rule, the modal section wrapper, and the locale key. ModulesPage renders the page body instead of redirecting; canvas/timeline Run Studio entries navigate to /modules directly. Settings tests, the timeline compute journeys and the Design Studio fixture are rebased on the page contract.
…presentational chrome - components/developer/modules-page.tsx keeps the app wiring (queries, deep-link router reads, copy assembly); pages/modules-page.tsx is deleted - presentational/modules-page-chrome.tsx: ModulesPageChrome + ModuleDetailChrome (+ DetailField, SchemaBlock), pure view with a runStudio slot - presentational/run-studio-chrome.tsx: RunStudioChrome, pure view driven by copy/world/form/advanced/run/proposal/runs view models - compute/run-studio.tsx keeps every hook, mutation, toast and the two confirm dialogs; public props unchanged, canvas entry untouched - app-registry modules.load → the new page - close T1 residuals F1-F5 (stale SSOT/section comments, dead shell.nav.modules locale key, unexported dead SettingsModalContext)
…t alias - fixtures/modules-rail-fixtures.tsx: ModulesPageChrome + ModuleDetailChrome + RunStudioChrome from static view models and literal copy (list with one selected, manifest detail beside the select prompt, run pending/success/ detail-error); theme follows the Studio toggle - fixtures/__tests__/modules-rail-fixtures.test.tsx: one assertion per variant testid plus a both-theme render smoke - @web-developer alias in tsconfig + vite + vitest (Studio resolves every @Web-* extract through all three) - surface-source-badge: classify @web-developer/* as an app presentational extract (test case mirrored) - gallery-index: surfaces-rail-apps shell entry + overview row - surfaces.tsx: render the fixture in SurfacesShellPage under data-testid=surfaces-rail-apps (no new route)
…n/v1.221-developer-rails
Move the /capabilities markup into components/developer/presentational/capabilities-page-chrome.tsx (copy as props, no transport/router/i18n) and relocate the page to components/developer/capabilities-page.tsx as pure wiring, so the Design Studio fixture renders it from static view models. Split the collapsed empty state into two branches: registry-empty (unchanged copy, wins over a filter) and filtered-miss (new keys quoting the query plus a Clear filter action via the EmptyState action slot). Match semantics and the read-only ?filter= seed are unchanged. Register the four-variant fixture (empty registry, filtered hit, filtered miss, error) in the Studio surfaces shell gallery, both themes.
Replace the sessions table with the v1.220 hub card frame: the short session id as the card title, the unchanged `statusVariant` pill, then Preset / Creator — `current_task_id` is dropped from the surface (the run view owns the in-flight pointer; the contract field and query stay untouched). The whole card is one link to `/sessions/<encodeURIComponent(session_id)>`, so each card is a single focus stop and `HubCard`'s `go` slot stays unused (no nested link). The card markup lands in `components/developer/presentational/session-card.tsx` (resolved view models plus an injected `renderLink`, no router/i18n) and the page relocates to `components/developer/sessions-page.tsx` as pure wiring; both the creator and developer `session` AppDefs repoint to it. `HubCard.title` widens to `ReactNode` and its status variant to `BadgeProps['variant']` — type-only, the Hub cards keep passing strings. The zero-session copy becomes "No sessions yet" / "Sessions appear here when a preset run starts." (the list also holds terminal sessions). Register the mixed running/completed/failed grid plus the empty state in the Studio surfaces shell gallery, both themes.
The chrome hardcoded id="caps-filter" for its label/input pair, so the four gallery mounts produced four duplicate DOM ids and each label focused the first input in the document. Generate the id with useId() so every mount is self-consistent and unique. Also drop data-testid="capabilities-page-body", which had no consumer. The design-studio fixture test now asserts the four frames get pairwise distinct ids, each id appears once in the document, and getByLabelText resolves each frame's own filter input. Refs QC3-R-1, QC3-R-2.
…n/v1.221-developer-rails
[Medium impact] The PR appears safe to merge; the earlier duplicate-ID finding is fully addressed. SummaryRebuilds the Modules, Capabilities, and Sessions developer pages with shared display components and Design Studio fixtures.
Reviews (4) · Last reviewed commit: "Merge branch 'fix/pr445-review-r3' into ..." · Reviewed by Greptile |
CI web vitest failed with "TypeError: document.getElementById(...) ?.scrollIntoView is not a function" from the Settings anchor scroll effect in settings-modal-host.tsx: jsdom does not implement Element.prototype.scrollIntoView, and the uncaught requestAnimationFrame callback error failed the run even with 222 files / 2349 tests passing. Optional-call the method; behavior in real browsers is unchanged.
auto-wood
left a comment
There was a problem hiding this comment.
⚠️ Verdict: needs fixes · Score 66% (pass)
| Findings | Count |
|---|---|
| 🔴 must-fix | 0 |
| 🟠 should-fix | 3 |
| 🔵 nit | 6 |
| ❓ unverified | 0 |
📋 Review
What this PR does: Completes the developer rail registry (操作台 / Harness / 模块 / 能力 / 会话) under the 2026-10-22 web-rebuild authorization: /modules becomes a first-class rail app with Settings-modal ownership deleted outright, capabilities and sessions rails lifted to the same generation (chrome/view split, hub-card sessions, deep links preserved), design-studio fixture + gallery wiring, an I-000058 trailing-space fold tripwire, and the thrice-verified rail-app rebuild recipe promoted to knowledge.
Revalidated findings
none
Findings
🟠 Legacy /settings/modules deep links silently land on the Agent settings section (I-000085)
- Evidence:
apps/web/src/components/layout/settings-section-registry.ts:125-145— after the ownership deletion,/settings/modulesmatches no special case and falls back toDEFAULT_SETTINGS_SECTION(agent);settings-modal-context.tsx:116-136preserveslocation.search, so the URL becomes/settings/agent?module=…&run=…. - Impact: v1.147 shipped in-app navigation to
/settings/modules(git 99d58f4fe,canvas-nav-commands.tsx:162, tests pinned/settings/modules?world=…), so old bookmarks/tabs render the wrong section with no signal. The closeout knowledge entry's "the product has never shipped" premise is contradicted by git history — the no-redirect decision stands on a false fact. - Merge class: should-fix
- Confidence: HIGH
- Fix sketch: explicit alias
/settings/modules → /modulespreserving search, or correct the knowledge decision record with explicit product sign-off on dropping those URLs.
🟠 Run Studio chrome ×3 in modules gallery with duplicate static DOM ids (I-000086)
- Evidence:
run-studio-chrome.tsx:199-348hardcodesrun-studio-world/run-runs-status-filter/run-runs-world-filter;modules-rail-fixtures.tsx:464-497mountsRunStudioChromethree times in one document. This PR itself converted the capabilities chrome touseId()(capabilities-page-chrome.tsx:88, commit 8bde104) with a negative-checked fixture test — the modules side lacks both, against knowledge recipe item 6. - Impact: label→control association and AT navigation break in the Studio acceptance surface; the documented regression defence (reverting to hardcoded ids must turn the suite red) does not exist here.
- Merge class: should-fix
- Confidence: HIGH
- Fix sketch:
useId()inRunStudioChrome+ pairwise-distinct id assertion mirroringcapabilities-rail-fixtures.test.tsx:36-48.
🟠 "Both themes" fixture passes assert fixture-owned classes — the dark pass proves nothing (I-000087)
- Evidence:
capabilities-rail-fixtures.test.tsx:110-111andmodules-rail-fixtures.test.tsx:95-96assert classes authored in the fixtures' own markup;vitest.config.ts:65runscss: false, so thedarktoggle has no observable effect. - Impact: the PR's "both themes" coverage cannot fail even if a chrome hardcoded a light-only color. The sessions fixture does it right by asserting product
HubCardtoken-backed classes. - Merge class: should-fix
- Confidence: HIGH
- Fix sketch: assert a chrome-owned token class, or drop the assertion and declare theme parity visual-gallery-only.
🔵 Locale: zh-CN settings nav workspace stays English ("Profiles")
- Evidence:
apps/web/src/locales/zh-CN/settings.json:9— the diff edits this exact block (removesnav.modules) and leaves the parity defect. - Merge class: nit · Confidence: HIGH
- Fix sketch:
zh-CN nav.workspace: "配置档案"(or comment the intentional product term).
🔵 Registry tests assert source shape, not product behavior
- Evidence:
settings-section-registry.test.ts:15-27pinsSETTINGS_SECTION_IDSorder andlabelKey === nav.${id};entrance-registry.test.tspins rule-array contents/icon wiring. - Merge class: nit · Confidence: MEDIUM
- Fix sketch: keep one nav-order contract test (user-visible order); delete the labelKey/icon shape pins or replace with rendered-nav-label assertions.
🔵 Surfaces index footer prose not updated for the new rail sections
- Evidence:
apps/design-studio/src/pages/surfaces.tsx:512-563— the surface/source enumerations stop at Selection Submenu while lines 802-858 add three new sections and the@web-developer/*extract root. - Merge class: nit · Confidence: HIGH
- Fix sketch: add the three rails and
@web-developer/*to both lists.
🔵 Non-product test assertions: badge class/innerHTML pins, unbound getByText('3')
- Evidence:
sessions-rail-fixtures.test.tsx:71-73andhub-card.test.tsx:55pin@42ch/nexus-uiclass names (innerHTML);modules-rail-fixtures.test.tsx:53-54asserts an unboundgetByText('3')any "3" satisfies. Badge-class pins follow an existing repo convention (sessions-page.test.tsx:121-127). - Merge class: nit · Confidence: HIGH
- Fix sketch: delete the innerHTML pin (or replace with a status→variant product assertion); scope the ABI value to its field row.
🔵 Stale invalidation comment in the canvas compute journey
- Evidence:
compute-events-canvas.test.tsx:169documents atimeline.allinvalidation the journey no longer exercises (unmount at:391-393, remount+refetch at:476-478). - Merge class: nit · Confidence: HIGH
- Fix sketch: update the comment to the remount mechanism.
🔵 session-card.tsx relative import exits the presentational/ alias root
- Evidence:
session-card.tsximports../hub-cardwhile the chrome convention (knowledge recipe item 2) allows hub-card only as an explicit composition exception via alias. - Merge class: nit · Confidence: HIGH
- Fix sketch: use the
@/components/developer/hub-cardpath.
Linked-issue AC
- I-000058 (trailing-space fold tripwire): met —
/v1%20/yadded to the HTTP fold matrix andv1 /xto the unit-seam refusal array. - I-000084 (Advanced-JSON open-state assertion): cut — disclosed residual, tracked open as I-000084 by the PR itself.
✅ Verified
- Full diff basis read (7774-line pinned pack,
origin/main...pr-445). - Deep links preserved on the new surface:
modules-page.tsx:69-81,capabilities-page.tsx:36, query write-back inselectModule/onRunOpen. - Dead-code sweep: no remaining importers of deleted
pages/*-page,settings-modules-section,nav.modules,Cpu,SettingsModalContext. - Locale parity en vs zh-CN across rail namespaces — clean.
- Security lenses (XSS, hrefs, deep-link params, entrance guards, static-serve, supply-chain) — no reachable sink with attacker-controlled input.
- ❓ CI (PR's own unchecked checkbox) — not verifiable at review time; a pre-existing red build is not a diff finding.
🗑️ Considered & rejected
- XSS via rendered fixture/registry content: rejected — React text/i18n interpolation only, no HTML sink.
- Session whole-card link open-redirect/
javascript:scheme: rejected —encodeURIComponent+ react-router<Link>. - Entrance guard weakening from the settings deletion: rejected —
/moduleskeeps the develop-only rule; no route newly reachable. - Static-serve regression: rejected — product change is comment-only; the test change tightens the
/v1fold refusal (I-000058 tripwire). - "No redirect" as full by-design defence for the alias finding: rejected as a complete defence — the decision is recorded in knowledge, but its factual premise (never shipped) is falsified by v1.147 history.
🛠️ Plan to fix
Expand fix plan
1. SF-2 (I-000086): `useId()` in `run-studio-chrome.tsx` for the three control ids; add pairwise-distinct id assertion + negative check to `modules-rail-fixtures.test.tsx`.
2. SF-1 (I-000085): decide alias vs record-correction. Either add `/settings/modules → /modules` alias preserving search, or fix `.mstar/knowledge/architecture-patterns/developer-rail-app-rebuild.md` item 1's "never shipped" premise with explicit product sign-off.
3. SF-3 (I-000087): re-point both theme-loop class assertions at chrome-owned token-backed classes (sessions style), or drop them and declare theme parity visual-gallery-only.
4. Nits batch: zh-CN `nav.workspace` label; surfaces.tsx footer enumerations; delete `hub-card.test.tsx` innerHTML pin + scope ABI `'3'`; update `compute-events-canvas.test.tsx:169` comment; `session-card.tsx` alias import.
5. Verification: `pnpm --filter web typecheck && pnpm --filter design-studio typecheck`; targeted vitest (modules/caps/sessions fixtures, hub-card, registry, entrance, static-serve).session-card.tsx reached ../hub-card, a relative import that exits the @web-developer presentational root (and resolves only through Studio's web-src fallback). HubCard is a read-only frame with no product wiring, so move it into presentational/ and import it as a sibling; update the two product importers to the new path. Review nit (cross-domain security seat handoff).
…tions I-000086 / Greptile 4367153745: the modules gallery mounts RunStudioChrome three times; hardcoded run-studio-world / run-runs-status-filter / run-runs-world-filter ids collided, so every label after the first frame pointed at frame 1's control. Derive them with useId() (same pattern as the capabilities chrome fix) and pin the contract in the fixture test: pairwise-distinct ids per control, each id unique in the document, each label resolving inside its own frame. Negative-checked by reverting the status filter id to a literal — the test goes red. I-000087: the 'both themes' tests asserted fixture-owned classes with css:false, which can never fail. Render each fixture under the Studio ThemeProvider's forcedTheme and assert the theme marker (<html>.dark) flips with it, plus the frames still render. Refs I-000086, I-000087.
…footer - zh-CN settings nav.workspace was left as the English 'Profiles'; use the repo's 个人档案 terminology. - compute-events-canvas: the journey returns to the Timeline by route, so the canvas remounts and refetches; the comment claimed timeline.all invalidation, which this path no longer exercises. - Surfaces index lists the three new rail-app fixtures and @web-developer/* in its source enumeration.
PR #445 SF-1 asked for the no-alias decision to be signed off explicitly rather than resting on an unrecorded premise. Record product decision D4 (2026-10-24): /settings/modules falls back to the default settings section, no redirect; the route only ever existed in in-app navigation in pre-release builds, and the disposition expires if the product ever ships with it reachable. Also corrects the recipe's hub-card import path after the presentational move. Refs I-000085.
auto-wood
left a comment
There was a problem hiding this comment.
⚠️ Verdict: needs fixes · Score 82% (good)
| Findings | Count |
|---|---|
| 🔴 must-fix | 0 |
| 🟠 should-fix | 1 |
| 🔵 nit | 5 |
| ❓ unverified | 0 |
📋 Review
What this PR does (round-2 increment 1959c45..1aaf7af, 12 files +88/−32): lands the round-1 fix wave — useId() for the Run Studio selects + negative-checked fixture assertion (SF-2), theme loops driven by ThemeProvider forcedTheme (SF-3), a D4 PM sign-off note in the knowledge recipe for the legacy /settings/modules disposition (SF-1), zh label, surfaces footer, stale comment, the hub-card move into presentational/, and a pre-round-2 jsdom scrollIntoView CI fix.
Revalidated findings
| Prior finding | State | Evidence |
|---|---|---|
| SF-1 /settings/modules alias (I-000085) | fixed | knowledge disposition section — D4 decision, PM sign-off 2026-10-24, corrected premise, expiry clause requiring a real alias if the product ever ships |
| SF-2 duplicate static ids (I-000086) | fixed | run-studio-chrome.tsx:188-190 three useId(); negative-checked test modules-rail-fixtures.test.tsx:37-58 |
| SF-3 fixture-owned theme assertions (I-000087) | fixed | forcedTheme is the real theme authority (theme-provider.tsx:45-56, effect :72-79); fixture-owned class assertions deleted |
| N-1 zh nav.workspace English | fixed | zh-CN/settings.json:9 "个人档案" |
| N-3 surfaces footer stale | fixed | surfaces.tsx:203-208, :552-557, :524-526 |
N-4 badge class/innerHTML pins + unanchored '3' |
still present | hub-card.test.tsx:55; modules-rail-fixtures.test.tsx:79 (disclosed) |
| N-5 stale invalidation comment | fixed | compute-events-canvas.test.tsx:168-171 |
| N-6 session-card relative import | fixed | R100 rename; session-card.tsx:17 ./hub-card; zero stale importers |
| N-2 registry source-shape assertions | still present | disclosed |
Findings
🟠 RunFormFields idPrefix="run-form" literal still duplicates ids across the 3-frame gallery (I-000086, occurrence 97)
- Evidence:
run-studio-chrome.tsx:246passes the literal;packages/nexus-ui/src/components/run-form-fields.tsx:148builds`${idPrefix}-${name}`withhtmlForbinding; all three fixture frames pass a proposal and renderRunFormFields(modules-rail-fixtures.tsx:455-500). - Impact:
run-form-attacker_id/run-form-defender_id(+-helper) appear 3×, so labels in frames 2/3 resolve to frame 1's controls — the same invariant SF-2 fixed for the selects, on the same acceptance surface. The new chrome comment (:186-187) overstates the fix's scope. - Merge class: should-fix
- Confidence: HIGH
- Fix sketch:
const formIdPrefix = useId();→idPrefix={formIdPrefix}; extend the fixture label list with the form-field labels so the negative check covers them.
🔵 Theme-test comments overstate what the assertion proves
- Evidence:
capabilities-rail-fixtures.test.tsx:110-112,modules-rail-fixtures.test.tsx:119-121— withcss: falsethehtml.darkmarker assertion proves the provider resolved the theme, not that the fixture adapts; visual parity stays gallery-only. - Merge class: nit · Confidence: HIGH
- Fix sketch: reword to "asserts the Studio theme authority drives the html.dark marker; visual parity stays gallery-only".
🔵 Rail apps enumeration omits the capabilities read-error variant
- Evidence:
surfaces.tsx:524-525says "Capabilities (registry, filter, empty)" while the section body renders a read-error state (:846-853). - Merge class: nit · Confidence: HIGH
- Fix sketch: add "error" to the enumeration.
🔵 hub-card.test.tsx left one directory away from its moved subject
- Evidence: component is now
presentational/hub-card.tsx; the test stayed atcomponents/developer/hub-card.test.tsx(import updated only). - Merge class: nit · Confidence: MEDIUM
- Fix sketch: move alongside
presentational/when the disclosed innerHTML pin is rewritten.
🔵 Disclosed not fixed (carried): registry source-shape assertions + badge class/innerHTML pins
- Evidence:
settings-section-registry.test.ts:15-27;hub-card.test.tsx:55innerHTML pin;modules-rail-fixtures.test.tsx:79frame-scopedgetByText('3'). - Merge class: nit · Confidence: HIGH
- Fix sketch: as agreed — product-behaviour rewrites or deletion.
Linked-issue AC
- I-000058 (trailing-space fold tripwire): met — unchanged; the increment does not touch the serve tests.
- I-000084 (Advanced-JSON open-state assertion): cut — still tracked open by the PR.
✅ Verified
merge-base --is-ancestor 1959c450 1aaf7af58— covering prior report, increment-only scope (325-line pack read in full).forcedThemeprop and thehtml.darkeffect confirmed intheme-provider.tsx— the new theme assertion has a real authority behind it.idPrefixduplication traced end-to-end: literal atrun-studio-chrome.tsx:246→ id generationrun-form-fields.tsx:148→ all three gallery frames render the form.- Zero stale
@/components/developer/hub-cardimporters (repo grep). - ❓ Negative-check runtime red/green not executed (read-only review; author reports it ran pre-push).
🗑️ Considered & rejected
- SF-1 "no alias" as unresolved: rejected — the D4 sign-off corrects the false premise, records the decision in the knowledge ADR-equivalent, and carries an expiry clause; valid disposition for a risk that requires the product to ship.
- NEW-1 as SF-2 "regressed": rejected — the select-side fix stands; the form-field residue is one new should-fix finding, captured as an occurrence on I-000086, not double-counted.
- hub-card
Linkimport as should-fix: rejected — knowledge item 2 now names the./hub-cardsibling exception; the whole-card link is product behaviour. (Adjudication note: the cited precedentshell-sidebar-chrome.tsxdoes not exist; the presentational-layer precedentdaemon-health-indicator-chrome.tsx:20uses renderer injection — the knowledge wording could name the Link exception explicitly.)
🛠️ Plan to fix
Expand fix plan
1. SF-R2 (I-000086): `useId()` for the RunFormFields idPrefix in `run-studio-chrome.tsx`; add the Attacker/Defender labels to the modules fixture id-uniqueness test so a literal idPrefix turns it red. Adjust the :186-187 comment scope.
2. Nit batch: reword both theme-test comments; add "error" to the surfaces Rail apps enumeration; move `hub-card.test.tsx` beside its subject with the product-behaviour rewrite of the innerHTML pin.
3. Verification: targeted vitest (modules-rail-fixtures, hub-card, registry) + `pnpm --filter web typecheck && pnpm --filter design-studio typecheck`.run-studio-chrome passed the literal idPrefix="run-form" into RunFormFields, so the gallery's three Run Studio frames emitted the same run-form-<field> ids and frames 2/3' labels resolved to frame 1's control. Derive the prefix from useId() like the sibling select ids, and extend the modules-rail fixture test to cover the schema field labels.
auto-wood
left a comment
There was a problem hiding this comment.
✅ Verdict: ship it · Score 94% (mergeable)
| Findings | Count |
|---|---|
| 🔴 must-fix | 0 |
| 🟠 should-fix | 0 |
| 🔵 nit | 5 |
| ❓ unverified | 0 |
📋 Review
What this PR does (round-3 increment 1aaf7af..f55af51, 2 files +12/−2): closes the round-2 blocking item — RunStudioChrome derives the Run form's id prefix via useId() and passes idPrefix={formIdPrefix}, and the modules gallery duplicate-id test extends its negative check to the schema field labels (Attacker/Defender).
Revalidated findings
| Prior finding | State | Evidence |
|---|---|---|
| SF-R2 RunFormFields literal idPrefix (I-000086 occ 97) | fixed | run-studio-chrome.tsx:191,247; fixture labels modules-rail-fixtures.test.tsx:52-53; dual red paths on revert (Set-size=1; [id=] ×3) |
| Theme-test comment overstatement (NEW-2) | still present | capabilities-rail-fixtures.test.tsx:110-112, modules-rail-fixtures.test.tsx:119-121 |
| Rail apps enumeration omits error variant (NEW-3) | still present | surfaces.tsx:524-525 |
hub-card.test.tsx one directory from subject |
still present | untouched |
| N-2 / N-4 disclosed carried nits | still present | disclosed |
The chrome comment (run-studio-chrome.tsx:186-191) is now accurate for the whole chrome — all four id bindings are useId()-scoped.
Findings
none new. The five carried nits remain listed above (deduct 6 points, cap 8) — none is merge-blocking; a cleanup pass can take them together.
Linked-issue AC
- I-000058 (trailing-space fold tripwire): met — unchanged.
- I-000084 (Advanced-JSON open-state assertion): cut — still tracked open by the PR.
✅ Verified
merge-base --is-ancestor 1aaf7af58 f55af51f6— covering increment scope (2-file pack read in full by main agent + quick seat).- Id chain end-to-end:
useId()prefix →run-form-fields.tsx:148→htmlFor/idpairs (entity-picker-field.tsx:71,88); chrome subtree has zero remaining static ids; repo-wide grep: zero consumers of the old literal. - Negative check judged genuinely red on revert via two independent assertions;
:rN:ids safe under the quoted selector. - ❓ Runtime red/green not executed (read-only review; author/CI report it ran).
🗑️ Considered & rejected
- Carried nits as verdict-relevant: rejected — nits alone cannot change the tally-derived verdict; they stay listed for the cleanup pass.
🛠️ Plan to fix
Expand fix plan
None required for merge. Optional cleanup pass (non-blocking):
1. Reword the two theme-test comments (provider marker vs visual parity).
2. Add "error" to the surfaces Rail apps enumeration.
3. Move hub-card.test.tsx beside its subject with the product-behaviour rewrite of the innerHTML pin; delete/replace the registry source-shape pins and the frame-scoped getByText('3').… residue Five carried PR #445 R3 nits, each with a recorded disposition: - theme comments (capabilities + modules rail fixtures) reduced to the one claim they prove: ThemeProvider applied the resolved `<html>.dark` marker. - rail-apps prose in the Studio surfaces page now enumerates the fixtures' own VARIANTS (Modules six states incl. the Advanced-JSON open frame, Capabilities four, Sessions two). - hub-card.test.tsx moved beside its subject and imports `./hub-card`. - registry source-shape pins trimmed to one order contract per list; the descriptor mirrors and naming loops are gone. - hub-card innerHTML string pin deleted (Badge owns that mapping); the ABI value assertion is scoped to its own DetailField row instead of the frame. Comment residue: the compute inspector points at `/modules?module=&run=` instead of "Settings → Modules", and the timeline invalidation note drops the canvas-behind-the-Settings-modal clause. The four re-sync comments and the sessions badge class pins stay by decision.
Summary
Completes the developer rail registry (操作台 / Harness / 模块 / 能力 / 会话) under the 2026-10-22 web-rebuild authorization. Three plans, each SDD-implemented with tri-QC + mandatory QA, serially merged to the integration branch:
885d8d5ca):/modulesbecomes a first-class rail app — Settings-modal ownership deleted outright (section, canonicalization, entrance row, modal-primary takeover; no redirect per product decision), page body migrated intocomponents/developer/with the pure-view chrome split (modules-page-chrome+run-studio-chrome), Run Studio + all deep links (?module=&run=,?world=&branch=) preserved, Studio fixture (5 variants, both themes), locale-parity, zero/settings/modulesresidue.40703cc55): capabilities + sessions lifted to the same generation — capabilities chrome split with locked copy decisions (registry-empty vs filtered-miss, Clear filter) and?filter=deep link intact; sessions table rebuilt as hub-card cards (field set per SessionSummary, whole-card link to run view,statusVariantreused as-is, F-F1 order pinned by DOM-order equality), fixtures + locale parity.abaccf2f3): I-000058 trailing-space fold tripwire (fails-first verified) + discovery-lifecycle green (comment fix, assertion semantics intact).Post-QC fixes: Q3-01 dead RunStudioCopy props (
4990fb08), QC3-R-1 duplicate filter input ids →useId()with negative-checked fixture test + QC3-R-2 (8bde1044). Close commit adds the thrice-verified rail-app rebuild recipe to knowledge.Verification
qc-consolidated.md/qa-report.md.Test plan
pnpm --filter web typecheck+pnpm --filter design-studio typecheck.mstar/sdd/v221-*/review/qa-report.md evidence)