Skip to content

Commit 0e6eef4

Browse files
fix(0.42): alpha.7 light-fullstack floor (TP-5.7)
fix(0.42): alpha.7 light-fullstack floor (TP-5.7)
2 parents 2c1d58c + 8a1c970 commit 0e6eef4

39 files changed

Lines changed: 835 additions & 228 deletions

docs/adr/ADR-0121-0-42-action-protocol-hardening-amendment.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,15 @@ this at the architecture level.
107107
`preventDefault()` skips the default morph (the page handles the
108108
failure itself). Network and non-HTML failures keep the reload
109109
fallback.
110-
12. **CSRF threat model (new; records the standing assumption).** The
111-
framework's form/action loop relies on the browser `SameSite=Lax`
112-
default for cookie-based authentication (sessions are 0.44 scope).
113-
Applications using ambient authentication (Basic, mTLS,
114-
`SameSite=None` cookies) must add Origin/Fetch-Metadata validation;
115-
a documented middleware recipe ships in the guide. A built-in check
116-
is deferred to the 0.44 session work.
110+
12. **CSRF threat model (amended 0.42.0-alpha.7 / #611).** Generated
111+
action POST handlers default to a fail-closed same-origin floor:
112+
reject when `Sec-Fetch-Site: cross-site`, or when `Origin` is present
113+
and does not match the request URL origin. Clients that omit both
114+
headers (typical non-browser tools) are allowed. Opt out by setting
115+
`OPEN_ELEMENT_DISABLE_CSRF=1` on the request `env` binding
116+
(`c.env` / Nitro runtime env). Session-aware CSRF tokens and cookie
117+
session primitives remain 0.44; this floor is the light-fullstack
118+
browser default, not a full auth stack.
117119

118120
## Consequences
119121

docs/audit/2026-07-28-alpha6-production-review.md

Lines changed: 229 additions & 0 deletions
Large diffs are not rendered by default.

docs/current/PACKAGE_SURFACE.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ authoring modes = Basic Element standalone + full application
1212

1313
## Current five-package surface
1414

15-
| Package | Responsibility | Supported public interface |
16-
| --------------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
17-
| `@openelement/element` | Custom Elements, JSX, DSD, hydration and signals | root, `jsx-runtime`, `jsx-dev-runtime`, `build-utils` |
18-
| `@openelement/app` | Pages, routing, islands and request/render semantics | root, `hono`, `model`, `spa`, `preact` |
19-
| `@openelement/adapter-vite` | Vite, content, SSG and Nitro build implementation | root, `nitro-mount`, `cli/build`, `sitemap` |
20-
| `@openelement/create` | Installed starter and coherent version entry | CLI binary (root) |
21-
| `@openelement/ui` | Optional, proven general-purpose primitives | root and retained primitive subpaths |
15+
| Package | Responsibility | Supported public interface |
16+
| --------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------- |
17+
| `@openelement/element` | Custom Elements, JSX, DSD, hydration and signals | root, `jsx-runtime`, `jsx-dev-runtime`, `build-utils` |
18+
| `@openelement/app` | Pages, routing, islands and request/render semantics | root, `hono`, `model`, `spa`, `preact` |
19+
| `@openelement/adapter-vite` | Vite, content, SSG and Nitro build implementation | root, `nitro-mount`, `cli/build`, `cli/start`, `cli/preview`, `sitemap` |
20+
| `@openelement/create` | Installed starter and coherent version entry | CLI binary (root) |
21+
| `@openelement/ui` | Optional, proven general-purpose primitives | root and retained primitive subpaths |
2222

2323
Application authors should normally learn `element`, `app`, `adapter-vite`,
2424
and `create`; `ui` is optional.
@@ -55,7 +55,7 @@ promise and are not application-authoring surface.
5555
"internal": ["i18n"]
5656
},
5757
"@openelement/adapter-vite": {
58-
"supported": [".", "nitro-mount", "cli/build", "sitemap"],
58+
"supported": [".", "nitro-mount", "cli/build", "cli/start", "cli/preview", "sitemap"],
5959
"internal": []
6060
},
6161
"@openelement/create": {
@@ -97,7 +97,8 @@ promise and are not application-authoring surface.
9797
`plugin-mdx`, `route-manifest`, `cli/build-client`, `cli/build-ssg`) were
9898
pruned at the 0.41.0 freeze (ADR-0119): they had zero consumer specifiers —
9999
the build pipeline and generated code import only relatively or through the
100-
supported root, `nitro-mount`, `cli/build` and `sitemap` subpaths. The module
100+
supported root, `nitro-mount`, `cli/build`, `cli/start`, `cli/preview` and
101+
`sitemap` subpaths. The module
101102
files remain inside the package for internal relative imports only.
102103
- `@openelement/ui` supported subpaths: `open-badge`, `open-button`,
103104
`open-callout`, `open-card`, `open-code-block`, `open-dialog`,

