Skip to content

fix(v0.44): bind page-route SSR tags to the compiled Part Program (#1276) - #1277

Merged
SisyphusZheng merged 1 commit into
devfrom
kimi/v044-b1.3-route-tag-repair
Sep 3, 2026
Merged

fix(v0.44): bind page-route SSR tags to the compiled Part Program (#1276)#1277
SisyphusZheng merged 1 commit into
devfrom
kimi/v044-b1.3-route-tag-repair

Conversation

@SisyphusZheng

Copy link
Copy Markdown
Member

Problem

#1276 (B1.3 qualification blocker B1.3-F1, proven by deno task fullstack:workspace-qualification → exit 1 at base 73a79000): entry codegen registered/rendered definePage routes under the path-derived fileToTagName tag while the page element's compiled Part Program carries its @element(...) tag. The 0.44 serializer fails closed on mismatch — renderDsd tag "workspace-records" does not match the compiled program tag "workspace-records-page" (OE_PROGRAM_MISSING, phase ssr) → HTTP 500 on /workspace-records, /magic-link, /reset-password under both Node 24 serve.mjs and Wrangler 4.123.0 workerd. Routes whose file-derived tag equals the element tag (/, /login) served 200. Not CI-gated: the qualification task was not a leg of autoflow:ci.

Owner

Implementer role, dispatch packet B1.3-repair (thinker ruling: the route→program binding follows the element's declared tag — the compiled program is the truth). Branch kimi/v044-b1.3-route-tag-repair from dev @ 73a79000.

Before

  • Generated entries emitted the bare path-derived tag literal at every page-route consumer: SSR registration (__registerSsrComponent("workspace-records", …)), the page/404 handlers (let __tag = "workspace-records"), and SSG routeInfo.
  • renderDsd's fail-closed tag check turned the mismatch into a request-time 500.
  • The workspace qualification task existed but was not CI-gated, and its pagination probe still scraped an <a id="next-page" href> anchor that the v0.44 fixture migration (c16b8dd) had deliberately replaced with a GET form (dynamic intrinsic attributes are outside the SSR part schema in grammar v1) — a stale tool masked by the 500.

After

  • One canonical binding: the generated entry resolves every page route's SSR tag at module evaluation via __resolvePageTag(routeModule, fallbackTag) (emitted by entry-render-runtime.ts, consumed via pageRouteTagExpr in entry-route-helpers.ts). It reads the route module's default-exported compiled class's __partProgram.tag (the @element(...) tag — definePage returns the class); the path-derived tag survives only as the fallback for classes with no compiled program, where renderDsd fails closed exactly as before.
  • The binding covers SSR registration (entry-orchestrator.ts), page and 404 handlers (entry-codegen.ts, entry-not-found-codegen.ts) and SSG routeInfo (entry-render-ssg.ts) — no request-time 500 is possible from a tag mismatch; a missing program still produces the pre-existing OE_PROGRAM_MISSING diagnostic.
  • .mdx pages are unaffected (their compiled tag is path-derived by construction, plugin-mdx.ts).
  • tools/qualify-workspace-runtime.ts nextHref() now reproduces the browser's GET form submission (form action + URL-encoded hidden inputs, empty values included) instead of scraping the removed anchor.
  • Regression gate: fullstack:workspace-qualification is a new required autoflow-ci.yml job (Node 24 + workerd, exact-SHA checkout), added to the pr-full-ci-evidence needs list and to REQUIRED_PR_CI_JOBS in tools/autoflow/loop-evidence.ts — the provenance verifier fails closed on unregistered run jobs, so both had to move together.

Why-not-second-owner

No duplicate-looking implementation added. Semantic ownership per the packet: the custom-element tag is owned by the element's compiled program (@openelement/element, serializer reads program.tag); route file-convention grammar stays with route-scanner.ts (registry row from #1270). The rejected alternative — static tag extraction in the scanner — would have required cross-file module analysis (the fixture route imports its page class from app/components/), which ADR-0148 excludes from the semantic core; runtime resolution in the generated entry reads the one canonical source directly. The scanner's path-derived tag remains only as the resolver's fallback argument.

Evidence

RED (base 73a79000, clean tree):

  • deno task fullstack:workspace-qualificationerror: Uncaught (in promise) Error: node-standalone first page returned 500 (task exit 1)
  • deno test --allow-read --allow-write --allow-env --allow-net --allow-run --allow-ffi --allow-sys packages/adapter-vite/__tests__/page-route-tag-resolution.test.ts → 0 passed / 8 failed (assertions on the not-yet-existing __resolvePageTag binding)

GREEN (this PR head 9308f30eb803c5e76c359045dd9b1cef7195e16f, gates run pre-commit on identical tree content):

  • deno task fullstack:workspace-qualification → exit 0; Node 24 standalone and Wrangler workerd both 200 on all qualification routes, byte-identical HTML across runtimes, 10 samples/runtime, fixture stats clean
  • deno task test → exit 0 (includes the new 8-test page-route-tag-resolution.test.ts and the updated [alpha.17] registration decoupling: definePage route tag vs self-registered content element (silent render bypass) #960 descriptor tests)
  • deno task fmt:check → 0; deno task lint → 0; deno task typecheck → 0
  • deno task docs:truth → 0; docs:check-public / docs:check-role-neutral / docs:check-current / docs:check-claims / docs:check-strategy → 0
  • deno task text-integrity:check → 0; package-surface:check → 0; interface:snapshot → 0 (no public API change)
  • deno task build → 0 (www SSG build exercises the new resolver across its full route set)
  • deno task graph:check / freeze:semantics:check / arch:check / repo:hygiene / workflow:check / workflow:check-slimming / actions:check-pins / verify:configs → 0
  • deno test … tools/autoflow/__tests__/ → 129 passed / 0 failed (provenance + workflow contract tests updated for the new required job)

Scope

Packet-owned paths only:

  • packages/adapter-vite/src/internal/ssg/**entry-render-runtime.ts (new __resolvePageTag helper), entry-route-helpers.ts (routeTagNameExprpageRouteTagExpr), entry-orchestrator.ts, entry-codegen.ts (ADR-0122 frozen path; commit message cites ADR-0151 per constitution §5.4), entry-not-found-codegen.ts, entry-render-ssg.ts, entry-descriptor.ts + route-scanner.ts (comment/note sweep only; no scanner behavior change), plus __tests__/page-route-tag-resolution.test.ts (new), __tests__/entry-descriptor.test.ts, __tests__/entry-render-ssg.test.ts (harness mock)
  • Qualification tooling: tools/qualify-workspace-runtime.ts (stale anchor probe → GET-form submission; the fixture itself is correct per c16b8dd and unchanged)
  • CI composition: .github/workflows/autoflow-ci.yml (new workspace-qualification job + evidence needs), tools/autoflow/loop-evidence.ts REQUIRED_PR_CI_JOBS and its tests — forced by the fail-closed provenance verifier, which rejects any CI run job outside the allow-list

No public API or export changed; docs/current/PACKAGE_SURFACE.md, package-surface:check and interface:snapshot are unchanged and green.

Risk

  • __resolvePageTag is a hoisted function declaration called during generated-entry module evaluation (registration, routeInfo); the full deno task test, deno task build and the dual-runtime qualification all evaluate generated entries green. .mdx and plain element routes resolve identically to their previous tag in every passing configuration, so the only behavior change is for routes that 500'd before.
  • Residual (pre-existing, out of packet scope): two routes whose distinct page classes declare the same @element tag would collide at SSR registration; the [alpha.16] fix(adapter-vite): dev SSR output not invalidated on route edits — page reload fires but stale content is served #952 ownership guard and customElements.define semantics fail closed there as before.
  • The new CI job adds one ~5–10 min leg to autoflow-ci.yml and needs npm:wrangler/workerd download in CI; it runs on push/PR/dispatch/call like the other legs.
  • Stop conditions hit: none. tools/autoflow/loop-evidence.ts is one step beyond the packet's literal path list but is a required consequence of the authorized workflow change (the release lane fails closed otherwise); flagged for thinker review.

Part of #1276. Part of #1224, #1150, #1155. Does not close #1276 or #1224 — thinker re-qualification closes them.

)

B1.3 qualification blocker B1.3-F1: entry codegen registered and rendered
definePage routes under the path-derived fileToTagName tag while the page
element's compiled Part Program carries its @element(...) tag; the 0.44
serializer fails closed on the mismatch (OE_PROGRAM_MISSING, phase ssr),
producing request-time HTTP 500 on /workspace-records, /magic-link and
/reset-password under both Node 24 serve.mjs and Wrangler workerd.

Repair per the thinker ruling: the route->program binding follows the
element's declared tag — the compiled program is the one canonical source.
The generated entry now resolves every page route's SSR tag at module
evaluation via __resolvePageTag(routeModule, fallbackTag), which reads the
route module's default-exported compiled class's __partProgram.tag and
keeps the path-derived tag only as the fallback for classes without a
compiled program (renderDsd still fails closed on those, exactly as
before). One canonical binding covers SSR registration, the page and 404
handlers, and the SSG routeInfo; .mdx pages are unaffected because their
compiled tag is path-derived by construction. The route file-convention
grammar (route-scanner.ts) keeps owning the ROUTE; it never owned the
element's identity, and static tag extraction there would need the
cross-file module analysis ADR-0148 excludes from the semantic core.

The B1.3 qualification also exposed a tooling drift it had masked: the
v0.44 fixture migration (c16b8dd) moved pagination from an
<a id="next-page" href> anchor to a GET form whose hidden inputs ride
compiled property Parts (dynamic intrinsic attributes are outside the SSR
part schema in grammar v1), so tools/qualify-workspace-runtime.ts now
reproduces the browser's GET form submission instead of scraping an
anchor.

Regression gate: fullstack:workspace-qualification is a required
autoflow-ci job, and the PR full-CI evidence set
(tools/autoflow/loop-evidence.ts REQUIRED_PR_CI_JOBS) is extended to
match — the provenance verifier fails closed on unregistered run jobs, so
the workflow change and the allow-list must move together.

entry-codegen.ts is an ADR-0122 frozen path; this repair is authorized
under the ADR-0151 Beta.1 framework-qualification boundary and changes no
public API (package surface and public interface snapshot unchanged).

Part of #1276. Part of #1224, #1150, #1155.
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

APIError: Insufficient Balance

opencode session  |  github run

@SisyphusZheng
SisyphusZheng merged commit dc3ee19 into dev Sep 3, 2026
10 checks passed
@SisyphusZheng
SisyphusZheng deleted the kimi/v044-b1.3-route-tag-repair branch September 3, 2026 05:15
SisyphusZheng added a commit that referenced this pull request Sep 3, 2026
…3ee19 (#1150) (#1279)

Fresh release-verifier closure battery at candidate SHA
dc3ee19: full test suite, scoped
parity/hostile/security suites, gate-of-gates, pack/artifacts/packaged
consumer, workspace-qualification regression gate, CI browser matrix and
benchmark SHA-gap re-confirmation all green; constitution 6.2 conditions
verified (no unresolved 4.3 failure, single 5.4-compliant frozen-path
touch in #1277 with ADR-0151 citation). Verifier verdict PASS, GO.

Part of #1150, #1155.

Co-authored-by: DevBot <devbot@openelement.dev>
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