Skip to content

chore: synchronize dev with main after alpha6 - #1475

Closed
SisyphusZheng wants to merge 6 commits into
devfrom
main
Closed

SisyphusZheng wants to merge 6 commits into
devfrom
main

Conversation

@SisyphusZheng

Copy link
Copy Markdown
Member

Summary

Synchronize the protected dev branch with the merged alpha6 main branch.

Verification

…me convergence (#1459)

* ci: trim advisory jobs from default PR workflow

* feat(alpha5): part-level streaming, WC admission tiers, runtime consolidation

Consolidates the alpha5 framework train (local implementation; CI pending):

- Streaming: opt-in compiled server executor with route-local defer
  manifests (#1450), generated Request->Response handler with preflush
  gates/cancel/backpressure (#1447), browser backfill installer and
  late part claim (#1448), navigation FSM stream ownership (#1449),
  shared stream-frame admission policy, Nitro Node/Workers proofs.
- WC admission: ADR-0157 tier diagnostics with third-party fixture
  evidence (#1451) and T1 snapshot prototype with negative upgrade
  result (#1452).
- Runtime: minimal-move keyed reconciliation + benchmarks (#1455),
  one internal LifetimeScope type replacing ElementLifecycle (#1458),
  internal renderer selection convergence with byte-identical
  generated entries (#1457), each prevalidation and fail-closed
  Part Program conformance (#1456).
- Release engineering: CI nested-packed de-dup, evidence/receipt
  hardening (#1443), streaming reference route + benchmark (#1453),
  ADR-0158/0159 drafts (PROPOSED), qualification matrix, CHANGELOG.

* chore(www): regenerate content dates after alpha5 content commit

* fix(alpha5): qualification hardening — T2 probe, review fixes, tier corrections

- T2 stream-survival probe (#1451): a Lit element server-born in the
  streamed shell survives stream+backfill (single instance, slot children
  preserved, interactive after upgrade); boundary documented — foreign
  tags are fail-closed inside backfilled deferred regions.
- Twenty review-driven fixes across five hardening rounds: typed-frame
  comparison (number/boolean/null text Parts), front-gate thenable sweeps
  (rejections become 500s, not process deaths), build/runtime/browser
  budget parity (32 fields / 64 owners / 64 properties), shared
  stream-frame admission policy with entity-obfuscation normalization
  (element canonical + router copy pinned by parity tests), early-frame
  claim-drift fix (resolved seed applied after pending own values, server
  value wins like late frames), explicit benchmark permissions, honest
  parity comments, no repo-internal paths in shipped artifacts.
- WC admission corrections (#1451): firstChild pre-upgrade capture
  (Shoelace sl-button and Material md-filled-button now genuinely pass
  T0 — 3/11), hydrationSafe null semantics, zh translation and full site
  wiring for the web-component-admission page.
- Legacy fixes surfaced by the full root suite: create README stale
  alpha.2 pin updated to registry truth alpha.4.

* refactor(alpha5): promote streamed-frame policy to a public export; accept ADRs

- Option B (owner ruling 2026-09-25): the canonical stream-frame
  admission policy is now exported from @openelement/element's public
  surface; the router-side verbatim copy is deleted and its consumers
  (entry-stream-runtime, stream-manifest) import the public export. The
  parity test is replaced by a direct unit test against the exported
  policy (32-case corpus; one stale 
 expectation corrected).
- ADR-0157/0158/0159 marked ACCEPTED (2026-09-25 owner ruling; alpha
  public-API variability explicitly accepted); qualification matrix and
  WC admission docs (en/zh) synced to the accepted status.
- Interface snapshot: +5 policy exports across the two surfaces.

* test(alpha5): sweep residuals — real assertions, honest gates, live evidence

Ten owner-ordered residual fixes plus two review follow-ups:
- SWEEP-1/2: replace tautological reconnect/signal assertions with real
  kernel/scope contract tests (distinct instances, abort lifecycle).
- SWEEP-3: stream-handler harness now slices the REAL generated
  channel-merge helper (fail-loud markers) and proves protocol-header
  precedence + Set-Cookie through the generated GET.
- SWEEP-4: WC light-DOM child verification scoped to the host element
  (balanced tag slicing) instead of whole-document substring match.
- SWEEP-5: 'export { x as loader }' aliases admitted end-to-end (export
  name is the module.loader contract); follow-up: duplicate 'as loader'
  exports rejected by the scan with an actionable diagnostic.
- SWEEP-6: cancel-while-parked no longer closes a cancelled stream;
  regression probe bounded with a deadline (no unbounded polling).
- SWEEP-7: app-shell gate investigated — project-level invariant kept,
  both build and generated-guard errors now tell the truth and name the
  project-wide fix.
- SWEEP-8/9: benchmark metadata now computed (git-status probe, no
  port race — single in-process serve).
- SWEEP-10: snapshot cache hit/miss are observations, README aligned.
* feat(www): generate article routes from content; document streaming

- New generator www/tools/generate-site-article-routes.ts: the single
  writer of article route modules, component bindings, and the route
  table (imported by article-pages tests). --check enforces byte
  identity; stray hand-written files and symlinks fail closed;
  duplicate output collisions (e.g. an 'index' article vs a collection
  overview) throw instead of silently dropping a route.
- All 24 existing articles migrated to generator output (46/48 files
  byte-identical to the hand-written versions; dist output verified
  byte-identical pre/post migration). check wired into generate:all and
  the release train.
- Streaming guide (en/zh): opt-in defer syntax, front gate vs deferred
  fields, budgets mirrored in four places incl. the 64-property seed
  cap, no-JS arrival-order tail, null literal semantics, islands and
  nested components emit as empty hosts on streamed shells (linked
  from islands-and-ssr), post-commit failures emit a generic error
  frame (no error-variant content), byte-identical output with
  streaming off.
- doc-figures pins re-baselined to current dist measurements.

* chore(www): regenerate content dates after generator lane content commit
* release: 1.0.0-alpha.5 — rendered as data resolves

Advances the six version points via version-bump (4 package configs,
create anchor, 4 fixture locks), admits the train
(ADMITTED_ACTIVE_TARGET, release-state sourceVersion/activeTarget), and
finalizes the CHANGELOG section header. Publishing follows the standard
train: exact-SHA CI on main, Release workflow dispatch under the
openelement-release environment, npm trusted publishing, receipt
verification, tag + GitHub Release, post-publish registry-truth sync.

* release: regenerate ui manifest at 1.0.0-alpha.5 (seventh version site, generator-owned)
The pinned fixture mirrors the repo's current source versions and
release-state; it still pinned alpha.4 after the train admission,
failing tools/repo#test:coverage:check (the eighth release-bookkeeping
site). Registry evidence in the fixture is untouched: npm truth stays
alpha.4 until publish.
…ete) (#1463)

Registry verified 2026-09-26: all four packages published under the
alpha dist-tag; latest untouched. latestPrerelease partition advanced,
per-package alpha dist-tags recorded, create README install pin and
the least-privilege exemption line follow the published version.
…lizer, manifest-driven injection (S0-S6) (#1474)

* docs(adr): ADR-0160 architecture-debt repayment charter + alpha6 baseline evidence

* chore(version): purge historical release names from shipped source; anchor www version truth

Runtime-facing copy no longer carries retired release trains: the generated
banner stamps protocol versions (part-program format / compiled module ABI,
central constants in protocol/part-program.ts), and error messages in the
element facade, jsx runtimes, compiler render stub, and router authoring
speak about the pipeline instead of the 0.44 line; historical comments are
de-versioned to their decision/# references.

www/app/data/version.ts derives OPENELEMENT_VERSION from SOURCE_VERSION
(generated from release-state.json), removing the same-screen dual version;
LATEST_PRERELEASE_VERSION and PUBLISHED_PACKAGE_VERSIONS had zero consumers
and are deleted.

version-bump gains a fail-closed shipped-source historical-version scan
(docs, changelogs, content, fixtures, locks allowlisted) and cross-asserts
the www anchors via the new www-release-anchor module, which the offline
release:state-machine:check also enforces. READMEs state the alpha.5 tree.

* refactor(compiler): generic static-sidecar admission replaces router intrinsic

The semantic core no longer knows @openelement/router. INTRINSIC_MODULES
shrinks to the @openelement/element intrinsics, and the island policy
statement (`export const openElement = defineIslandConfig(...)`) is matched
against host-injected 'static-sidecar' descriptors
({moduleSpecifier, exportName, kind}) threaded through compileElementModule /
compileElementProgram / compiledElementPlugin options; the default core
admits none, so an unconfigured compiler rejects the statement with OEC9008.

Router owns its admission descriptor in exactly one place
(vite/internal/protocol/island-admission.ts) and injects it at every call
site that compiles route or island sources: the vite plugin transform and
HMR hook, the SSG and client inline builds, the stream-manifest page
identity compile, and the client-only island stub codegen (now generated
from the descriptor instead of parallel string literals). The www and saas
test harnesses that drive the compiler directly inject the same descriptor.

Fail-closed provenance is unchanged and pinned by tests: aliased canonical
imports admit, namespace/default/type-only/conflicting/relative-re-export
provenance reject (OEC9008 statement fallthrough, OEC9027 decorator
provenance), and the element default without injection never admits the
sidecar.

* chore(router): declare element subpath imports

router/src imports six @openelement/element subpaths (. , /authoring,
/html, /build-utils, /compiler, /logger) that previously resolved only
through the workspace member map; packages/router/deno.json now declares
each subpath explicitly, making the dependency visible in the package
manifest instead of an artifact of workspace co-presence.

* refactor(core): centralize streaming policy constants

The numeric streaming/runtime admission budgets lived as bare literals at
every enforcement site: the deferred manifest/seed checks in the element
facade, the router's build-time manifest scan, the generated stream runtime
timeout default, and the generated-string copies the browser bootstrap and
entry codegen carry. A value adjusted at one site silently drifted from the
others.

internal/protocol/policy.ts (import-free, same base-of-graph contract as
stream-frame-policy.ts) now names them once — STREAM_MAX_FIELDS (32),
STREAM_MAX_OWNERS (64), STREAM_MAX_SEED_PROPERTIES (64),
STREAM_MAX_PAYLOAD_LENGTH (256 Ki), MAX_COMPOSITION_DEPTH (8),
MAX_ACTION_BODY_BYTES (10 MiB), STREAM_TIMEOUT_MS (30s),
IDLE_FALLBACK_TIMEOUT_MS (50ms) — and the public surface carries them to
the router pipeline the same way STREAM_FRAME_* already travels. The
real-TS enforcement sites compare against the named constants; the copies
inside generated strings are a separate convergence pass.

* refactor(router): converge raw errors into the error catalog

The build pipeline still raised bare Errors, so a host could not classify
a delivery, entry-admission, descriptor or SSG-render failure by code and
the failure surfaces could not be enumerated. internal/error-codes.ts now
carries one table per raising surface — DeliveryErrorCode,
IslandEntryErrorCode, DescriptorErrorCode, SsgRenderErrorCode and
DocumentErrorCode — plus a buildError factory (phase 'build', cause
preserved) beside the existing authoringError. The five converged files
(delivery.ts, entry-generators.ts, entry-descriptor.ts, ssg-render.ts,
document.ts) raise through it; their message text is unchanged, and
copy-inside-generated-string throws are a later pass.

element: the streamed-seed type mismatch in connectedCallback no longer
reuses OE_PROGRAM_MISSING — it reports its own OE_STREAM_TYPE_MISMATCH,
so a host can tell a seed contract drift from a missing compiled program.
The error-catalogue tests gain coverage for both: a router catalog test
(stable unique values, factory phases, one raiser per converged surface)
and a stream-facade test pinning the new code and phase.

* refactor(www): derive locale unions from the SiteLocale single source

Every handwritten 'en' | 'zh' in site routes, site-ui, data and tooling
now points at SiteLocale from www/site-config.ts (issue #1468 item 6).
The redirects emitter drops its locale cast outright: indexing perLocale
with SITE_LOCALES is already exact and fails closed if a locale is added.

* feat(ui): export instance-state, mark six unadopted components experimental

Owner ruling C1 (#1468, item 7): open-card, open-callout, open-dialog,
open-dropdown, open-tabs and open-input carry no compatibility promise
until each has standalone adoption evidence.

- The ui manifest generator's fail-loud policy registry now also owns a
  per-class status ('stable' | 'experimental') emitted into each
  declaration's openElement block; OpenElementExtensions grows the
  optional field.
- The six component headers state the experimental status; the other
  four are pinned 'stable'.
- readInstanceState/writeInstanceState join the @openelement/ui public
  surface (index re-export plus a side-effect-free './instance-state'
  subpath); the www island-state copy is deleted and both islands
  consume the subpath — the barrel would pull all ten compiled
  components into the two largest island chunks against the 100 KiB
  islandKB SLO (www/site-budget.ts).
- The www API reference page renders an experimental chip from the
  manifest status; the api-reference generator threads it through.
- Public-interface snapshot regenerated for the new exports.

* chore(repo): retire dead tasks and merge the duplicate core gate

Remove tasks and scripts with zero references anywhere (workflows, hooks,
tests, docs included): tools/repo census + tracked-census.ts, perf:baseline /
perf:record + perf-record.ts, the generate:export-files write variant, and
tools/release consumer:packaged-serve:node / :bun / consumer:element-smoke
(the CI workflows invoke those scripts directly with scoped flags).

generate:export-files was the write-mode enrollment that fed generate:all;
with it gone the generator's committed output is refreshed by the documented
script invocation, so check-generator-gates orphan detection now follows its
own documented rule ("referenced by some task") instead of only counting
generate:*/emit-* task references. The generated file header points at the
script and the regeneration also lands the pending ui/instance-state export
left stale by 13105b234.

gate:ci and verify:core were byte-identical; verify:core survives as the one
definition and check-task-contracts.test.ts pins it. Doc rows and docstrings
that named gate:ci or the deleted consumer tasks now name the surviving
surface.

* ci(release): verify all four package versions without deno eval

The release workflow's two inline deno eval version checks violated the
least-privilege tripwire (deno eval runs with implicit -A) and only covered
element and router. They are replaced by tools/repo/check-release-version.ts,
a deno run --allow-read script that reads every manifest through
version-bump.ts PACKAGE_CONFIGS (the same single source the bump edits) and
validates the dispatch input and each manifest version against the release
line contract in tools/lib/version.ts, so the workflow can no longer verify
a narrower package set than the bump writes.

* chore(benchmarks): remove committed local measurement, mark manual scripts

Delete benchmarks/streaming/alpha5-local.json: a dirty-checkout darwin
Chromium run committed into the tree, against the jfb harness doctrine that
local results are observations in gitignored .artifacts/, never committed
baselines. The streaming README and measure.ts docstring now point --out at
.gitignored .artifacts/ and state the script is manual — deno task bench and
CI never run it — and the alpha5 qualification map no longer describes the
file as tracked.

micro.ts keeps its packages/element/src/internal/** imports (the compiled
runtime, serializer and signal engine it measures are deliberately not on
the public surface) and now says so in its header, together with the fact
that deno task bench exercises the suite through micro.test.ts; its
deterministic self-checks pass under the bench task flags.

* docs: correct comments that reference retired modules

props-utils.ts described the normalizePublicProps consumers as the deleted
render-dsd.ts and jsx-render-dom.ts; the surviving truth is the
collectPublicProps host-collection path plus the shared isDangerousKey
predicate that injectPropsSafe and the Router page projectors enforce.
router/vite/index.ts listed a defineIsland element export that never existed
on that surface (island authoring is defineIslandConfig on the router root
entry) and named a generated-data-resolver.ts module that is gone (the
import-map resolver in deno-import-map.ts owns the site aliases today).

* test(router): re-pin client entry bytes after banner de-versioning

b22a3c7c3 de-versioned the generated client entry banner ('(v0.44 - ' ->
'('), shrinking both client entries by 8 bytes; the renderer-adapter pin
test still asserted the old bytes, so the router suite failed round 1 of
the full gate. Re-pin native client 1991 -> 1983 (e3ea822d...) and lit
client 3146 -> 3138 (ec06ba01...); server pins are unchanged.

Not a blind re-pin: re-inserting the removed 'v0.44 - ' segment into the
current output reproduces both old SHA-256 hashes exactly, proving the
generated-output delta is precisely that intentional banner removal and
nothing else. renderer-adapter.test.ts is not one of the oracle files.

* docs(adr): index ADR-0157 through ADR-0159

* feat(serializer): shared pure serialization kernel + differential parity harness

The compiled Element serializer had two parallel Part Program tree walkers:
the server serializer (internal/compiled/server/index.ts) and the runtime
seed serializer (internal/compiled/runtime.ts serializeToHtml). Both walked
the template, interpreted anchors and Regions, and assembled attributes;
only the escape implementation was shared.

serialize-program.ts is now the ONE tree walker (issue #1469, ADR-0160 rule
b): host-free, DOM-free, module-state-free, no signal creation — every
execution-mode difference is an explicit seam (signal reads, sink
emissions, item admission, trusted-HTML capability, nested-element
rendering, slot projection, pending streamed markers). The server and seed
entry points delegate to it through thin seam adapters.

The pre-kernel walkers stay temporarily as *Legacy oracle exports, and
compiled-serializer-differential.test.ts replays the full corpus (static
and dynamic attrs, bool/class/style sinks, prop JSON, void tags, when/each
Regions, item slots, slot projection, trusted HTML, unsafe-program
rejections, DSD modes, nested custom elements, deferred pending seeds,
the shared compiled-claim fixture) through both implementations and
requires byte-identical output. Serialization bytes are unchanged.

* refactor(serializer): retire the duplicated walkers, parity test becomes kernel contract

With the differential harness green over the full corpus, the pre-kernel
seed walker (runtime.ts serializedFixedAttributes/serializeElement/
serializeNode) and the pre-kernel server walker (server/index.ts
snapshotProgram context machine plus its serialize* walk) are deleted.
The seam adapters in both execution modules are now the only
serialization logic they carry; serialize-program.ts is the one
tree-walking serializer (issue #1469, ADR-0160 rule b).

compiled-escape-parity.test.ts keeps every byte pin it guarded before —
the attr/fixed-attribute/text corpora still require byte-identical output
across the server and seed entry points and pin escapeAttr/escapeText
directly — and gains a structural guard asserting both execution modules
import the shared kernel and carry no private walker. The
void-tags-convergence consumer list moves runtime.ts to the kernel,
which now owns the void-tag references the retired walker carried.
Serialization bytes are unchanged.

* refactor(router): typed response/header channel replaces string runtime (block a)

Issue #1470 block a of the ADR-0160 generated-entry repayment. The
request-time semantics that used to live inside codegen template strings —
the loader/action response-header channel merge with its protocol-header
precedence and multi-value Set-Cookie handling (ADR-0129), the streamed
route's late-mutation Proxy gate and its header-commitment switch
(ADR-0158), and the CSP auto-nonce creation/policy instantiation — move
into typecheckable modules under
packages/router/src/vite/internal/server-runtime/ (types.ts,
response-channel.ts, mod.ts barrel). Generated entries now import them via
the new @openelement/router/server-runtime export subpath; the emitters
keep only call sites.

Generated-entry bytes change (admitted: ADR-0160 "Admitted output
deltas", S3a): renderer-adapter byte pins re-computed (native server
27507 -> 27032, lit server 25562 -> 25087; client entries unchanged).
Derived artifacts regenerated by ritual: generated-export-files.ts and
the public-interface snapshot gain the new subpath (6 documented symbols).

Oracle evidence: request-time-parity green on all 29 steps for both dev
and Nitro against the rebuilt fixture; stream-manifest, renderer-scope-
parity, ssg-admission-parity, lit-graph-boundary (new subpath walked,
kernel-free), compiler-open-core-boundary, registry-marker-drift all
green unweakened; stream-handler.test.ts now binds the real module
through its string-eval harness (no local copy). New unit tests pin the
module semantics (14 cases in server-runtime-response-channel.test.ts).

* refactor(router): typed page/document/render runtime replaces string helpers (block b)

Issue #1470 block b of the ADR-0160 generated-entry repayment. The
page-render semantics that used to live inside codegen template strings —
the __ssr page seam (native renderDsd fork and lit renderLitPageToHtml
fork), the __resolvePageTag forks (compiled program / lit openElementPageTag),
the page props projection with its dangerous-key guard (#1214), the
page-definition/route-meta extractors, path locale resolution, shell href
localization, status-page HTML, and the app-shell composition
(__resolveAppShell/__renderAppShell) — move verbatim into typecheckable
modules under packages/router/src/vite/internal/server-runtime/ (page-render.ts,
renderer-runtime.ts, document-runtime.ts, mod.ts barrel). Generated entries
import them via @openelement/router/server-runtime and keep only the
renderer-adapter-selected factory bindings wired to their serialized build
data; the element functions (renderDsd, trustedHtml, escapeHtml, the SSR
registry, renderLitPageToHtml) are injected at binding time, so the modules
carry no Element import edge and the LIT entry graph stays kernel-free
(#1339). entry-render-runtime.ts is deleted; the native/lit fork keeps its
renderer-adapter seam shape (runtimeSeam()) with renderer-neutral call sites.

__createDeferredPageShell moves emitter-to-emitter into the stream runtime
emission (entry-stream-runtime.ts) with its text unchanged: it is stream
machinery called only by streamed routes, and the stream-manifest oracle pins
its emitted shape — its typed migration belongs to the streaming-pump block
with that oracle. Helpers are now gated on page routes existing.

Generated-entry bytes change (admitted: ADR-0160 "Admitted output deltas",
S3b): renderer-adapter byte pins re-computed (native server 27032 -> 22783,
lit server 25087 -> 22323; client entries unchanged at the pre-lane baseline:
1983 / 3138 bytes). Derived artifacts regenerated by ritual: the
public-interface snapshot grows the server-runtime subpath from 6 to 22
public symbols (./server-runtime was already a declared export).

Oracle evidence: request-time-parity green on all 29 steps for both dev and
Nitro against the rebuilt fixture (the fixture build exercises the new
factory wiring end to end: app-shell composition, error-boundary re-render,
styled 404, locale resolution); stream-manifest green unmodified; renderer-
scope-parity, ssg-admission-parity, lit-graph-boundary (walk stays kernel-
free), compiler-open-core-boundary, registry-marker-drift, and
compiled-escape-parity all green unweakened. The string-eval harnesses now
execute the shipped typed modules directly; stream-handler.test.ts binds the
real implementations through its evaluation context and runs the real emitted
__createDeferredPageShell text (15 tests). New unit tests pin the page
projection, tag-resolution forks (native + lit), renderer seam, and app-shell
runtime semantics.

* refactor(router): typed action runtime replaces string protocol (block c)

The action POST protocol moves out of the generated-entry template
strings into @openelement/router/server-runtime/action-runtime.ts
(ADR-0160 rule a, #1470 block c): the CSRF floor (#611/#921/#938/#1382),
named-action dispatch (#542), the canonical classification (#541), the
RFC 9457 problem+json channel (#863/#549), PRG (#548), the default body
limit (#568) bound to the serialized MAX_ACTION_BODY_BYTES policy
constant (S1c), the ADR-0121 303 coercion, the 500 error mapping (#558),
and the internal Hono-WinterCG bridge. The entry keeps call sites and
one-time bindings; the hono/body-limit and protocol-constant imports are
gone from generated code, and the @openelement/router import shrinks to
the two lifecycle guards.

The ADR-0120/0121 entry-renderer string pins become wiring pins plus a
new 22-test behavior suite driving the shipped module; the string-eval
harness binds the real bridge/body-limit through deps (no harness-local
reimplementation left). Byte evidence at the renderer-adapter pin:
native server entry 22783 -> 17478, lit 22323 -> 17018; client entries
unchanged at the pre-lane baseline. Delta recorded in ADR-0160 (S3c).

request-time-parity green on all 29 steps for both runtimes against the
rebuilt fixture - it caught one real wire divergence during migration
(fetch redirect answering HTTP 303 instead of the pinned HTTP 200 +
body-303 ActionResult), fixed before landing.

* refactor(router): typed stream runtime replaces embedded template (block d)

The streaming pump moves out of the generated-entry template strings into
@openelement/router/server-runtime/stream-runtime.ts (ADR-0160 rule a,
#1470 block d): the request scope with its abort fan-out and cancel
detach, the deferred-field observer front gate with its
rejection-observation sweep, the 32/64 bounded manifest budget, the
256 KiB payload bound on the escaping encoder and the backfill ranges,
the shell commitment with its typed seed attribute, the bounded wake/queue
behind the highWaterMark:0 ReadableStream, the 30s timeout sweep,
Part backfill frames with their terminal error frames, the no-JS tail,
and the browser installer bootstrap as the single-point
STREAM_BROWSER_BOOTSTRAP constant string. The entry imports the four
functions, binds const __streamBody = __createStreamBody({ escapeAttr }),
and passes the bootstrap constant to documentStreamParts; the bootstrap
stays an inline head script per streamed page (S4 re-homes it).

The policy numbers now reference the S1c policy constants instead of bare
literals: element/authoring gains the nine STREAM_* budgets/frame lists
(kernel-free leaf, same transport as the action protocol constants), so
the typed module imports them without an Element-runtime edge and the LIT
graph stays kernel-free. The server-runtime subpath grows 34 -> 44 public
symbols and the element authoring leaf by nine constants; the
public-interface snapshot is regenerated for both.

The __createDeferredPageShell gate stays emitted byte-identical
(entry-stream-runtime.ts shrinks to that one emitter): the read-only
stream-manifest oracle pins its emitted shape, so its typed migration
needs an oracle amendment - recorded in the S3d delta. Pre/post diff
evidence: the bootstrap string is byte-identical (14039 chars) and the
gate emission is byte-identical up to the removed pump.

Evidence: stream-manifest green unmodified (13 tests, emitted-shape pins
hold); request-time-parity green on all 29 steps for both runtimes
against the rebuilt fixture; lit-graph-boundary green with the negative
control; renderer-adapter byte pins unchanged (17478/17018 server,
1983/3138 client - the pinned descriptors have no stream routes);
renderer-scope-parity, ssg-admission-parity, compiler-open-core-boundary,
registry-marker-drift, and compiled-escape-parity green with assertions
intact; stream-handler.test.ts green (15 tests, unchanged assertions)
with the real pump bound through deps; stream-browser.test.ts green
(7 tests, real Chromium against the byte-identical bootstrap); new
server-runtime-stream-runtime.test.ts drives the typed module directly
(13 tests).

* refactor(router): generated entry reduced to route wiring (block e) + gate: no runtime bodies in codegen

ADR-0160 rule (a), #1470 block e. The generated Hono entry's final form is
imports + route-descriptor data + one createGeneratedApp({...}) factory call
plus per-route wiring. New server-runtime modules own the assembly:

- app.ts: createGeneratedApp — Hono app + WinterCG bridge, the composed
  handler exports (middleware.use onion, devFetch, runtime adapter), the
  #951 client-script plumbing, and the page-render bindings.
- security.ts: the SSR registry guard (fail-closed ownership; imports the
  canonical registry markers instead of generated-string injection) and the
  canonical DANGEROUS_KEYS binding.
- route-dispatch.ts: the startup stream guards, the page handler table, and
  the 405 responder.

Serialized-copy deletion per the S3 import verdict (only the repo's own
string-eval harness cannot import element, and it binds via deps):
__DANGEROUS_KEYS and __maxActionBodyBytes are gone — the factory imports
both from the kernel-free /authoring leaf, which gains MAX_ACTION_BODY_BYTES.
__ssrRenderableTags/shell plan/locales stay descriptor data, now factory
config. Emitted per-route GET/POST/404 wiring and call sites are unchanged;
the dispatch composition follows the handler population as before.

Hard gates (generated-entry-gate.test.ts): standalone TS parse, allowlisted
function declarations + retired-emission ban, no dangerous-key literals,
client graph never reaches server-runtime (walk + negative control).

registry-marker-drift is rewritten per the lane's design exception: wire
values and polyfill banner pins stay, the injection pins become guard-seam
pins plus guard behavior cases. Byte pins: native server 17478 -> 14141,
lit server 17018 -> 14254; clients unchanged at the pre-lane baseline.
Oracle evidence and the full delta are recorded in ADR-0160 (S3e).

* refactor(router): amend ADR-0160 (amendment 1) — last emitted runtime body becomes typed

The __createDeferredPageShell gate was the lane's last runtime function
body emitted into a generated entry — the one carve-out S3d kept "with
the oracle" because the read-only stream-manifest pin held its emitted
text byte-exact. ADR-0160 Amendment 1 replaces that pin with the typed
seam: stream-runtime.ts gains the createDeferredPageShell factory (same
fail-closed condition, same error text, same executor delegation), the
entry imports and binds it next to the pump, the emptied
entry-stream-runtime.ts emitter is deleted, and the gate's contract is
pinned as behavior on the typed module (missing manifest, program
tag/version mismatch, uncompiled class, exact delegation). The
generated-entry-gate request-time function allowlist narrows to zero
(streamed entry shapes now gated) and the emitted head joins the
retired list; ./server-runtime grows by the factory and its config type
(interface:snapshot:write ritual).

* chore: wire idle-fallback constant

S1c named IDLE_FALLBACK_TIMEOUT_MS but left one emission site reading a
bare 50: island-scheduler.ts's requestIdleCallback ||
requestAnimationFrame || setTimeout fallback. The import-free scheduler
module (it bundles into any consumer build unchanged, #868) cannot
import the constant, so the value rides the #1470-block-c
__maxActionBodyBytes seam: IslandSchedulerDeps gains
idleFallbackTimeoutMs and the generated client entry serializes the
policy constant at build time (derivation pinned in
entry-generators.test.ts, fallback behavior pinned in
island-scheduler.test.ts). Client-entry bytes move +29 B (renderer
pins updated; the 2048 B #868 wiring budget still holds); the delta is
recorded under ADR-0160 admitted output deltas (S4).

* fix(router): await the deferred-shell executor delegation (require-await)

deno lint's require-await flagged the async gate: it returned the
createDeferredDsdExecutor promise without an await. `return await`
keeps the gate an async function — the fail-closed check still rejects
instead of throwing synchronously, matching the emitted original — at
the cost of one extra microtask before settlement, which no consumer
observes (every call site awaits the gate). Repo-wide deno lint green;
stream-handler, stream runtime behavior pins, stream-manifest oracle,
generated-entry gate, and request-time-parity (29 steps) all green.

* feat(router): ClientAssetManifest + client-before-SSG build order

Issue #1471 items 1/2/5 (ADR-0160 S4a, rule d — manifest-driven client
asset injection):

- New ClientAssetManifest protocol (internal/protocol/client-assets.ts):
  { entry, islands: Record<tag, { file, strategy, preload? }>, shared[] },
  keyed by compile-time island identity (delivery tag). The builder
  (vite/client-asset-manifest.ts) joins the island declarations with the
  Phase 2 build manifest (dist/client/.vite/manifest.json) and Rollup
  output module metadata — a chunk is matched by the module ids it
  contains, never by parsing output chunk file names.
- closeBundle build order is now Phase 1 SSR -> Phase 2 client -> Phase 3
  SSG, so the SSG render pass and the request-time artifact carry the
  final asset addresses. SSG consumes the exact pre-reorder Phase 1 facts
  (closeBundle snapshots the island declarations before Phase 2 narrows
  them to the reachable client set); markComplete semantics and log
  numbering unchanged.
- dist/server/client-script.js becomes client-assets.js — pure structured
  manifest data, no injection logic. The generated dist/server/index.js
  reads clientAssets.entry through the #951 client-script wiring
  (__setRequestTimeClientScript(clientAssets.entry)); the ssg-render
  placeholder emits the empty manifest. readClientEntryFromManifest is
  retired with its test (entry lookup lives in findClientEntryFile).

Admitted output delta recorded in ADR-0160 (S4a): only dist/server/index.js
and the renamed client-assets.js move; fixture rebuild diffs show rendered
HTML, client chunks, and island manifests byte-identical. All seven oracles
plus generated-entry-gate and the renderer-adapter byte pins green.

* fix(router): drop unused resolve import in client-asset-manifest (deno lint)

* feat(router): document-time script injection from asset manifest; postprocess filename surgery deleted

#1471 items 3/4/6 (S4b, ADR-0160 rule d). ResolvedDocument carries the
structured clientScripts descriptors (loading/module/nonce.ts; public
ClientScriptDescriptor type + optional third resolvePageDocument
parameter), and every render channel — request-time handlers, the SSG
render pass, the styled 404, both renderer adapters — serializes the
final script tags at document time from the same resolved field. The SSG
build hands the Phase 2 client asset manifest's entry URL to the SSR
bundle before prerendering through the existing #951 setter seam; the
per-request CSP nonce still attaches exactly once at serialization and
SSG output keeps rejecting nonces.

matchIslandChunkFile (filename-prefix matching), injectClientScript
(post-build HTML rewriting) and buildIslandChunkMap are deleted; the
strategy/layer/per-page island manifests survive, reading chunk URLs
from the manifest's delivery-tag-keyed record via
islandChunkMapFromAssetManifest (identity-driven; unrecorded islands are
surfaced, never silently mapped). Generated server entries move
14141->14249 (native) and 14254->14362 (lit) bytes; client entries
unchanged. interface:snapshot:write regenerated (./document grows
ClientScriptDescriptor/ResolvedDocument.clientScripts; ./vite records
the S4a clientAssetManifest slot) and the S4b delta is recorded in
ADR-0160. New ssg-asset-manifest.test.ts pins the nine-scenario matrix —
identity never drifts with a chunk file name.

* test(harness): shared qualify harness replaces triplicated ritual runners

tests/lib/qualify-harness/ owns the scaffold -> workspace-alias -> router
build -> static serve -> Playwright ritual that three harnesses each
implemented privately (#1472, ADR-0160 alpha6 debt repayment):

- scaffold-app: generate the temporary app via packages/create (source or
  packed CLI) and copy fixture sources in;
- workspace-alias: point the app's import map and vite aliases at workspace
  source exports, rewire the build task, prime app-local node_modules
  (deno run - from stdin, replacing the deno eval call);
- build-router + admission-plan: the in-repo Router build CLI wiring and the
  quote-aware ssrAdmissionPlan reader (data extraction, never code loading);
- serve-static: thin re-export of tools/lib/static-server.ts;
- drive-chromium: browser launch + dist serving + page-error collection with
  ordered teardown.

web-component-interop/qualify.ts (1472 -> 1214), third-party-web-components/
qualify.ts (1083 -> 966), and starter-smoke/setup.ts (222 -> 169) become
callers that keep only their charter-specific probes. The duplicate package
export enumeration moves to dependency-light tools/lib/package-aliases.ts so
fixture-scoped configs can resolve it.

Verified at pre-refactor depth against a HEAD worktree: interop qualify
evidence (3 engines x 12 probes) and third-party smoke logs are line-
identical modulo checkout paths, entry.js/index.html byte-identical from the
same checkout, starter dist artifacts identical; starter-smoke Playwright
projects pass 12+24 on chromium/firefox/webkit. Interop fixture gains
npm:mime + npm:pathe (tools/lib/static-server transitives); deno.lock
regenerated via fixtures:locks:update, check green.

* fix(www): reference-page stability chip satisfies the compiled list-Region grammar

The conditional JSX committed in 13105b234 ({element.status && ...}) is
rejected inside an each-Region item by OEC9013. Render the chip
unconditionally and let the existing .chip:empty rule (page-reference-styles.ts)
hide it for stable rows carrying status ''. Left uncommitted in the worktree
since S1d; S4's site:build gate was green on top of this fix, so it is
load-bearing and belongs on the lane.

* refactor(element): split compiled runtime into single-duty modules; unify pre-upgrade bookkeeping

The 2,779-line compiled Part Program executor becomes a re-export facade
plus the seed-serializer seams entry; the execution modules live under
compiled/runtime/: program-kernel (shared context/signal/node core),
fresh-dom (template walk + fresh creation), regions (when/each machinery),
parts (fixed-Part install + text Part), claim + claim-recovery (staged
scan, bounded owning recovery), pre-upgrade-events (capture/replay
family). The pre-existing kernel.ts keeps its CompiledElementKernel
tenant, so the shared core takes the program-kernel.ts name.

The facade capture registry (preUpgradeCaptures and the ensure/replay/
release wrappers in the OpenElement facade) merges into pre-upgrade-events
— one bookkeeping layer; the public ensurePreHydrationClickCapture export
(name, signature, JSDoc) is unchanged and the public runtime surface
re-points to the new home. Moved function bodies are byte-identical to the
pre-split module (verified by extraction diff); the 64-record cap and
every pre-upgrade behavior pin are untouched.

The facade deliberately keeps hosting serializeToHtml: the compiled-escape-
parity oracle and the when-operator convergence guard pin its source as a
site that imports condition-holds and the shared serializer kernel.

* refactor(compiler): split semantic core into single-duty modules; semantic analysis takes injected vocabulary

The 2,381-line compile.ts facade decomposes into six single-duty modules
(#1473 item 1): parse-module.ts, analyze-module.ts, lower-program.ts,
emit-program.ts, emit-module.ts and compiler-diagnostics.ts. Every moved
block is byte-verified verbatim against the pre-split file; compile.ts
stays the internal entry, wiring parse -> analyze -> lower -> program ->
emit and re-exporting the boundary surface (CompiledElementError,
compileElementProgram, CompileElementResult, ElementCompilerDiagnostic),
so plugin.ts and every compiler test import unchanged.

parse-module.ts is the narrow parse seam (#1473 item 2): an explicit
ParserPort (source -> TS AST + syntax diagnostics) whose sole
implementation is the one module depending on the TypeScript parsing API.
Its header records the retirement triggers from ADR-0160 and the alpha6
lane: the TS6 JS-API EOL or oxc standard decorators reaching stability,
whichever lands first — the future oxc/tsgo backend swaps in exactly
there.

The semantic core no longer knows @openelement/router (#1473 item 3,
S1b follow-up; ADR-0160 rule c). analyzeModuleSemantics gains an
optional ModuleSemanticsOptions.vocabulary of plain
ModuleVocabularyDescriptor entries ({moduleSpecifier, exportName, kind:
'page-definition' | 'element-registration'}); the built-in vocabulary
covers only the element package's own registration factories, so the
default scan fails closed on router factories. Router owns its
vocabulary in one place (vite/internal/protocol/module-vocabulary.ts,
beside the S1b island-admission descriptor) and injects it at every
module-scan call site: route-scanner, foreign-tag-scanner,
static-component-scanner and build-client. Element's plugin gate keeps
the default scan. Tests keep their counterexamples and gain the
fail-closed default cases: with no injection, definePage and router
registration factories are not recognized; foreign bindings stay
unrecognized even with the vocabulary injected.

Derived artifacts regenerate by ritual: the public-interface snapshot
gains ModuleSemanticsOptions/ModuleVocabularyDescriptor and the widened
analyzeModuleSemantics signature (www api-reference unchanged).
void-tags-convergence's consumer list points at the new VOID_TAGS home
(lower-program.ts).

Verified: all 31 moved blocks byte-identical (git-show diff harness);
packages/element suite 428 passed; packages/router suite 946 passed
(read-only oracles intact, assertions unchanged); targeted deno
check/lint/fmt on every touched file; tools/repo generate:all clean.

* refactor(router,repo): split god files into single-duty modules; unify fail factory and control-char scan

S6c mechanical decomposition (issue #1473 items 3/4/6): move + re-export
only, zero behavior change.

- vite/plugin.ts (999 lines) -> plugin.ts composes hooks only; the hook
  families move to plugin-config.ts (shared OpenPluginState, option and
  config-file resolution, config/configResolved), plugin-scanners.ts
  (buildStart + route/island discovery + entry descriptor), plugin-hmr.ts
  (transform/handleHotUpdate with the per-instance program-shape map),
  plugin-watch.ts (configureServer watcher), plugin-virtual-modules.ts
  (virtual ids + open:virtual-entry). Plugin names, hook sets, and the
  returned plugin order are unchanged.
- element fail() micro-factories converge on one phase-parameterized
  factory, raiseFrameworkError (protocol/errors.ts): the four identical
  csr/render throwers (styles, context, kernel, program-kernel) and the
  near variants (serialize-program failUnreachable, part-program fail,
  public-runtime failUncompiled, open-element-implementation
  failMissingProgram) now delegate to it; call-site message text is
  unchanged. server/shared.ts keeps CompiledProgramValidationError (a
  different error class, not a frameworkError variant).
- router control-character scan loops (authoring.ts x2, delivery.ts,
  entry-generators.ts, island-scanner.ts) unify on the canonical
  hasControlCharacter helper (internal/control-characters.ts) with a
  dedicated test; rejection messages unchanged. critical-assets.ts keeps
  its distinct CSS-allowing predicate.
- candidate-evidence.ts splits its four spawn-free duties into
  candidate-evidence-{aggregate,validate,tarballs,site-e2e}.ts with the
  shell re-exporting the public surface (existing import paths keep
  working); job-record and fresh-clone duties stay in the shell per the
  S6c owner ruling (Mimosa write-hook false-positive on the verbatim
  argument-list Deno.Command helpers - see stage notes). The zero-reference
  bare candidate:evidence task is removed from tools/repo/deno.json;
  check-task-contracts and check-task-flags stay green.
- .gitignore gains .zcodeignore (local ZCode tool file), removing the
  repo's only untracked-unignored path.

Verified: element 428 tests, router 948 tests, candidate-evidence +
task-contract/flags/permissions + evidence-reuse 78 tests, new
control-characters tests 2 - all green; tools/repo + tools/lib deno check
clean; changed files deno fmt/lint clean.

* fix(repo,www): repoint split-module references in error-reference scan and ci-contracts test

The #1473 single-duty splits moved code without moving the hardcoded
references that track it:

- www generate-error-reference scanned only compile.ts and
  module-analysis.ts for OEC literals, so the catalog collapsed to 14
  codes after the compile facade split. The scan now lists every
  semantic-core module that carries OEC literals (analyze-module,
  compile, compiler-diagnostics, emit-program, lower-program,
  module-analysis), and per-code entries are merged across modules
  before pushing: since the split one code's raising sites legitimately
  live in several files, so the catalog keeps the first authored
  message in static COMPILER_SOURCES order and sums the site count.
- check-ci-contracts read candidate-evidence.ts for four Site E2E
  contracts whose symbols moved to candidate-evidence-site-e2e.ts
  (path constant), candidate-evidence-aggregate.ts (sidecar read from
  the fresh-clone job) and candidate-evidence-validate.ts (audit wired
  into the rollup). Each assertion now reads the module the symbol
  lives in; the no-reintroduction guard stays on candidate-evidence.ts,
  where the producer's extras staging still lives. Regexes unchanged.

* test(ui): assert the layer/hydrate/status policy message

S1d (13105b234) added the status dimension to the manifest policy table and
its error message ('No layer/hydrate/status policy') but left the matching
test assertion update uncommitted in the worktree; the acceptance full-suite
run was green on top of this change, so it is load-bearing.

* fix(router): explicit return types for packed public API (slow-types)

* fix(router): gate the CSP auto-nonce off hono/ssg prerender passes (SSG stays nonce-free)

* chore(repo): regenerate public interface snapshot for the CI-fix exports

Covers 98480f0d3 (createActionBodyLimit gains its MiddlewareHandler return
type; ./server-runtime shape hash) and a7cd22655 (isSsgPrerenderDispatch
exported). No other drift: exactly two hunks.

* chore(packages): drop decision-record citations from shipped source comments

The pack-surface gate (#1412) bans ADR ids in npm artifacts; deno pack
preserved two of them (escape-text, runtime facade). Reword the two
flagged sites plus the four same-family kernel/ParserPort comments to
issue references, which the policy allows.

* chore(packages): finish pack-surface cleanup — remaining ADR citations and ui subpath doc

entry-server-codegen emits its CSP comment into consumer artifacts, so the
ADR id ships in dist output; postprocess carries the same citation family.
ui/README now documents the /instance-state subpath added in 13105b234
(policy: every packed export subpath is named in the shipped README).

* chore(router): reword remaining ADR citations in codegen sources

Two emitted comment strings shipped ADR ids into consumer artifacts
(entry-orchestrator), one JSDoc survived packing (entry-route-helpers);
issue references replace them per the #1412 artifact-surface policy.

* chore(packages): sweep decision-record citations from shipped source (#1412 surface policy)

Final ADR pass over the four shipped packages' src: co-cited ADR
tokens collapse to their issue reference (e.g. 'ADR-0160 rule a,
#1470 block e' -> '#1470 block e'), pure citations (ADR-0129,
ADR-0120/0121, ADR-0123, ADR-0158) drop the token and keep the
sentence; no issue numbers invented. 20 files, comments only.

The entry-descriptor/entry-codegen hits all sit in source comments,
not emitted strings, so generated entry bytes should be unchanged;
renderer-adapter pins are verified below by the router suite.

git grep 'ADR' packages/{element,router,create,ui}/src: zero hits.

* chore(router): drop repository-internal path from shipped page-render comment

pack-surface:check flagged page-render.js/.d.ts line 14: the JSDoc
spelled out the repo path 'packages/element/src/internal/core/
security.ts', which ships in the packed artifact. Name the constant
(DANGEROUS_KEYS) and its re-export leaf instead of the path. Comment
only; found while sweeping the #1412 gate after the ADR pass.

* test(router): re-pin server entry bytes after the emitted-comment ADR sweep

8b75ebdc8 reworded the generated entry's emitted comments for the #1412
surface policy, dropping the ' (ADR-0160 rule a)' segment from the
emitted 'Generated-app assembly' comment (-18 bytes, native and lit
alike) but did not re-pin, so the router suite has been failing since
that commit. Re-pin native server 14249 -> 14231 (e37d5c18...) and lit
server 14362 -> 14344 (4433f89b...); client pins are unchanged.

Not a blind re-pin: diffing the full dumped native/lit server+client
entries across d94b0af5e..current shows exactly that one emitted-comment
line per mode and nothing else, and the follow-up comment-only sweep
commits (72fe3e09d, 91ff589cb) regenerate byte-identical entries to
8b75ebdc8 — the generated-output delta is precisely 8b75ebdc8's
intentional emitted-comment rewording. Assertion semantics untouched
(both the length and the SHA-256 assertEquals remain). renderer-adapter
.test.ts is not one of the oracle files.

* chore(repo): drop the repository-internal path from the export-files banner

pack-surface:check flagged the generated file's shipped banner: it told
readers to regenerate via a 'tools/repo/...' script path, which ships in
the npm artifact and is a path a consumer cannot open (#1412 internal-
reference rule). The banner now names the repo's 'generate:all' tooling
task instead; the tracked generated module is regenerated in the same
commit so tools/repo#export-files:check stays in sync. Comment-only:
the OPENELEMENT_EXPORT_FILES data is unchanged.

* docs(router): document the server-runtime public subpath in the README

pack-surface:check requires every public export subpath to be named in
the shipped README (#1412 facade rule); '@openelement/router/server-
runtime' was the one undocumented subpath. New section describes what
the subpath carries (the generated entries' request-time runtime: the
generated-app factory, the response-header channel and its commitment
gate, the CSP auto-nonce, the page SSR renderer seam, the action POST
protocol, the streaming pump), that generated entries — not
applications — import it, and why it is public (type-aware tooling,
hand-rolled hosts).

* fix(router): client asset manifest fails closed; package island identity is exact-match

T1 (#1471, alpha6): the Phase 2 client asset manifest is now a hard gate.
readViteClientManifest throws OE_CLIENT_ASSET_MANIFEST_READ /
MANIFEST_MALFORMED naming the manifest path and underlying reason instead of
catching to null; buildClientAssetManifest throws ENTRY_MISSING when the
manifest records no "virtual:open-client-entry" file (islands and
enhanced-forms-only alike) instead of shipping entry: '', and an admitted
island that resolves to no asset is a named failure
(ISLAND_UNMAPPED) instead of the silent `continue`. The buildClient
warn-and-continue call site is gone; failures propagate through the existing
Phase 2 error path (buildError / internal/error-codes.ts). Pure-static and
zero-admitted-island builds still emit zero client JS (pinned by a new
static-only fixture regression).

T2: package-island module identity no longer substring-matches
(id.includes(modulePath) first-hit) — the resolver and the client build's
chunk grouping (native manualChunks and the lit codeSplitting group name)
share one exact rule (moduleIdentityMatches / packageIslandChunkName):
segment-boundary equality or trailing-path match, extension-insensitive,
with zero and multiple matches both failing (ISLAND_UNMAPPED /
ISLAND_IDENTITY_AMBIGUOUS). Because a declared modulePath is not globally
unique (import-map targets resolve outside the specifier string; two
packages can ship the same relative name), buildClient resolves each
package specifier through the same deno.json import map the build resolver
uses and joins on the real module path when one exists; npm/jsr specifiers
keep the declared specifier as identity under the same exact rule. Tests
for both directions of the rule live in the shared module's suite — no test
helper reimplements the matching. New tests: missing manifest, malformed
manifest, missing client entry, enhanced-forms-only missing entry, admitted
island without a chunk mapping, two-package same-name module ambiguity.

* refactor(compiler): defineIsland vocabulary verdict (removed)

The module-scan vocabulary on both sides registered defineIsland as an
element-registration factory: element CORE_VOCABULARY
(@openelement/element) and ROUTER_MODULE_VOCABULARY (@openelement/router).
Forensics show both entries are dead — the vocabulary admits imports of a
binding neither package exports:

- the element package retired the defineIsland() runtime in v0.44
  (f0610f102 deleted internal/core/island.ts where it lived);
- the router never exported the name — packages/router/src/index.ts
  exports defineIslandConfig only, and authoring.test.ts pins
  'defineIsland' in appSurface === false as part of the v0.44 removal;
- the shipped public-interface snapshot carries no bare defineIsland
  across either package's export paths;
- repo-wide grep finds zero real call sites: docs, www content, create
  templates, test fixtures, and the www app all use defineIslandConfig.
  The only textual defineIsland callers were the vocabulary's own tests.

The entries came over mechanically from the pre-split semantic core,
which hard-coded ['@openelement/element', '@openelement/router'] times
defineIsland from the era when the runtime existed. No valid program can
import the removed binding, so removing the entries cannot change scan
outcomes for authored routes; the default scan and the router-injected
scan now fail closed on them.

- module-analysis.ts CORE_VOCABULARY and module-vocabulary.ts drop the
  defineIsland entries, each with a comment recording the verdict.
- module-analysis.test.ts: the alias cases keep defineElement coverage;
  two regression pins added — the default scan no longer recognizes
  defineIsland, and the injected router vocabulary no longer admits it.
- foreign-tag-scanner.test.ts: the island fixture no longer authors
  tags via defineIsland; a regression pin proves a legacy defineIsland
  call site surfaces as a foreign tag (visibility-only) instead of
  being silently excluded.
- foreign-tag-scanner.ts header comment drops the stale mention.

No public surface moves: CORE_VOCABULARY is module-private,
module-vocabulary.ts is vite-internal, and the public-interface snapshot
is unchanged. The defineIslandConfig policy statement (static sidecar)
and the defineElement registrations are untouched.

Verified: module-analysis (26), compiler-intrinsic-provenance (20),
foreign-tag-scanner (14), lit-renderer-codegen (5),
route-scanner-tagname (11) all green; element batch 429 passed; router
batch 800 passed with the same 20 pre-existing native-binding
environment failures as HEAD (verified by stash).

* refactor(repo): candidate evidence record contract and fresh-clone producer become single-duty modules

Split the two remaining duties out of candidate-evidence.ts (alpha6 debt
train): candidate-evidence-record.ts now owns the StepResult/JobResult/
LoadedJob record shapes, the process primitives (repoRoot/denoExe/required/
expectedSha/assertCleanAtSha/toolVersions), the SHA-free log bookkeeping,
and the workspace --job producer; candidate-evidence-fresh-clone.ts owns
the fresh-clone lane and its Site E2E sidecar staging. The CLI file keeps
only the import.meta.main dispatch and the compatibility re-exports.

Aggregate and validate import the record foundation directly, which
dissolves the intentional shell-to-lane cycle left by #1473 without adding
any new cycle. All moved code is verbatim (diff-audited against HEAD);
shapes, failure texts, path roles, SHA/sidecar bindings, and exit behavior
are unchanged. The check-ci-contracts no-reintroduction guard follows the
moved producer to the record module (semantics unchanged, per the same
file's #1473 precedent).

* docs(adr): ADR-0160 closure amendment — manifest and identity contracts, vocabulary verdict

Amendment 2 records the lane's closure stage (T1-T4):

- T1: the Phase 2 client asset manifest failure contract — the five stable
  OE_CLIENT_ASSET_* codes, the fail-closed boundary (no catch-to-null, no
  warn-and-continue, no entry:'', no silent island drop), and the message
  contract (manifest path, island label, identity string named).
- T2: the package-island identity contract — one shared exact rule
  (moduleIdentityMatches/packageIslandChunkName), zero and multiple matches
  fail, declared specifiers resolve to the real module path where possible.
  Comply-or-explain: the resolution half covers deno.json imports maps only,
  not the workspace-member exports the build resolves through
  workspace-alias.ts, so the official Site's packageIslands: ['@openelement/ui']
  now fails OE_CLIENT_ASSET_ISLAND_UNMAPPED — disclosed with the evidence
  that the pre-closure green build was silently mis-attributing those islands
  to the client entry chunk. Open repair handed to the implementation lane;
  not fixed in this docs-only stage.
- T3: the defineIsland vocabulary verdict (removed) — dead entries admitting
  an import no package exports, with the forensics and the fail-closed
  consequence (legacy call sites surface as foreign tags).
- T4: the S6 final responsibility boundary — all seven #1473 items mapped,
  including the completed candidate-evidence split.
- Closure output-delta reconciliation: zero closure delta (fixture trees
  byte-identical vs bf57d945c; oracle batch 38 passed/29 steps; targeted
  suites green), every T0-baseline difference mapped to a recorded entry,
  and the kernel-bundling client chunks' pre-closure byte movement recorded
  (S1 error-catalog enum entry, S6c fail-factory convergence).

Also fixes the one stale authoritative-doc reference the closure sweep found:
the CSRF floor citation in docs/architecture/security-and-release-engineering.md
still pointed at entry-action-runtime.ts; the floor lives in the typed
runActionProtocol since #1470 block c.

* fix(router): package island identity resolves through the build's alias table

Workspace packages carry no deno.json import-map entry — their module
identity lives in the resolve.alias table the client build ships. Extend
packageIslandSourcePath to resolve declared specifiers through that same
table (segment-exact, longer finds first) so the manifest join and the
resolver agree on one identity. Completes the closure workflow's final-gate
fix round that the run timeout cut short.

* fix(router): alias-table resolution helpers complete the island identity join

d1727cbc3 committed the consumer (build-client importing
resolveThroughAliases) but not the provider half the interrupted closure
fix round had staged. Adds resolveAliasSourcePath/resolveThroughAliases
with @rollup/plugin-alias matching semantics (exact or segment-boundary
string finds; first-occurrence rewrite; bare/virtual replacements keep the
declared specifier) and strips a version reference from the defineIsland
verdict comment.

* test(www): hydration e2e tolerates the package-island chunk naming

The exact-identity join names package island chunks island-<tag>-<hash>.js
(#1471); the loader-source regex and namespace unwrap follow the loader's
own shape. Last leg of the closure fix round the run timeout interrupted.

* fix(router): close client asset manifest failure seams
@SisyphusZheng

Copy link
Copy Markdown
Member Author

Superseded by conflict-free synchronization PR #1476.

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