docs/current/VERSION_PLAN.md

Lines changed: 132 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
1-
# v0.42.0 — WC Application Loop release plan
1+
# v0.42.0 — WC Application Loop (light fullstack) release plan
22

33
> Current source package line: `v0.42.0-alpha.6`\
44
> Current npm registry line: `v0.42.0-alpha.6`\
5-
> Active release target: `v0.41.1`\
6-
> Planning release target: `v0.42.0` (WC Application Loop)\
5+
> Next alpha train: `v0.42.0-alpha.7` (TP-5.7)\
6+
> Active release target: `v0.42.0-alpha.7`\
7+
> Planning release target: `v0.42.0` (WC light fullstack / Application Loop)\
78
> Next release line: `v0.43.0` (Universal WC SSR)\
89
> Current maturity stage: stable (0.41.x line); 0.42.0 planned under ADR-0120
10+
> Plan amendment: 2026-07-28 — light-fullstack product promise, CSRF floor
11+
> in alpha.7 ship gate, login-via-recipe clarified (no 0.44 wait)
912
1013
## Objective
1114

12-
`0.42.0` ships the WC Application Loop governed by
13-
[`ADR-0120`](../adr/ADR-0120-0-42-0-wc-application-loop-scope.md): one
14-
route-to-interaction loop — load, DSD render, progressive form, action,
15-
error/redirect and revalidation — that works without JavaScript. It extends
16-
the frozen static-first model to request time without touching the ADR-0119
17-
freeze surface.
15+
`0.42.0` ships **WC light fullstack**: the Application Loop governed by
16+
[`ADR-0120`](../adr/ADR-0120-0-42-0-wc-application-loop-scope.md) — one
17+
route-to-interaction loop (load, DSD render, progressive form, action,
18+
error/redirect, revalidation) that works without JavaScript — plus the
19+
minimum operational floor so a stranger can `build → start` a dynamic
20+
route, trust static prerender failures, and post forms under a default
21+
same-origin CSRF check. It extends the frozen static-first model to
22+
request time without touching the ADR-0119 freeze surface.
1823

1924
```text
2025
OpenElement = Web Components-native fullstack application framework
21-
0.42 scope = the route-to-interaction loop at request time
26+
0.42 product promise = WC light fullstack
27+
0.42 scope = request-time application loop + first-mile + CSRF floor
28+
0.42 does NOT ship = framework session/flash, cache/ISR, auth packages
29+
login apps = supported via recipes (better-auth) on Web-standard Request
2230
component contract = standard Custom Elements (unchanged)
2331
official build path = Vite + Nitro (unchanged)
2432
```
2533

