Skip to content

release: 1.0.0-alpha.13 — starter lifecycle bin and artifact hygiene train - #1588

Merged
SisyphusZheng merged 12 commits into
mainfrom
release/alpha13-artifact-honesty
Oct 10, 2026
Merged

SisyphusZheng merged 12 commits into
mainfrom
release/alpha13-artifact-honesty

Conversation

@SisyphusZheng

@SisyphusZheng SisyphusZheng commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Summary

The 1.0.0-alpha.13 train: the generated starter's lifecycle rides a real package bin, the scaffold's dependency surface matches what the docs say, and the build output drops two artifact defects.

Lifecycle contract

  • @openelement/router now ships an openelement bin (build / start [--mode=preview]), declared through the packed manifest (packedMetadata() / PACKAGE_BINS, with the entry path sourced from the same pack-layer constant that puts the module in the tarball). The starter's build/start scripts run the short commands through the installed .bin shim — no installed-tree paths in scripts, READMEs, or CLI hints.
  • The starter's preview script is removed. The default scaffold has request-time routes, so static-only preview always refused; the mode remains available as openelement start --mode=preview for builds without a server side, and the refusal message points at openelement start.
  • tailwind.theme / tailwind.components sources now resolve app-relative against the project root, matching the documented contract. The template declares ['app/styles/theme.css']; hand-counted ../../ climbs are gone. Disambiguation is lexical plus filesystem (a source that exists under the app root is the app file; @/# specifiers pass through), so tailwindcss/theme.css style package sources keep working.

Starter dependencies

  • The two @openelement/* deps use caret ranges (^1.0.0-alpha.13) over exact pins: a fresh install resolves the current release, pnpm update picks up later 1.0.0 prereleases and the final 1.0.0.
  • @hono/vite-dev-server and hono moved to devDependencies (they are dev-server-only surface; build/start never resolve them), and @playwright/test is removed from the scaffold. Docs updated to match.

Build output

  • The zero-byte, unreferenced _virtual_open-build-trigger-* chunk no longer ships in dist/.
  • The inner client build sets publicDir: false, so public/ is no longer copied into dist/client/ — every static asset previously shipped at a second URL, byte-identical and differently cached.

Docs and DX

  • Getting-started no longer teaches a nonexistent --no-tailwind flag (en + zh); the configuration guide documents the tokens.css vs theme.css relationship (en + zh); 404 build semantics corrected across the affected guides and template comments (hybrid builds render the not-found route via the request-time server; static-only builds write dist/404.html); the create README's Tailwind self-contradiction and stale version prose are fixed.
  • The create handoff box prints the generator version; dev logs drop the staging-entry absolute path and the glued site-title suffix.
  • Publish-surface scans extended (node node_modules/... command form, template-script file-path form, mutually-exclusive contract terms) with red fixtures plus a live-green run, so these classes fail in the gate instead of shipping.

Release

Version bumped to 1.0.0-alpha.13 across all stamp sites and workspace members (dry-run roster verified against the previous train's bump commit); CHANGELOG entry added.

Verification

  • Full suites: router (1151), element, create (36), tools (380+), www (170 after site build) — green.
  • pack:dry-run → pack-surface:check (incl. new bin-equality and missing-bin-target scans) → package-artifacts:check (new rules) green on real tarballs; the packed router tarball carries bin: { openelement: ./src/cli/cli.js } at 1.0.0-alpha.13.
  • consumer:packaged end-to-end green: scaffolds from the packed create, installs the packed tarballs, exercises dev/check/test/build/start, the 3-browser matrix, preview refusal, and build equivalence between bin and subpath entries.
  • starter-smoke browser suite 27/27 across chromium/firefox/webkit; bin dispatch equivalence pinned by packages/router/__tests__/cli-bin.test.ts; the consumer probe's Playwright pin anchored to the root pin by test.
  • fmt / lint / typecheck / interface snapshot / content-dates / api-reference checks clean.

Pre-existing (reproduced on pristine main, not introduced here)

  • @openelement/www#check:routes-types fails with 22 TS2307 ./x.css errors — identical on a clean main worktree.
  • One webkit ARIA assertion in the request-time fixture's browser matrix flakes identically on main.

Both are worth separate follow-ups; neither is touched by this change.

E lane (appended): www fact layer, homepage redesign, registry-truth machinery

Fact layer. Version notes, roadmap publish status and the getting-started note no longer assert registry state from a tracked block that can lag: they state the invariant and point at npm view @openelement/create dist-tags. Package counts derive from the release-state packages array (hand-written counts eliminated); the ambiguous "no single stable version" caption now says what it means; stat-copy calibrated ("3 browser engines in CI", "0 third-party runtime deps", WinterCG server entry wording). en + zh.

Homepage (bounded redesign). One-line version block (v1.0.0-alpha.13 · six packages · one version, mono/nowrap, linked to npm); sentence-form stats strip at readable contrast; marquee aria-hidden with reduced-motion stop and hover pause; top nav gains API and Roadmap; code-block scrolling consolidated; the §1 double scrollbar and a WebKit slotted-@scope styling seam fixed and e2e-pinned across three engines.

Registry-truth machinery. The publish train ends with a registry read-back written into the tracked release state and the site's display constants (single-writer module shared with both drift checks), with a second read-back after the alpha alias re-point so both tags are recorded as served. The synced files land via a maintainer PR from the release artifact; until they land, the gate:release drift check stays red. A new registry-drift:check verifies displayed versions against the live registry — fail-closed on drift, explicit SKIP only when offline, proved findings never dropped on network blips. The tracked block now carries the real registry truth, which is what fixes the stale version table on the homepage.

Cleanup. The routes-types gate typechecks again (css-modules ambient types added; 22 TS2307 gone, no hidden errors surfaced); the zag-combobox spec waits on island definition explicitly, with the WebKit root cause (loader race under shared-chunk TLA) documented for a follow-up; HMR test deadline raised for 2-core CI; openelement version / --version added; starter README notes pnpm's supply-chain bookkeeping file. CHANGELOG extended accordingly.

F lane (appended): dev/build styling parity + hero scrim

The site's Tailwind preset now rides the framework's config-file tailwind key instead of a build-only consumer plugin, so dev serves the same compiled theme sheet the build ships — local development shows the real site. The hero scrim covers the photograph band's full height (the stats copy previously sat on raw photography at 2.7:1 dark / 1.3:1 light), the ramp starts earlier, and the hero box clips the stage's exit transform so it can no longer spill over the stats strip and marquee bands; e2e pins both.

G lane (appended): the dev preset channel owns no file (KR-5)

The build half's inner vite build empties .openElement/tailwind-preset/, which used to be where the dev channel staged its entry — a dev server that had run beside any build answered the stylesheet link with 404 until restarted. The dev channel now serves the entry as an in-memory module at the same URL (resolveId/load over the same generator the build compiles), writes nothing to disk, and keeps the HMR contract; a regression test replays the incident end to end.

H lane (appended): self-hosted fonts, the error document, mobile overflow

  • Fonts are npm dependencies (byte-identical to the CDN pins): the Tailwind preset bundle compiles the fontsource stylesheets and ships each woff2 as a hashed same-origin asset. The jsDelivr render-blocking links are gone — a hung CDN no longer stalls first paint (old build never fires load under a hung CDN; new build 121ms), hero glyphs pixel-identical, and no artifact may reference cdn.jsdelivr.net (test-enforced).
  • The static serve path answers a miss with the build's own 404.html (and locale copies) at a 404 status; /404.html itself answers 404 — the error document is never a 200 page. Dynamic builds keep their own 404. One seam in static-serve.ts covers the CLI and every fixture server.
  • Long-form pages stop scrolling horizontally at 390px: unwrappable generated URL tokens and inline identifiers get break opportunities, the contributing fence owns its scroll, grid tracks get a real floor — all 68 built routes sweep 0px at 390/768, with an e2e guard and negative proofs. The /reference per-package split is documented as a follow-up (route/nav/search re-homing decisions).

I lane (appended): package islands work packed; ErrorBoundary compiles; trustedHtml islands; grammar docs

  • packageIslands works from packed artifacts end to end: the plugin's config hook places the derived ssr.noExternal on the resolved config, and the compiler recovers the style edges of its own emitted artifacts so client builds emit real .css assets instead of MISSING_EXPORT. A new packed-consumer UI appliance rides consumer:packaged (pack → scaffold → open-button/dialog/code-block app → dev/build/start), with both detectors proven by mutation — the flagship "consume @openelement/ui as package islands" story now holds outside the workspace.
  • The compiler admits ErrorBoundary as a canonical base: the README pattern (subclass, hasError branch, catchError/retry) compiles verbatim; the README sentences themselves are pinned unchanged by a test.
  • trustedHtml property defaults survive island SSR (the seed resolves the class's own branded default when the metadata projection is null).
  • The action success channel is documented as what it is — 303 PRG by contract; the never-emitted success envelope is removed from the wire type, and the router guide shows the loader-side success-data pattern (en + zh).
  • New guide page "The compiled module grammar" (en + zh): thirteen enforced laws, each traced to its OEC diagnostic site with a compiled positive example and the real rejection; the tag-name JSX diagnostic now explains itself in plain words.

…train

Lifecycle contract:
- Router ships an `openelement` bin (build / start [--mode=preview]) declared
  through the packed manifest; the generated starter's scripts run the short
  commands through the installed .bin shim instead of installed-tree paths.
- The starter's preview script is removed: the default scaffold has
  request-time routes, so static-only preview always refused; the mode stays
  available as `openelement start --mode=preview` for builds without one.
- `tailwind.theme`/`tailwind.components` sources resolve app-relative against
  the project root, matching the documented contract; the template declares
  `app/styles/theme.css` (no more hand-counted ../.. climbs).

Starter dependencies:
- Caret ranges (^1.0.0-alpha.13) over the release line instead of exact pins;
  `pnpm update` picks up later 1.0.0 prereleases and the final 1.0.0.
- `@hono/vite-dev-server` and `hono` moved to devDependencies (dev-only
  surface); `@playwright/test` removed from the scaffold.

Build output:
- The zero-byte `_virtual_open-build-trigger-*` chunk no longer ships in dist.
- The client build no longer copies public/ into dist/client/ (no duplicated
  assets under a second URL).

Docs and DX:
- Getting-started no longer teaches a nonexistent --no-tailwind flag;
  configuration documents the tokens.css vs theme.css relationship; the 404
  build semantics are corrected (hybrid renders via the request-time server);
  create README's Tailwind self-contradiction and stale version pins fixed.
- The create handoff box prints the generator version; quieter dev logs.

Release: version bumped to 1.0.0-alpha.13 across all stamp sites and workspace
members; CHANGELOG entry added; publish-surface scans extended (installed-tree
command form, template-script path form, mutually exclusive contract terms)
with red/green fixtures.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Deploying openelement with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3b940e9
Status: ✅  Deploy successful!
Preview URL: https://3c0eca83.lessjs.pages.dev
Branch Preview URL: https://release-alpha13-artifact-hon.lessjs.pages.dev

View logs

…13 E lane)

Fact layer (E1):
- The version note, roadmap publish status and getting-started note no longer
  assert registry state from a possibly-stale tracked block: they state the
  invariant truth and point at `npm view @openelement/create dist-tags`.
- Package counts derive from the release-state packages array (six) instead
  of hand-written numbers; the ambiguous "no single stable version" caption
  now says what it means (prerelease line, no stable release yet, one shared
  version); "3 browser engines in CI", "0 third-party runtime deps", the
  WinterCG server entry wording replace the vague originals. en + zh.

Homepage (E2, bounded):
- One-line version block (v1.0.0-alpha.13 · six packages · one version,
  mono/nowrap, linked to npm) replaces the six-fold repeated version list.
- Sentence-form stats strip at readable contrast; marquee aria-hidden with
  prefers-reduced-motion stop and hover pause; top nav gains API and Roadmap;
  code blocks scroll in one place; §1 double scrollbar and the WebKit
  slotted-code @scope seam fixed (surface moved to the doc layer, e2e-pinned
  across three engines).

Registry truth machinery (E3):
- Publish train ends with a registry read-back written into release-state and
  the site's display constants (registry-sync.ts, the one writer both drift
  checks read); a second read-back after the alpha alias re-point records
  both tags as served. Synced files land via maintainer PR from the release
  artifact; until they land, the gate:release drift check stays red.
- New registry-drift:check verifies displayed versions against the live
  registry (fail-closed on drift, explicit SKIP only when offline, proved
  findings never dropped on network blips).
- The tracked registry block and site constants now carry the real registry
  truth (alpha.12) — fixing the stale homepage version table.

Cleanup (E4):
- routes-types gate: add the css-modules ambient types (22 TS2307 gone).
- zag-combobox spec: explicit island-defined waits; WebKit root cause
  (loader race under shared-chunk TLA) documented in the local ledger.
- HMR test deadline 30s; `openelement version` / `--version`; starter README
  pnpm supply-chain note. CHANGELOG extended.
…3 F lane)

- The site's Tailwind preset now rides the framework's config-file `tailwind`
  key (openelement.config.ts) instead of a build-only consumer plugin: dev
  serves the compiled theme sheet from /.openElement/tailwind-preset/entry.css
  and links it, the build emits the same compile as /assets/open-tailwind.css
  — both channels deliver one fact, and local dev shows the real site.
- Hero scrim: the darkening gradient now covers the photograph band's full
  height (previously only the lower third; stats copy sat on raw photography
  at 2.7:1 dark / 1.3:1 light), the ramp starts earlier, and the hero box
  clips the stage's exit transform so it can no longer spill over the stats
  strip and marquee bands. e2e gains a band guard pinning both.
…er take the dev channel down (KR-5)

The build half's inner vite build runs with emptyOutDir against
.openElement/tailwind-preset/, so any `vite build` emptied the directory the
dev channel staged its entry at: a dev server that had run beside a build
answered the document's stylesheet link with 404 until restarted (reproduced
on the site, alpha.13 F lane).

The dev channel now owns no file. resolveId maps the contract URL — in both
spellings Vite hands plugins (absolute path and root-relative URL, query
suffix preserved) — onto the entry's identity path, and load serves the
generated text per request (the same renderStagedPresetEntry the build
compiles, so both channels keep one compile input). configResolved writes
nothing. URL contract, HMR css-update semantics (#1582) and the OFF state
are unchanged.

The regression test pins the incident: dev up, no file on disk, a stale
staging write is not the served source, and after wiping the directory both
the ?t= and plain URL forms still answer the compile.
Comment thread packages/router/__tests__/dev-tailwind-preset.test.ts Fixed
…rflow (alpha.13 H lane)

- Fonts are npm dependencies now (@fontsource 5.3.0, byte-identical to the
  CDN pins): the Tailwind preset bundle compiles the package stylesheets and
  emits each woff2 as a hashed same-origin asset. The four jsDelivr
  render-blocking links are gone — a hung CDN no longer stalls first paint
  (measured: old build never fires load under a hung CDN; new build 121ms),
  and hero glyphs are pixel-identical. font-delivery/build-output tests pin
  the new contract; no artifact may reference cdn.jsdelivr.net.
- The static serve path answers a miss with the build's own 404.html (and
  its locale copies) with a 404 status, and /404.html itself answers 404 —
  the error document is never a 200 page. Dynamic builds keep their own 404
  (dist/server takes precedence). One seam in static-serve.ts covers the CLI
  and every fixture server; registered in seams.md with consumer tests.
- Long-form pages no longer scroll horizontally at 390px: unwrappable
  generated URL tokens and inline identifiers get break opportunities, the
  contributing fence owns its scroll, grid tracks get a minmax(0,1fr) floor.
  All 68 built routes sweep 0px at 390/768; an e2e guard pins the four
  pages with a negative proof. The /reference split remains a documented
  follow-up (needs route/nav/search re-homing decisions).
Comment thread www/__tests__/build-output.test.ts Fixed
…ompiles, trustedHtml islands SSR (alpha.13 I lane)

- packageIslands now works from packed artifacts, end to end: the plugin's
  config hook places the derived ssr.noExternal on the resolved config (dev
  SSR bundles declared packages), and the compiler recovers the style edges
  of its own emitted artifacts so client/dev builds emit real .css assets
  instead of failing MISSING_EXPORT. A new packed-consumer UI leg rides
  consumer:packaged (pack -> scaffold -> open-button/dialog/code-block app
  -> dev/build/start), with both detectors proven by mutation.
- The compiler admits ErrorBoundary as a canonical base (README pattern
  compiles verbatim: subclass, hasError branch, catchError/retry). The base
  publishes kernel captures into a now-writable hasError field; the README
  sentences are pinned unchanged by test.
- trustedHtml @Property defaults survive island SSR: the seed resolves the
  class's own branded static-props default when the metadata projection is
  null — the exact /tmp/allpkgs failure now builds, serves and hydrates the
  branded markup.
- The action success channel is documented as what it is (303 PRG; the
  never-emitted success envelope is removed from the wire type) and the
  router guide shows the loader-side success-data pattern (en + zh).
- New guide page "The compiled module grammar" (en + zh): thirteen enforced
  laws, each traced to its OEC site with a compiled positive example and the
  actual rejection. The tag-name JSX diagnostic now says so in plain words.
…(CodeQL)

The null guard above the loop already skips null candidates, so the
`candidate !== null` inside the object branch was a dead comparison
(js/comparison-between-incompatible-types). Behavior is unchanged.
….13 J)

The README sentence led readers to a two-return render with a binding inside
the branch — three natural readings, all rejected by the compiler. The
section now states the compilable shape (re-declared @Property hasError,
single-return render with a fully static ternary, catchError sets the flag)
and points at the compiled grammar guide. The admission test pins the new
sentences (the contract is unchanged: what the README teaches must compile),
and the grammar guide gains the failure-injection law — throws live in
lifecycle methods, never in render() (en + zh).
…scan (CodeQL)

#4226 (js/file-system-race): the dev-channel-writes-no-file assertion was an
existsSync check on the same path the test then writeFileSync's a stale
staging fixture onto — the TOCTOU shape. Assert via readFileSync throwing
ENOENT instead: no exists->write pairing, and a strictly stronger claim.

#4227 (js/incomplete-url-substring-sanitization): the jsDelivr scan was a
bare substring test. Anchor it on the URL host boundary
(scheme-or-protocol-relative //cdn.jsdelivr.net followed by a delimiter or
end) so the predicate is complete.
…rappers, signal-write routing, SSR fallback

The compiled model compiled ErrorBoundary and its manual catchError/retry/
reset channel, but no automatic capture existed: authored lifecycle throws
were emitted verbatim and escaped to window.onerror, a property write whose
signal-graph evaluation threw escaped through the facade setter, kernel
update failures were captured only into the element-local service with no
ancestor routing, and SSR had no boundary at all — a subtree evaluation
throw 500'd the route while the README claimed the boundary renders a
fallback.

- OpenElement gains a protected _captureError(error) that climbs the
  composed ancestor chain (crossing shadow boundaries via the host) to the
  nearest isErrorBoundary and captures through that boundary's own error
  service, so publication rides the existing onError -> hasError path: the
  fallback Region flips exactly as an application catchError() call. With
  no boundary it returns false and callers keep today's rethrow semantics.
- The compiler wraps the seven platform lifecycle callback bodies in
  try/catch -> _captureError at emission time (pay at compile time; the
  body's source-map segments stay byte-identical, the scaffolding lines
  stay unmapped; module ABI 1 -> 2).
- The facade property setter routes synchronous signal-graph evaluation
  throws — handler-driven and external writes alike.
- The kernel's Region update-error sink additionally routes non-boundary
  elements to the nearest boundary.
- SSR: a subtree throw at renderDsd re-serializes the nearest boundary's
  program with hasError forced true — the program's own when-Region
  renders the fallback branch in place of the failed subtree; without a
  boundary the route still fails loudly.

Seams registered in docs/architecture/seams.md with consumer-form tests
(element/compiler/router suites incl. Chromium).
…cument context and the page-scope contract

- ui README and CUSTOMIZATION.md advertised open-card/open-badge/
  open-callout/open-tabs, which do not ship (the package exports button,
  input, code-block, theme-toggle, dialog, dropdown only). Removed, with
  the customization guard's pinned surface updated in the same change per
  its own documented protocol.
- compiled-grammar's canonical boundary example used a named export; the
  static-component scanner registers only the module's default export tag,
  so the verbatim example never SSR-expanded. Now default-exported, with
  the registration asymmetry documented: page-scope components register
  through the route import chain, islands through the template scan.
- The context example referenced a starter file that does not exist and
  imported consumeContext from the wrong module. Replaced with a worked
  provider->consumer example against the real API, stating the grammar
  constraint that forces the subscribe-and-copy shape.
- islands-and-ssr now states the page-scope contract explicitly:
  non-island components are server-rendered only by design — no client
  code, no upgrade, no runtime lifecycle; interactivity is the island
  form.
…e --tw-shadow dev/build delta

--mode=preview delegated to the app's own vite preview, whose index.html
fallback answered 200 for every miss and even for extensionless pretty
URLs like /about — two static serving modes disagreeing. Preview now
rides the same createFetchHandler/serveFetch dispatch as start: pretty
URLs resolve the real page, a miss answers 404 + the build's 404.html,
/404.html itself answers 404. The vite-preview ergonomics stay
(--port/--host/--strictPort, the 4173 default, next-port probing, and a
loopback-only default bind — start remains the 0.0.0.0 deployment
server).

The dev/build Tailwind parity test now pins --tw-shadow explicitly. The
peer's serve and build variants order @layer properties differently
around utilities and differ only in minified literal spellings; after
normalization the declaration sets are equal. The three inherent delta
classes are documented in the test, and anything beyond them fails.
@SisyphusZheng
SisyphusZheng merged commit 6173669 into main Oct 10, 2026
13 checks passed
@SisyphusZheng
SisyphusZheng deleted the release/alpha13-artifact-honesty branch October 12, 2026 15:02
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.

2 participants