Skip to content

OSS hardening 4/4: CI rebuilds the widget shell and fails on drift - #73

Closed
adamstankiewicz wants to merge 2 commits into
mainfrom
oss/ci-stale-artifact
Closed

OSS hardening 4/4: CI rebuilds the widget shell and fails on drift#73
adamstankiewicz wants to merge 2 commits into
mainfrom
oss/ci-stale-artifact

Conversation

@adamstankiewicz

Copy link
Copy Markdown
Owner

CI now rebuilds public/widget-shell.html and diffs it against the committed copy. The committed shell was stale — debate-ai and writing-workshop were silently broken in MCP hosts — which this both fixes and prevents: a registry change that forgets pnpm mcp:build fails the build instead of failing students.

Final part of the OSS hardening stack (#69#70 → 3/4 → this).

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
interactive-learning-experiences Ready Ready Preview Aug 30, 2026 5:55pm

Request Review

adamstankiewicz and others added 2 commits August 30, 2026 13:55
public/widget-shell.html is a committed build artifact by necessity (next
build copies public/ before mcp/build.mjs can run), and it rots silently:
this commit's rebuild proves it — the committed shell predated the two
newest widget kinds, so debate-ai and writing-workshop rendered 'No
renderer registered' in every MCP host. The rebuild is byte-deterministic
(verified: identical hashes across runs), so CI now rebuilds and diffs it
after the build step, with a fix-it message when it drifts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CI guard failed on its first real run, honestly: next/font's
@font-face rules carry build-environment-specific media hashes, so the
rebuild was never byte-deterministic. The rules were also dead weight —
a standalone single-file shell has no ../media/, so those fonts never
loaded. Stripping them fixes both: 4KB lighter, and consecutive builds
are now byte-identical (verified).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
adamstankiewicz added a commit that referenced this pull request Aug 30, 2026
mcp/shell.css imports the app's own globals.css and owns the few things
next/font would have provided (every --font-* variable gets a real
fallback stack — including the serif the warm-minimal branch needs — plus
host transparency). build.mjs compiles it with the Tailwind CLI instead
of lifting chunks out of .next, which:

- breaks the circular next-build dependency, so the shell runs as
  predev/prebuild and is never committed (gitignored) or manually rebuilt,
- is deterministic by construction — next/font never runs here, so there
  is no @font-face to strip and nothing environment-dependent to guard,
- single-sources theming: a retheme of globals.css lands in the shell
  automatically,
- and drops 20KB of CSS that was never reachable.

Verified: builds in ~90ms with no .next present; two consecutive builds
byte-identical; zero url() references; theme tokens, dark block, and
focus styles present; full pnpm build green end-to-end.

Supersedes the committed-artifact staleness guard (#73's CI step) once
both merge — a build product can't go stale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamstankiewicz

Copy link
Copy Markdown
Owner Author

Superseded by #86, unmerged on purpose: every change here existed to police the committed public/widget-shell.html artifact — the CI rebuild-and-diff guard, the @font-face determinism strip, and a rebuilt shell blob. #86 removes the artifact from git entirely (the shell becomes a prebuild product compiled from mcp/shell.css), so all three become dead code before they could merge.

Nothing is lost in the trade: CI's existing pnpm build step now inherently builds the shell, so a breakage fails the build directly — a stronger check than diffing a committed copy. The determinism analysis this PR produced (next/font media hashes as the one environment-dependent input) is what motivated #86's design, where the problem is gone by construction rather than stripped by regex.

🤖 Generated with Claude Code

@adamstankiewicz
adamstankiewicz deleted the oss/ci-stale-artifact branch August 30, 2026 18:08
adamstankiewicz added a commit that referenced this pull request Aug 30, 2026
mcp/shell.css imports the app's own globals.css and owns the few things
next/font would have provided (every --font-* variable gets a real
fallback stack — including the serif the warm-minimal branch needs — plus
host transparency). build.mjs compiles it with the Tailwind CLI instead
of lifting chunks out of .next, which:

- breaks the circular next-build dependency, so the shell runs as
  predev/prebuild and is never committed (gitignored) or manually rebuilt,
- is deterministic by construction — next/font never runs here, so there
  is no @font-face to strip and nothing environment-dependent to guard,
- single-sources theming: a retheme of globals.css lands in the shell
  automatically,
- and drops 20KB of CSS that was never reachable.

Verified: builds in ~90ms with no .next present; two consecutive builds
byte-identical; zero url() references; theme tokens, dark block, and
focus styles present; full pnpm build green end-to-end.

Supersedes the committed-artifact staleness guard (#73's CI step) once
both merge — a build product can't go stale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
adamstankiewicz added a commit that referenced this pull request Aug 30, 2026
* experiment: does the shell survive as a post-build product?

If the Vercel preview serves /widget-shell.html despite it being absent
from git, output collection happens after the build command — and the
committed artifact, the manual mcp:build step, and the CI staleness
guard can all be deleted in favor of the build building the shell.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: the widget shell builds itself, themed from one source

mcp/shell.css imports the app's own globals.css and owns the few things
next/font would have provided (every --font-* variable gets a real
fallback stack — including the serif the warm-minimal branch needs — plus
host transparency). build.mjs compiles it with the Tailwind CLI instead
of lifting chunks out of .next, which:

- breaks the circular next-build dependency, so the shell runs as
  predev/prebuild and is never committed (gitignored) or manually rebuilt,
- is deterministic by construction — next/font never runs here, so there
  is no @font-face to strip and nothing environment-dependent to guard,
- single-sources theming: a retheme of globals.css lands in the shell
  automatically,
- and drops 20KB of CSS that was never reachable.

Verified: builds in ~90ms with no .next present; two consecutive builds
byte-identical; zero url() references; theme tokens, dark block, and
focus styles present; full pnpm build green end-to-end.

Supersedes the committed-artifact staleness guard (#73's CI step) once
both merge — a build product can't go stale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <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