34+
**Light fullstack means (0.42 exit):** dynamic `loader`/`action` routes;
35+
no-JS and enhanced form loops; honest npm tags; runnable server artifact;
36+
fail-closed static prerender; default CSRF same-origin on generated POST;
37+
documented login via third-party session on `Request` headers.
38+
39+
**Light fullstack does not mean:** framework-owned session store, flash
40+
across redirects, OAuth package, or production cache/ISR — those remain
41+
`0.44.0` (or stay recipes forever if a library already owns them).
42+
2643
The protocol layer is evidence-backed by the six-framework study archived at
2744
[`docs/audit/2026-07-27-application-loop-framework-research/`](../audit/2026-07-27-application-loop-framework-research/README.md):
2845
standard form POST wire format, one POST/two responses, the 303/422 status
@@ -39,16 +56,27 @@ invariant.
3956
- The external adopter pilot #390 stays retired by ADR-0119; 0.42 builds
4057
adoption evidence through reproducible recipes and dogfooding instead of
4158
a pilot program.
42-
- Request-time data, forms, sessions and cache are explicitly unfrozen;
43-
this plan covers data and forms only. Sessions and cache stay with
44-
`0.44.0`.
59+
- Request-time **data and forms** are the 0.42 unfrozen surface this plan
60+
freezes at TP-6. **Framework session and cache semantics** stay with
61+
`0.44.0`. **Login is not blocked on 0.44**: apps attach better-auth (or
62+
any cookie session library) via `loader`/`action` + `Request` headers per
63+
`docs/integrations/better-auth.md`; the framework does not own the
64+
session blob.
4565
- ADR-0120 records the scope boundary (self-built loop semantics and
4666
continuity mechanism; third-party server layer and Web-standard context)
4767
and the action protocol. Changes to either require an ADR-0120 amendment.
48-
- The line shipped as four themed alphas; a fifth remediation alpha
49-
(TP-5.5) was inserted by the first implementation audit round before the
50-
stable decision; the stable freeze scope for request-time semantics is
51-
decided by a separate ADR at the end of the line.
68+
- CSRF: ADR-0121 accepted a documentation recipe. This plan **promotes a
69+
default same-origin check on generated action POST** into the 0.42 light
70+
fullstack floor (#611); the #611 PR lands the ADR-0121 amendment text
71+
with the code (fail-closed default, documented opt-out for non-browser
72+
clients). Full session-aware CSRF tokens remain 0.44-optional.
73+
- Surgical `@openelement/element` security fixes that do not change the
74+
ADR-0119 authoring surface are allowed in remediation alphas (e.g. #602
75+
attr-name allowlist). Broader element API work is still a stop-and-recheck
76+
signal.
77+
- The line shipped themed alphas plus remediation alphas (TP-5.5–TP-5.7);
78+
the stable freeze scope for request-time semantics is decided by a
79+
separate ADR at TP-6.
5280

5381
## Task packages
5482

@@ -288,22 +316,68 @@ expanding the 0.42 scope.
288316
included; release-tier gates green; alpha.6 published with two-stage
289317
evidence; #592/#593 stay open as the TP-6 agenda they record.
290318

291-
### TP-6`0.42.0` stable decision
319+
### TP-5.7`0.42.0-alpha.7` light-fullstack floor + audit round 3
292320

293-
Goal: decide and ship the request-time freeze scope.
321+
Goal: close the third review round (issues #597#616, milestone
322+
`v0.42.0-alpha.7`, source `docs/audit/2026-07-28-alpha6-production-review.md`
323+
plus the 2026-07-28 orchestrator review) **and** land the light-fullstack
324+
operational floor (runnable server, fail-closed SSG, CSRF default, honest
325+
tags/claims) without expanding into framework session/cache (0.44).
294326

295-
- 准入: TP-5.6 closed (alpha.6 published); the seven-day P0 watch on the
327+
- 准入: TP-5.6 closed (alpha.6 published); round-3 findings filed as
328+
#597#616 (done 2026-07-28); this plan amendment (light fullstack + CSRF
329+
ship-gate promotion) accepted in-repo (2026-07-28).
330+
- 执行步骤:
331+
1. **Ship gate (must)** — Meta checklist #616:
332+
- Morph residual: #597 H1 `__scanSubmitRoots`, #598 H2 `form.action`
333+
IDL / `name=action`, #599 H3 multi-form last-wins
334+
- Build/ops floor: #600 H4 SSG non-200 fail-closed + freeze hard-fail;
335+
#601 H5 request-time `start`/import-map/preview first mile
336+
- Security floor: #602 M1 `serializeAttrs` attr-name allowlist;
337+
**#611 CSRF same-origin default on generated action POST** (promoted
338+
from capacity → must; lands ADR-0121 amendment with the PR)
339+
- Honesty floor: #607 M6 npm `latest`→stable; #608 M7 zero-JS /
340+
client.js / STATUS claim precision
341+
2. **In-train if capacity** (not required to publish alpha.7): #603 M2
342+
morph focus/scroll/controls, #604 M3 nested DSD, #605 M4 `open:ready`,
343+
#606 M5 single island scheduler, #609 L1 PageRenderingMode collapse,
344+
#610 L2 extract morph module.
345+
3. **Login path (docs, not framework session):** keep better-auth recipe
346+
accurate against the alpha.7 loop; README/STATUS one-liner: “login via
347+
recipe on 0.42; framework session is 0.44”. Optional: one CI or
348+
recorded dogfood that boots the recipe shape (not a new package).
349+
4. **Explicit non-blockers:** framework session/auth primitives #612
350+
0.44; loader head/SEO #613; sourcemaps #614; SPA/server loader typing
351+
#615; freeze-gate shape #592 and freeze baseline policy #593 → TP-6.
352+
- 准出: every ship-gate issue closed with code + test/gate proof; fixture
353+
suite three engines green including multi-form / island-only paths
354+
touched by H1–H3; CSRF default covered by unit or e2e deny/allow;
355+
`deno task start` (or documented equivalent) hits a dynamic route in CI
356+
or release smoke; release-tier gates green; alpha.7 published with
357+
two-stage evidence; `npm view` dist-tags honest (`latest` stable, `alpha`
358+
current); deferred issues stay open with `deferred` label.
359+
360+
### TP-6 — `0.42.0` stable decision (freeze light fullstack)
361+
362+
Goal: freeze and ship the **WC light fullstack** request-time surface.
363+
364+
- 准入: TP-5.7 closed (alpha.7 published); the seven-day P0 watch on the
296365
last alpha shows no P0.
297366
- 执行步骤:
298-
1. Stable-scope ADR: which request-time semantics freeze (expected: the
299-
loop contract and action protocol; expected unfrozen: streaming,
300-
performance behavior) and which defer to 0.43/0.44.
301-
2. Migration note: zero-cost upgrade proof for static-only users; prose
302-
for SPA users.
303-
3. Release through the full workflow; restart the seven-day P0 watch.
367+
1. Stable-scope ADR: freeze the loop contract, action protocol, CSRF
368+
default, and first-mile start semantics. Explicitly **unfrozen /
369+
out of 0.42 claim**: framework session/flash, cache/ISR, streaming,
370+
performance SLOs, third-party WC SSR corpus (0.43), production
371+
runtime recovery (0.44). Record that login apps use recipes.
372+
2. Product wording: README/www/STATUS say `0.42 = WC light fullstack`
373+
with the non-goals above; no “full production runtime” claim.
374+
3. Migration note: zero-cost upgrade proof for static-only `0.41.x`
375+
users; prose for SPA users; note for apps adding better-auth.
376+
4. Release through the full workflow; restart the seven-day P0 watch.
304377
- 准出: npm, dist-tags, tag, GitHub release, docs and evidence agree on
305378
`0.42.0`; the freeze ADR is accepted; #37-style stable gate for 0.42
306-
opened fresh (not reused).
379+
opened fresh (not reused); light-fullstack acceptance bullets below
380+
are all green.
307381

