Skip to content

fix(story-gate): thread consumer plugins into every project, not the root - #1184

Closed
schickling-assistant wants to merge 1 commit into
schickling-assistant/2026-09-02-stylex-focus-ringfrom
schickling-assistant/2026-09-02-gate-project-plugins
Closed

schickling-assistant wants to merge 1 commit into
schickling-assistant/2026-09-02-stylex-focus-ringfrom
schickling-assistant/2026-09-02-gate-project-plugins

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator

Top of stack #1177, on #1183. Fixes the reason the ptg story gate has never executed a story on any pin — a fact carried across eight PRs and a whole migration with no cause.

The defect

Vitest projects do not inherit root-level plugins. Each project is its own Vite config.

createStoryGateConfig returns a bare project config for one theme and { test: { projects } } for several. So the documented consumer pattern —

defineConfig(mergeConfig(createStoryGateConfig({ themes: [...] }), {
  plugins: [createStylexVitePlugins()],
}))

— works by accident in the single-theme case, where the merge target is the project, and silently stops applying the moment a second theme is added. The plugins land beside test where nothing reads them, and every story fails to load with a runtime error from untransformed source.

The old comment in each consumer said the plugin "has to be registered here rather than inherited". That was correct, and registering it "here" is exactly what does not work once there is more than one theme. A correct instruction that cannot be followed reads as a followed instruction.

Measured

Same host, same devenv shell, minutes apart:

consumer themes shape returned result
effect-schema-form-aria none bare project config 4 stories ran, 4 distinct references captured
apps/ptg 2 { test: { projects } } dies at import, Tests no tests

Threading the same plugins per-project moved ptg from Tests no tests to 10 stories executed. The metric that moved is whether anything ran, which is all the diagnosis needed.

The change

  • A plugins option, applied to every project, ordered before the Storybook plugin because a compiler transform has to see the source first.
  • Storybook plugin construction becomes an injected seam. storybookTest eagerly loads a real Storybook config directory, and the invariant worth guarding is where plugins land — independent of what they are.
  • The in-repo consumer migrates off mergeConfig.

The test is proven live, not merely passing

Reverting the placement to the pre-fix form fails 3 of its 4 cases. It also:

  • asserts per project, and separately that the root does not carry them — so a refactor cannot satisfy it by duplicating plugins everywhere;
  • carries a negative control (no plugins supplied) so the threading cannot become load-bearing for consumers needing no transform;
  • imports project.ts relatively, so it is immune to workspace link direction.

What is not verified, and how I know

The effect-schema-form-aria config migration is unverified. In my worktree that package resolves @overeng/utils to a sibling checkout rather than to this source, so its clean typecheck says nothing about this change.

I found that with a negative control: a deliberately bogus option produced no type error either, which means the check could not have caught a real mistake at that call site. The green was uninformative, not reassuring.

It is a four-line mechanical migration and CI installs properly. Flagging it beats presenting a pass that cannot fail.

Follow-ups, both filed

  1. ptg's consumer still needs migrating — separate repo, blocked on this landing.
  2. Blocker 2 is independent and still open. With the transform applied, all ten ptg stories then throw Cannot read properties of null (reading 'useState') — a null hook dispatcher from a third-party component resolving through a sibling worktree's store, because ptg's node_modules is a symlink. Duplicate React, same mechanism diagnosed earlier in this migration. R08 needs both fixes.
Posted on behalf of @schickling
field value
agent_identity dev3.direct.omp.mhx73ur2
session dev3.mhx73ur2
agent_persona generalist
agent_supervisor unavailable
agent_tool OMP
agent_tool_version 18.0.11
agent_runtime OMP 18.0.11
tooling_profile dotfiles@e8f0615-dirty

…root

Vitest projects do not inherit root-level plugins -- each project is its own
Vite config. createStoryGateConfig returns a bare project config for one theme
and {test:{projects}} for several, so the documented consumer pattern of merging
plugins into the returned config works by accident in the single-theme case,
where the merge target IS the project, and silently stops applying the moment a
second theme is added: the plugins land beside 'test' where nothing reads them
and every story fails to load from untransformed source.

This is why the ptg story gate has never executed a story on any pin, a fact
carried across eight PRs with no cause. Measured on one host minutes apart: the
one-theme consumer ran 4 stories and captured 4 distinct references; the
two-theme consumer reported 'Tests no tests'. Threading the same plugins
per-project moved it to 10 stories executed.

Adds a 'plugins' option applied to every project, ordered before the Storybook
plugin because a compiler transform must see the source first. The Storybook
plugin construction becomes an injected seam, because storybookTest eagerly
loads a real Storybook config directory and the invariant worth guarding is
WHERE plugins land, which is independent of what they are.

The test is proven live rather than merely passing: reverting the placement to
the pre-fix form fails 3 of its 4 cases. It asserts per project and also that
the root does NOT carry them, so a refactor cannot satisfy it by duplicating
plugins everywhere. It imports project.ts relatively, so it is immune to
workspace link direction.