308382
## Acceptance
309383

@@ -312,19 +386,36 @@ Goal: decide and ship the request-time freeze scope.
312386
- A pure-static `0.41.x` project upgrades to `0.42.0` with zero changes and
313387
byte-identical output.
314388
- The no-JavaScript form loop is proven in three engines, not inferred.
389+
- Enhanced multi-form and island-only client paths do not throw or silently
390+
drop successful actions (TP-5.7 ship gate).
391+
- A project with request-time routes can `build` then `start` (documented
392+
one-command path) and complete a GET loader + POST action without tribal
393+
knowledge.
394+
- Unexpected static non-200 prerenders fail the build; freeze/missing-page
395+
assertion is a CI hard fail.
396+
- Generated action POST rejects cross-site browser requests by default;
397+
opt-out is documented.
398+
- npm `latest` points at the last stable line; `alpha` at the active alpha.
399+
- Login-via-recipe is documented as supported on 0.42; framework session is
400+
explicitly a 0.44 topic — not a prerequisite for signed-in apps.
315401
- The WC + DSD + static-first loop is documented as the product
316-
differentiator in README/www after alpha.4.
402+
differentiator in README/www.
317403

318404
## Non-goals
319405

320-
- No session or cache semantics (0.44); request-time routes ship a
321-
conservative no-cache default only.
322-
- No auth, OAuth, ORM, database or storage features — recipes only.
323-
- No new package; no change to `@openelement/element`; no change to the
324-
ADR-0119 frozen surface or the SPA client-side chain.
406+
- **No framework session or cache semantics** (0.44). Request-time routes
407+
keep a conservative no-cache default only. Cookie sessions owned by
408+
better-auth (or equivalent) are in-bounds via recipes.
409+
- **No auth, OAuth, ORM, database or storage packages** — recipes only.
410+
Signing users in on 0.42 is a recipe problem, not a “wait for 0.44” gate.
411+
- No new package; no change to the ADR-0119 frozen authoring surface or the
412+
SPA client-side chain. Surgical element security fixes only (see Entry
413+
truth).
325414
- No `shouldRevalidate`-style route opt-outs until a proven need exists.
326415
- No streaming SSR; per-request render is buffered in 0.42 (streaming is a
327416
0.43/0.44 candidate).
417+
- No pulling 0.43 third-party WC SSR corpus or 0.44 production-runtime
418+
platform into this line.
328419

329420
## Test matrix
330421

@@ -352,8 +443,13 @@ Goal: decide and ship the request-time freeze scope.
352443
## Risks
353444

354445
1. Island state preservation across morph is the least proven piece; TP-4
355-
carries it deliberately, and a failing matrix holds the alpha.
446+
carried it, TP-5.6–5.7 harden residuals; a failing matrix still holds
447+
the alpha.
356448
2. Dev/build parity drift between hono and Nitro is contained by the TP-2
357449
contract test — the 0.41-era dev/SSR/SSG drift lesson.
358-
3. Scope pull toward sessions/cache is blocked by ADR-0120; any temptation
359-
is an amendment, not an edit.
450+
3. Scope pull toward **framework** sessions/cache is blocked by ADR-0120;
451+
any temptation is an amendment, not an edit. Recipe-based login is
452+
encouraged and must not be misread as “framework session shipped.”
453+
4. Over-claiming “fullstack” without first-mile/CSRF/honest tags burns
454+
trust faster than missing features — TP-5.7 ship gate exists to prevent
455+
that.

docs/governance/PROJECT_WORKFLOW.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ complete because an issue, chat message, or SOP says it is complete. It is
1111
complete only when the repository contains the decision, the execution package,
1212
the implementation, and the gates that prove the claim.
1313

14-
Current execution anchor: published package line `v0.42.0-alpha.6`, completed
15-
implementation anchor `v0.42.0-alpha.6`, and `0.42.0` WC Application Loop planning
14+
Current execution anchor: published package line `v0.42.0-alpha.6`, active
15+
train `v0.42.0-alpha.7` (TP-5.7), and `0.42.0` WC light fullstack planning
1616
under ADR-0120 and `docs/current/VERSION_PLAN.md`.
1717
OpenElement is one Web Components-native,
1818
static-first application framework: Basic Element is an authoring mode, not a
@@ -116,11 +116,14 @@ smoke are release evidence, not telemetry. A version line is not closed until
116116
the status, roadmap, release checklist, release note, and public README files
117117
record the npm outcome truthfully.
118118

119-
Dist-tag policy for the alpha line: every prerelease publish tags both its
120-
prerelease line (`alpha`/`beta`/`rc`) and `latest`, so `latest` never lags the
121-
active release line. `tools/verify-npm-release.ts` enforces this by asserting
122-
`dist-tags.latest` equals the just-published version; stable publishes keep the
123-
npm default of tagging `latest`.
119+
Dist-tag policy (#607): prerelease publishes tag only their line
120+
(`alpha`/`beta`/`rc`). **`latest` always tracks the last stable release** so
121+
`npm install @openelement/*` never lands on alpha by default. Consumers who
122+
want the alpha train use `@alpha` or an exact version. Stable publishes keep
123+
npm's default `latest` tag. `tools/verify-npm-release.ts` asserts
124+
`dist-tags.<prerelease>` for alphas and `dist-tags.latest` for stables.
125+
After alpha.6, run a one-time `npm dist-tag add @openelement/<pkg>@<last-stable> latest`
126+
if registry `latest` still points at an alpha.
124127

125128
## Automation Gates
126129

docs/release/v0.41.0-interface-snapshot.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
"exports": {
116116
".": "./src/index.ts",
117117
"./cli/build": "./src/cli/build.ts",
118+
"./cli/preview": "./src/cli/preview.ts",
119+
"./cli/start": "./src/cli/start.ts",
118120
"./nitro-mount": "./src/nitro-mount.ts",
119121
"./sitemap": "./src/sitemap.ts"
120122
},
@@ -135,6 +137,14 @@
135137
"sha256": "19b817d8d114f0b107b7708675754de3fdc12c76c85c4dd3333814b6643894bd",
136138
"publicDeclarations": []
137139
},
140+
"./cli/preview": {
141+
"sha256": "207adce03deb976db737773bd957e65af6bf5675b460c1121e77203181c06f65",
142+
"publicDeclarations": []
143+
},
144+
"./cli/start": {
145+
"sha256": "968339216c37c775eb83672ca3b61eebd8e8ff974c6ed019ce50fccbf1f78957",
146+
"publicDeclarations": []
147+
},
138148
"./nitro-mount": {
139149
"sha256": "33056a4b4e9c291ea90532ad68268aaa9ad7be1a7f4897986847bd4e54926c47",
140150
"publicDeclarations": [

0 commit comments

Comments
 (0)