VERIFIED: @overeng/utils tsc --noEmit clean; the 4 unit cases pass and 3 fail on
the pre-fix placement.

NOT VERIFIED: the effect-schema-form-aria config migration. In this worktree
that package resolves @overeng/utils to a sibling checkout rather than to this
source, so its green says nothing about this change -- confirmed by a negative
control, where a deliberately bogus option produced no type error either. The
change is a four-line mechanical migration and CI installs properly; flagging it
rather than presenting an uninformative pass.
@schickling-assistant schickling-assistant changed the title schickling assistant/2026 09 02 gate project plugins fix(story-gate): thread consumer plugins into every project, not the root Sep 2, 2026
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Storybook Previews

Subject Status Report Details Updated
notion-cli success notion-cli preview deployed Preview is ready 2026-09-02 09:13 UTC
effect-react success effect-react preview deployed Preview is ready 2026-09-02 09:13 UTC
genie success genie preview deployed Preview is ready 2026-09-02 09:13 UTC
react-inspector success react-inspector preview deployed Preview is ready 2026-09-02 09:13 UTC
tui-react success tui-react preview deployed Preview is ready 2026-09-02 09:13 UTC
effect-schema-form-aria success effect-schema-form-aria preview deployed Preview is ready 2026-09-02 09:13 UTC
notion-md success notion-md preview deployed Preview is ready 2026-09-02 09:13 UTC
notion-react success notion-react preview deployed Preview is ready 2026-09-02 09:13 UTC
megarepo success megarepo preview deployed Preview is ready 2026-09-02 09:13 UTC
Report history

PR 1184 · 2026-09-02 09:14 UTC

Subject Status Report Details Updated
notion-cli success notion-cli preview deployed Preview is ready 2026-09-02 09:13 UTC
effect-react success effect-react preview deployed Preview is ready 2026-09-02 09:13 UTC
genie success genie preview deployed Preview is ready 2026-09-02 09:13 UTC
react-inspector success react-inspector preview deployed Preview is ready 2026-09-02 09:13 UTC
tui-react success tui-react preview deployed Preview is ready 2026-09-02 09:13 UTC
effect-schema-form-aria success effect-schema-form-aria preview deployed Preview is ready 2026-09-02 09:13 UTC
notion-md success notion-md preview deployed Preview is ready 2026-09-02 09:13 UTC
notion-react success notion-react preview deployed Preview is ready 2026-09-02 09:13 UTC
megarepo success megarepo preview deployed Preview is ready 2026-09-02 09:13 UTC

schickling-assistant added a commit that referenced this pull request Sep 2, 2026
Caught by accident and worth recording as a trap: PR #1185's branch was forked
from #1183's branch rather than from #1184's, so it never contained #1184. The
stack still LOOKED right, because 'gh stack link' set #1185's base to #1184 and
GitHub computes a PR diff from the merge-base -- which was #1183's tip, so
#1185's diff was exactly its own commit and read as correctly stacked.

Setting a base does not make a branch contain that base. A clean-looking PR diff
is equally consistent with 'correctly stacked' and 'forked from a sibling', and
only ancestry distinguishes them: 'git merge-base --is-ancestor' over all eight
heads found seven IN and one MISSING.

It surfaced because a liveness probe tried to back up
gate/project.unit.test.ts, the backup failed silently, and the file turned out
to be untracked -- so a broken 'cp' in a throwaway check is what revealed a
missing PR.

The conflict was cleanly disjoint and both sides were needed: this branch
carries the React alias/pin work (reactAliasRules, pinReactToConsumer) that
makes cross-checkout consumers render at all, and #1184 carries the plugins
option and the storybookPluginFor seam. Resolved by taking this side and
re-applying #1184's threading, with the React pin kept FIRST so its alias
applies to whatever a caller's transform emits.

VERIFIED: both feature sets present, zero markers, and the plugin-placement
unit test passes 4/4 in the merged tree -- the same test that fails 3 of 4 when
the placement is reverted.
@schickling-assistant

Copy link
Copy Markdown
Collaborator Author

Superseded by #1191, which collapses this stack onto main as one tree.

Closing rather than merging: propagating main through eight stacked branches meant re-reconciling the lockfile, the fixed-output hashes and the Buck projection once per branch. Branch 2 alone produced 11 conflicts, a semantically broken auto-merged lockfile, a BUCK admission rename and a circular tooling block. On one tree each of those happens once.

This PR's content is in #1191, verified with git merge-base --is-ancestor over all eight heads — #1184 was found missing that way and merged in, because a gh stack base does not make a branch contain that base.

This body stays as the record of the per-change evidence, which #1191 summarises but does not reproduce in full.

Posted on behalf of @schickling
field value
agent_identity dev3.direct.omp.mhx73ur2
session dev3.mhx73ur2
agent_persona generalist
agent_supervisor unavailable
agent_tool OMP
agent_tool_version 18.0.11
agent_runtime OMP 18.0.11
tooling_profile dotfiles@e8f0615-dirty

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