Current implementation audit
Server Components client reference identity for 0.12.0 candidate
From synchronized main SHA 02b06a17dbd5e9221bf02367beadabfbf84e54ee, on
feat/server-components-client-reference-integrity-v012. The Vite analyzer already rejected
ambiguous selectors at build time; the candidate closes the remaining runtime gap. Generated
surrogates now pass { type, expectedSelector } to provideClientReferences, and the host checks the
real Angular component selector (public reflectComponentType), rejects two distinct components
claiming one selector (previously the last silently won) and rejects non-components, as a
reference failure inside the existing all-or-nothing preflight. Evidence levels are recorded in the
candidate report. This is not M4 or 1.0
certification.
Browser reference refusal integration (0.13.0; test-only)
From origin/main SHA 7c6942f5b458335d57265c527b3195362814d300. The existing failure harness now
mutates one SSR boundary selector before Chromium scripts execute, exercising the real
StrataIslandHost unknown-reference preflight. On Node and local workerd it reports one classified
reference error and one terminal observation, leaves all boundaries in that host uncreated with
the original DOM nodes, and preserves healthy sibling interaction. The packed 0.13.0 consumer
positive matrix and its existing budget refusal pass. Malformed generated descriptors remain
unit-only; installed-package selector-negative cases, the Vite optimizer cache matrix, performance
series, the full local CI matrix and hosted CI remain unqualified. This is test-only: an empty Changeset
records no package bump, and there is no 0.14.0 version claim. See the qualification report.
Server Components bounded hydration preflight for 0.11.0 candidate
From synchronized main SHA b69e2eec5bf0ba39f322e03143978ab892fba8a2, on
feat/server-components-hydration-budgets-v011. Candidate browser-side budgets are 128 owned
client boundaries and 1 MiB aggregate serialized payload UTF-8 bytes per Server Component host,
alongside the existing 64 KiB individual payload limit. The preflight retains all-or-nothing
hydration and uses the resource-limit error category. Package unit tests, Node production/Chromium,
local workerd/Chromium and the packed consumer pass for the candidate. The regular Analog dev suite
passes but does not yet exercise an over-budget host; performance series and hosted CI are pending.
This is not M4 or 1.0 certification. See the
candidate evidence report.
Analog route registration integrity for 0.11.0 candidate
From synchronized main 7ac01d44f7c4f20f0817dca661cad135ed956f44 (@strata-sc/analog 0.10.0),
branch feat/analog-route-registration-integrity-v011. Against the locked H3 1.15.11 / Nitro 2.13.4
router, real parameter-shape collisions are order-dependent: same-method registrations with
/products/:id and /products/:slug both succeed, and the first registered template supplies the
handler and parameter name. Nitro also shares the parameter-name metadata across methods; GET
/:id plus POST /:slug delivers whichever parameter name was registered first to both handlers.
Analog registration planning now rejects differing templates with an equivalent supported shape
across the router's Strata-owned routes, independent of method, batch and apiBoundary. Identical
templates remain valid across methods. Unsupported Nitro syntax outside the boundary remains
unchecked except for exact method/path duplicates; native registrations remain invisible to Strata.
The Analog suite passes (247 tests); full local build/lint/type/test/format and the runtime, packed
consumer, Server Components regression and E2E matrices passed. Startup registration timings and
unrun/hosted evidence are recorded in the report. PR #145's hosted Verify passed; deploy remained
skipped and no deployed qualification was performed. See the
candidate report.
Server Components hydration observations (0.10.0 candidate)
From synchronized main efd14e2d1375e1762846c3ee6ab6bb49d5440609 (@strata-sc/server-components 0.9.0).
The browser runtime now supports an optional, application-injector provideStrataHydrationObserver() callback.
It emits one immutable terminal host event after a successful all-boundary commit, a preflight refusal, or a
commit failure after rollback. It does not replace Angular ErrorHandler, alter skew recovery, include boundary
values or observe later Angular behavior. Unit coverage and the local qualification matrix are recorded in the
evidence report; unrun and hosted evidence must
remain explicitly marked there. M4 and Strata 1.0 remain open.
Analog validated URL-encoded forms for 0.10.0 (feature branch)
On branch feat/analog-validated-forms-v010, work began from synchronized main
68e4a5135964e3ea6f8f86ef216baa969fe4e429 (@strata-sc/analog 0.9.0). The additive
request.readValidatedForm(schema) API validates application/x-www-form-urlencoded with Standard Schema v1,
inferred outputs and transformations. It reads at most 1 MiB, caps nonempty form fields at 10,000, rejects
malformed percent/UTF-8 encoding and unsupported media/charset with safe existing 400/413/415 envelopes, and
uses request-local reader ownership so raw-first reads cannot bypass bounds or JSON/form readers reinterpret
one another. Repeated values preserve order; nested path syntax is literal. Unit tests pass (241), release-gate
decision tests pass (17), and the real fixture passes Node production, Vite dev and local workerd with 24-request
overlap. The packed consumer and full local CI matrix pass. Hosted CI, live workflow-run transition and deployed
qualification remain open. One Analog-only 0.10.0 Changeset is present. See the evidence report.
The same branch fixes issue #117 in a separate release workflow change: publishing now waits for the exact
main push's successful Verify, checks that main still points to that SHA, and rechecks immediately before
Changesets. The unit decision matrix passes, but hosted workflow-run transitions are not yet verified; issue
#117 stays open until merge and live-transition inspection. At the starting SHA, GitHub Release and npm both
confirm Analog 0.9.0 was published (release run 40120642476).
Analog header validation for 0.9.0 (feature branch)
On branch feat/analog-header-validation-v09, work began from synchronized main (769899c). request.validateHeaders(schema)
validates a frozen, lowercase, string-valued, request-local header snapshot with Standard Schema v1 through the existing
validation path: inferred output, safe 400 with source: "headers", cleanup-first precedence and input-rejected
observation. Repeated fields are the native Headers combination (", "); nothing is split, and no credential is verified.
Implemented, unit tested, locally integrated (pnpm test:analog-headers on Node, dev, local workerd) and packed-consumer
type-qualified; not hosted-CI qualified, deployed or adopted. See the evidence report.
Analog request observability for 0.8.0 (feature branch)
On branch feat/analog-request-observability-v08, work began from synchronized main
ea9b47a1bb49491ee92b840ae75f3209ce4384ea (@strata-sc/analog 0.7.0). registerControllers() gains the opt-in
onRequestSettled observer: one frozen, structured observation per Strata-owned invocation (registered route
template, a finite outcome, the phase, a status only where Strata produced it, streaming, cleanup, and the
monotonic invocationMs / elapsedMs), delivered once after cleanup has settled, and for an owned
streamResponse() when its body completes, fails or is cancelled, not when the handler returns. It carries no
request values, headers, bodies, error details or response content. It is synchronous and isolated: a throwing or
rejecting observer changes neither the response nor the original error. Client disconnect is not observable on the
pinned adapters, so a stream never read or cancelled is never reported. Native routes, serverFn and the API
boundary's own 404/405 are not observed. Qualified by 26 unit tests on a real h3 router, pnpm test:analog-observability on the real fixture (Node, dev, local workerd) and the packed consumer. Not hosted-CI
qualified when written, not deployed, no production adoption, observation overhead not measured, and the
publish-before-CI issue #117 is untouched. See the
evidence report.
Server Components deployment skew for 0.8.0 (feature branch)
On branch feat/server-components-deployment-skew-v08, work began from synchronized main
ef31c0d0d73ef57f3228856bc70e5b7833d5df0f (@strata-sc/server-components 0.7.0). The 0.7.0 and
Analog 0.7.0 work described below is merged (#118, #119, #120); the Analog 0.7.0 version PR (#121) was still
open and is untouched. strataServerComponents({ buildId }) compiles a consumer-chosen label into every
environment of one build; server-rendered boundaries carry data-strata-build and the browser compares it
before creating any island. StrataBoundaryError gains a stable category, a confirmed mismatch of two
well-formed identities is StrataBuildMismatchError, and provideSkewRecovery() adds an opt-in, bounded
(sessionStorage counter) reload of the current document for that case only. Protocol numbers are unchanged;
without the option nothing changes. Qualified with three independently built releases of an external consumer
installed from the packed tarball, a document of one served with the script of another, on Nitro Node and
local workerd (125 checks). Not deployed, not hosted-CI qualified when written, no production adoption; scripts
built before the feature cannot detect anything. See the
evidence report.
Analog streaming lifecycle for 0.7.0 (merged in #120; version PR #121 open when written)
On branch feat/analog-stream-lifecycle-v07, work began from synchronized main
279275eafe7519725b8413046335fc91330795d6 (@strata-sc/analog 0.6.0). The old behaviour is reproduced on the real
fixture: an ordinary Response over a ReadableStream finds its request-scoped resource already closed when
Nitro reads the body. @strata-sc/analog gains streamResponse(body, init?), an explicit opt-in that holds the
invocation's cleanup scope until the body completes, fails or is cancelled, then closes it once (LIFO, awaited,
AggregateError rules unchanged). Ownership is claimed after every interceptor settles, so a replaced response
closes eagerly; an owned body read before the claim fails with a configuration error. Every other handler keeps
the eager contract. Qualified on Analog 2.7.2 / Nitro 2.13.4 / H3 1.15.11: Node (production and dev) delivers
incrementally; local workerd (Cloudflare Pages) buffers the body but still releases once. Client disconnect does
not reach the body on either runtime (NO-GO; bodies must be finite), and what a client sees after a mid-body
failure is up to the transport (Node: connection left open; workerd: empty 200). A real compiled serverFn
(GET and POST, validated input) runs in the same application, undisturbed by the API boundary; POST needed the
@analogjs/router h3 dependency declared (workspace packageExtensions), and on workerd every serverFn
answers 500 (Angular JIT). Packed consumers (TypeScript 6 and 5.9.2) stream from tarballs, including a request
Angular injector that outlives the handler. See the evidence report.
Experimental fixture evidence: not hosted-CI qualified when written, not deployed, not production adoption, and
not M3 or Strata 1.0 certification.
Server-only firewall vs the dependency optimizer (0.9 candidate)
From main 481976818675519015edddd209b7bd3c4fc3c83d. A bypass was reproduced against the installed Vite 8.3.0
(Rolldown 1.2.8) dev server: package modules asserting server-only, barrels re-exporting them and dynamic imports of
them were served pre-bundled. strataServerComponents now adds a plugin to optimizeDeps.rolldownOptions.plugins that
fails the optimizer run (no chunk) and a Connect error middleware that strips frame/source/stack from [strata] dev
errors. No new public option. Locally integrated and packed-consumer qualified through
pnpm test:server-component-optimized-deps; unit tested; not hosted-CI qualified, not deployed, not production adopted.
The merged release authorizer was found rejecting its own genuine CI run (path spelling) and received a minimal fix
with tests, issue #117 stays open until a hosted run proves it. See the report.
Server Components SSR integrity for 0.7.0 (merged in #118, released as 0.7.0 in #119)
On branch feat/server-components-ssr-integrity-v07, work began from synchronized main
a54948b614368d8d0246f11fca34e72ea02838aa (@strata-sc/server-components 0.6.0, @strata-sc/analog 0.6.0).
The intermittent Server Component SSR failure (HTTP 200, empty island, JIT compiler unavailable) has a
reproduced mechanism: Analog builds the client and ssr environments concurrently with one shared Angular
plugin that discards its compilation output when the first environment ends, so the other environment's files
are skipped and ship with their decorators uncompiled. The plugin now holds each environment at buildEnd
until every environment has finished transforming, and a post-transform gate fails a production build that
still contains an uncompiled source-owned Angular class or an unlinked partial declaration. The qualification
suites now classify every SSR answer (an HTTP 200 alone is never a pass) and the packed consumer adds two
failing-build controls, an adversarial environment schedule, and cold-start overlapping first requests. The
mechanism was reproduced by fault injection, not observed naturally (0 of 18 un-injected local builds), and
the two CI failures are consistent with it but were never captured with their build output; the failure is
therefore mitigated and made detectable, not proven eliminated. See the
evidence report. Experimental fixture evidence, not a
deployment and not Strata 1.0 certification.
External Angular package islands (2026-10-21; merged in #107, released as 0.4.0)
Starting main SHA: 88f68832361694caf683d529adc15d5e628559ea. The clientComponents registry,
import matching, alias-aware package references, package surrogate imports and Vite package
prebundling/SSR inclusion are implemented in the feature branch. The Server Components unit suite
passes (257 tests); package typecheck/build and repository lint/format/test gates pass. The isolated
packed consumer passed npm installation, TypeScript 5.9/6 declaration checks, Angular AOT, Node SSR,
Chromium hydration/interaction, installed-plugin server-only tests, Vite dev/HMR, Cloudflare
Pages/workerd SSR and hydration, and graph-confidentiality checks. The consumer library is
independently partial-compiled and packed in Angular Package Format. Hosted CI, measured bundle cost
and production adoption were pending when this was written. See
package-island evidence. Superseded: the feature was
merged in #107 and published as @strata-sc/server-components 0.4.0 (see its CHANGELOG); the packed consumer
still gates it on every pull request. Production adoption remains unestablished.
Analog HTTP routing and API boundary for 0.6.0 (merged in #113, released as 0.6.0 in #116)
On branch feat/analog-http-routing-v06, work began from synchronized main
b7ab2d6173fc3710ffb9fed92b64a612ba8e8063. registerControllers() gains the opt-in apiBoundary option:
explicitly owned prefixes answer a safe JSON 404 for unknown paths, 405 with Allow for unsupported
methods, a derived HEAD and an opt-in plain OPTIONS (never a CORS preflight), without shadowing native
Nitro routes, pages or Analog server-function dispatch. Package unit tests (173), real Analog dev and Nitro
production HTTP checks, the packed external consumer on Node production and local workerd (including a
hydrated island that observes JSON answers), and the full local CI matrix pass. Server functions are covered at
route-table level only; no real serverFn module was exercised. The intermittent Server Component SSR failure
seen on PR #112 was reproduced once on main and was unresolved when this was written; it has since been
traced to a reproduced mechanism (see the 0.7.0 section above); see the
original report. Experimental fixture evidence, not a deployment and not Strata
1.0 certification.
Analog HTTP application contract for 0.5.0 (feature branch)
On branch feat/analog-http-contract-v05, work began from synchronized main
0031b654058dc0658cab65eb2fd360b7dcad4b15. After main advanced with the
Angular CDK patch update in PR #92, the branch was rebased onto
ff1034def709abe2d92e13e2fcbb4b39e7fd1b77. @strata-sc/analog adds
StrataAnalogHttpError, jsonResponse() and noContent(). The route boundary
translates expected errors only after successful request cleanup; cleanup
failures and unexpected exceptions retain Nitro's failure behavior. The
fixture's protected inventory API exercises Standard Schema body validation,
401/403 guards, 404/409/422 application errors, 201 JSON and empty 204.
Focused package tests, real Analog Node production/dev HTTP qualification,
local Pages/workerd and packed-consumer behavior pass. Repository build,
lint/typecheck, unit/release/performance tests, integration matrix and Entwine
check/build pass. Website E2E passes against a fresh server on a dedicated
port; the initial default-port run reused an unrelated process on port 3000.
Cancellation remains unavailable: pinned Nitro/H3 does not expose Cloudflare's
native incoming Request signal to the Strata event; the public
StrataAnalogRequest has no signal. See the
qualification report. This is
experimental fixture evidence, not production deployment or Strata 1.0
certification.
Packed Angular DI consumer (2026-10-20)
Starting from synchronized main 8272ce98e3908c40d788ce00363c675917c85a34,
test/analog-packed-angular-di-consumer adds a Node-only profile that installs the current
@strata-sc/{core,analog,server-components} tarballs into an external Analog app. Strict
TypeScript 6, a real Nitro Node production server, consumer-owned Angular root/request injectors,
synthetic guard decisions, 24 concurrent requests, request cleanup and the Nitro close hook pass.
This is a partial qualification: the complete two-interceptor and validation matrix, failure
precedence, bundle/confidentiality comparison, and broader CI/Entwine reproduction remain open. The
Worker JIT NO-GO is unchanged. See
the external consumer report.
Angular DI with Analog HTTP composition (2026-10-20)
On feature branch test/analog-di-http-composition, starting from synchronized
main eb1d17a81283d57569e9dc1b4712dea2623f6fd6, the existing SPEC-003
CatalogController is composed with the existing guard, interceptor,
Standard Schema and cleanup APIs. The separate public SPEC-003 routes retain
their prior behavior. Node dev and Nitro production evidence, provider
overrides, failure precedence, a 24-request matrix and remaining limitations
are recorded in the composition report
and regenerated integration baseline.
The known local workerd Angular JIT NO-GO is preserved. The minimal packed Node DI profile is
partially qualified; shared SSR/controller DI, real application adoption and Strata 1.0 remain
unqualified.
Analog request-scoped HTTP interceptors (2026-10-20)
Starting from 8417d60e040190a6a68c91a96f8380db934908bf, the feature branch
feat/analog-http-interceptors adds opt-in onion-style wrappers to
@strata-sc/analog. Guards precede interceptors; interceptors wrap the one
factory/handler execution and share its cleanup scope. Focused package tests
pass (109 tests), as do the package typecheck, Node dev/production integration,
packed TypeScript/Node consumers, and local workerd qualification. The full
local CI-equivalent matrix and Entwine validation pass except for the standalone
www E2E data-app-ready assertion (no apps/www changes; www Server Component
dogfood passes). Hosted Actions are pending after push. This does not qualify
M3, production cancellation or Strata 1.0. See
the interceptor report.
Analog request-scoped guards (2026-10-19)
The feature branch feat/analog-request-guards adds opt-in registration-level
guards to @strata-sc/analog. Guards run sequentially before controller
construction, receive immutable route identifiers and the existing request
cleanup hook, and return an explicit allow or 401/403 denial. Identity and
authorization remain consumer-owned. Analog unit, real production/dev Node,
packed TypeScript 5.9/Node, and full-stack packed Node/local workerd fixture
checks pass. The local CI-equivalent matrix and Entwine checks pass, while
hosted Actions remain pending. This is not an M3 or 1.0 qualification. See
the guard report.
Opt-in Analog request validation (2026-10-17)
At baseline 41079a32e84f5c8d652fee04d212801571cb97f4, the feature branch
feat/analog-standard-schema-validation adds validateParams, validateQuery
and readValidatedJson using Standard Schema v1 and @standard-schema/spec.
The API is opt-in and preserves raw readers. Validated JSON is bounded to 1 MiB;
safe 400/413/415 JSON responses are created only after successful cleanup.
Package-level tests, Zod/Valibot interoperability, transformed output inference,
and 24 overlapping mixed requests pass. Real Analog Node production/dev,
Cloudflare Pages/workerd, packed TypeScript 5.9.2 and full-stack TypeScript 6.0.3
and 5.9.2 consumers pass. External full-stack workerd rehearsal also passes;
no public deployment was run. Evidence and explicit remaining gaps:
research report. This does not qualify
authentication, authorization, guards/interceptors, production adoption, M3 or
Strata 1.0.
Full-stack performance scaling: one island versus three distinct islands (2026-10-16)
At 546b35ccb708d784ef46bea2eb35a90736a3da79, STRATA_PERF_ISLANDS=1|3 pnpm test:full-stack-performance runs the
same four arms with one or with three distinct interactive islands (default 1; no other count is accepted).
One island reproduces the PR #86 numbers; with three, the Server Components browser overhead did not grow
(+4.3 → +3.6 KiB raw, +1.7 → +1.5 KiB gzip; the net is confounded with server-only code exclusion), controllers still add 0 browser bytes, and
the full-stack server/Worker overhead stayed at about 10 to 11.5 KiB raw / 3.4 to 4.4 KiB gzip. A hosted run of the existing extended.yml
(islands=both, same runner, run 38904588640) agrees with local to a few bytes. Hydration,
one-request-per-click interaction and confidentiality hold in every arm; 11 negative controls are rejected. The PR #86 candidate budgets stay
PROPOSED and unenforced (browser: supported on this fixture; server: insufficient evidence). R13 stays open; cold start, Worker memory,
cancellation/cleanup, production budgets and real-application performance remain NOT MEASURED/NOT QUALIFIED. Details:
report.
Full-stack performance and bundle-cost baseline: first controlled measurement (2026-10-16)
At 1297271669ed9c419caf6f9997a3e58bffb06bf2, pnpm test:full-stack-performance builds one small Analog
application in four arms (native, controllers, Server Components, both) from the packed tarballs outside
the workspace and measures Nitro node-server and local workerd. On this tuple the incremental cost is
small and deterministic: controllers add 0 browser bytes and +4.2 KiB (Node) / +7.5 KiB (Worker) server
graph; Server Components add +1.7 KiB gzip browser and about +4 to +7 KiB server graph; both together
+11.1 KiB (Node) / +11.5 KiB raw, +4.4 KiB gzip (Worker), with no interaction. No latency or RSS difference is
detectable above run-to-run noise. Confidentiality holds in every arm and six negative controls are rejected.
Candidate budgets are proposals only: nothing is enforced, R13 stays open, and cold start, Worker memory,
cancellation/cleanup and real-application performance are NOT MEASURED/NOT QUALIFIED. Main's own CI was
already red at this SHA (npm 10 peer-resolution crash with vitest 5), unrelated to this work. Details:
report.
Cloudflare controller concurrency anomaly: not reproduced, cause unconfirmed (2026-10-15)
A diagnostic increment classified and attempted to reproduce run 38727456938's single anomalous controller
answer. Local Node, local workerd and the existing public deployment each ran a bounded matrix (829, 829
and 478 requests) with 0 anomalies; the adapter audit found no cross-request state. One new dispatch
(38741663534, deployment 0a77889b) passed
every check, so that candidate is GO on Cloudflare Pages; the two earlier runs stay NO-GO and the root cause
is UNCONFIRMED. Details: report.
Full-stack deployed Cloudflare qualification: two real runs, both NO-GO (2026-10-15)
The workflow was dispatched twice (runs 38722004014
and 38727456938). Run 1 deployed but the
runner could not reach the new project's URL (fetch failed ×40). Run 2 passed every functional verifier on
the public URL (controller, Server Component SSR, island, confidentiality: GO) but failed the bounded
concurrency smoke (1 anomalous answer in 25 concurrent controller requests), so the real deployment is
NO-GO, not GO. Cause of the anomaly is unknown; a third run is pending a decision. Details:
report. The paragraph below is the earlier,
pre-dispatch state.
Earlier state: machinery added, deployment NOT RUN
At 32a174707b356c300ce91dae51eedf92cf45f043, pnpm test:full-stack-deployed-cloudflare takes the SAME
external consumer as below, built from the three source-packed tarballs (SOURCE-PACKED CANDIDATE, not a
registry release), builds it with Nitro's cloudflare-pages preset (no nodejs_compat), and runs the
black-box verifier on local workerd (142 checks, passing; a rehearsal, not a deployment). The manual workflow
full-stack-deployed-cloudflare.yml deploys to the isolated strata-fullstack-qualification project and
verifies the public URL. The real deployment has not run: REAL DEPLOYMENT: NOT RUN, and the deployed
controller, SSR, island and confidentiality verdicts are NOT RUN. A GO would still describe a fixture, not
authentication, tenant caching, shared DI or a real application. See the
report.
Full-stack external consumer: core + analog + server-components (2026-10-15)
At 2f4f37d37c26690b2b7790835a38ecaf2f5ee056, pnpm test:full-stack-package-consumer packs
@strata-sc/core@0.2.1, @strata-sc/analog@0.2.1 and @strata-sc/server-components@0.2.0, installs the
three tarballs with npm into one Analog app outside the workspace and proves, on Nitro node-server and
local workerd (no nodejs_compat), a Strata controller API called by a hydrated island next to a
server-only @ServerComponent(): tarball/lock/single-core identity, declarations under TypeScript 6.0.3
and 5.9.2 (skipLibCheck: false), controller routing, request-scoped lifecycle, SSR, SSR DOM reuse, one
click = one request = one state update, document navigation with the opt-in guard, graph/canary scans
and the server-only firewall. Controller and Angular SSR DI stay independent and shared DI is NOT
QUALIFIED. A fixture is not production adoption. See the
report.
Server Component request-bound authorization isolation (2026-10-14)
At 4ffee83d874c86d425d5c6ea21191bbb290e5e2c, the fixture adds a narrow,
fixture-owned authorization scenario at /server-component-authorized/:tenant.
An independent per-render provider resolves a principal only from exactly one
synthetic opaque HttpOnly cookie. Route, query and arbitrary identity-header
values remain untrusted resource or override inputs. The protected service
checks the verified principal against the requested resource before calling its
server-only repository; a request-local read trace proves denied requests never
read the target record.
pnpm test:server-component-authorization passes on Nitro node-server and
local Wrangler/workerd with 200/no-store own-tenant reads, 401/no-store missing
or invalid credentials, 403/no-store cross-tenant denials, 404/no-store unknown
resources, duplicate-cookie rejection, 24-request overlap in both orders,
expected-failure recovery, graph scans and browser document navigation with
HttpOnly cookies. This is synthetic fixture evidence, not production
authentication, a Strata authorization API, tenant-aware caching or a 1.0
qualification. See the authorization report.
Server Component SSR response and expected service recovery (2026-10-14)
At 7738b475fd82e1e6f4db8dcb642e4092c42f9356, the public Angular/Analog API investigation found
no general bridge from an unexpected buffered Server Component render error to an HTTP status or
fallback response. Angular's public RESPONSE_INIT is not provided by Analog; Analog's public
RESPONSE token is a mutable consumer-owned response passed through the Nitro renderer. The
existing constructor, injected-service and template failure controls therefore remain HTTP 200
with Angular's empty or half-rendered output.
The fixture now demonstrates a narrower supported pattern at
/server-component-ssr-recovery: a typed application service catches only an expected repository
failure, sets Analog RESPONSE to 503/no-store and renders generic HTML; missing or malformed
request context is 400/no-store. Unexpected errors are rethrown. The new
pnpm test:server-component-ssr-response gate passes on Nitro node-server and local Wrangler/
workerd, including concurrent isolation and document navigation/Back/re-entry. This is a narrow
consumer-owned GO, not a Strata package API or a general render-failure fix. See the
response and recovery report.
The full failure criterion, streaming, true SPA Server Component navigation and Strata 1.0 remain unqualified.
Server Component document navigation APIs (2026-10-21)
The optional @strata-sc/server-components/navigation entry now exports StrataDocumentLink,
StrataDocumentNavigator and the hardened strataDocumentNavigationGuard, all on one same-origin
resolver built on public Angular APIs (starting main 21baee81f644b46b2142ca3dfd789a691417f16c).
Links stay native anchors; navigation is always a real document request; the guard cancels Router
activation in canActivate. SPA subtree hydration, streaming and an SSR-error status redesign remain
out of scope and unsupported. See the report.
Server Component document-navigation fallback (2026-10-14)
At f62722b3e0ba23c80e61669bca7f4f056081979c, the fixture qualified a consumer-owned experimental
fallback for Angular Router transitions into designated Server Component routes. This increment
promotes that policy to the optional @strata-sc/server-components/navigation entry as
strataDocumentNavigationGuard. A public Analog defineRouteMeta/Angular canActivate guard
allows SSR and initial bootstrap, then uses public Router state and same-origin browser
Location.assign to cancel later SPA activation and request a fresh document. The fixture now
consumes the package API; its separately identified unprotected route retains the historical empty
surrogate control without a URL-controllable bypass. Node Nitro qualification continues to cover
RouterLink, navigateByUrl, Back/Forward, query/fragment preservation, SSR HTML, DOM-reusing island
hydration, exactly-once interaction and graph confidentiality.
This is CONDITIONAL GO. Explicit document navigation is SUPPORTED; the opt-in package fallback is EXPERIMENTAL and a full reload; SPA hydration of a newly fetched Server Component subtree, streaming, automatic route discovery, generic router integration and Strata 1.0 remain UNSUPPORTED/unqualified. See the fallback report.
Request-scoped Server Component context and service isolation (2026-10-14)
At baseline e06fae5, Angular 22.1.7 publicly exposes the SSR-only
REQUEST_CONTEXT token and Analog 2.7.2 publicly forwards per-render
platformProviders to renderApplication. The fixture provides an immutable
synthetic value through that seam and consumes it from a parent Server Component,
a nested server-owned child and a server-only service/repository. The fixture
now allow-lists alpha/beta, rejects unknown or mismatched identities into an
empty safe state for rejected identities, consumes distinct private values, and proves the parent
and child share the component-provided service instance within one render.
pnpm test:server-component-request-context covers overlapping and sequential
requests, failure/recovery, Node and local Cloudflare workerd, graph controls,
and browser-output absence of both private values.
This is a narrow experimental GO for buffered document SSR and synthetic fixture data isolation. It does not qualify controller/SSR shared DI, Angular Router navigation, streaming, authentication, authorization, tenant/cache isolation or Strata 1.0.
Reconciliation 2026-09-24
Inspected at c671ba4 (main, merge of PR #19) plus the SPEC-003 branch. The sections after this
one are the 2026-09-17 audit at f258fc3. They are kept as recorded, and this section supersedes
their current-state claims where the two differ. Work now runs on two tracks with independent gates:
| Track | Scope | State |
|---|---|---|
| H3 consumer qualification | @strata-sc/h3 (H3 v2) packed consumers, strict declarations; M1 | SPEC-001 and SPEC-002 executed. Upstream-blocked: H3's HTTPError.isError lib assumption and its crossws import graph fail strict TS 6.0.3 consumers; no published closure qualifies. pnpm test:consumer stays failing on AC2 by design. Unchanged by later work. |
| Analog integration / Server Component feasibility | @strata-sc/analog (Nitro 2 / H3 v1) inside a real Analog 2.7.2 app; M2 | Node integration baseline passes in dev and production; the ordinary packed and full-stack consumers also pass, but neither exercises Angular DI in an external consumer. SPEC-003 controller DI is NO-GO on local workerd because Angular JIT is refused. Guard/interceptor composition with the DI-backed controller remains unqualified (cross-runtime report). @strata-sc/analog exposes no H3 v2 (or v1) declarations, so the H3 type blocker does not apply. |
Analog track increments since the 2026-09-17 audit:
| PR / spec | Increment |
|---|---|
| #7 | Analog integration baseline fixture and pnpm test:analog |
| #8 | @strata-sc/analog: registerControllers(nitroApp.router, …) from a Nitro plugin; depends only on @strata-sc/core |
| #17 | Provisional StrataAnalogRequest (method, path, URL, headers, params, query, context snapshot, lazy body readers); Nitro/H3 event not exposed |
| #18 | Controllers are request-scoped: no instance at registration, one per request through experimental controllerFactory (default new Controller()) |
| #19 | Experimental onCleanup on the factory context: LIFO, awaited, exactly once, on success and every failure path, before the route handler settles |
| SPEC-003 | Angular DI feasibility: CONDITIONAL GO through consumer-owned Angular injectors on the existing seam; no Strata API change (report) |
| SC PoC | Server-component graph PoC: CONDITIONAL GO. Server component + transitive server-only deps absent from browser output; child hydrates onto SSR nodes and is interactive; fixture-only, no package change (report) |
| SC nav | Server-component navigation: CONDITIONAL GO. Explicit document navigation GO; the optional @strata-sc/server-components/navigation entry provides an experimental Angular canActivate fallback for designated RouterLink/navigateByUrl transitions; unprotected SPA navigation remains NO-GO with the empty surrogate and is retained as a separately identified control (fallback report, baseline report) |
| SC CF | Server-component PoC on Cloudflare Pages (local Wrangler/workerd): CONDITIONAL GO. SC direct load, document navigation, controllers GO; SPEC-003 Angular DI NO-GO (JIT refused) (report) |
| SC pkg | Server-component mechanism extracted into the public @strata-sc/server-components package (0.1.0 on npm, next channel, experimental, document-navigation-only); the fixture consumes it. Client references are explicit [strataClient] boundaries. PoC verdicts unchanged (README) |
| SC compose | Server Component composition (public experimental package): ordinary server-only child and grandchild components, nested Server Components, and transitive client-boundary discovery through local imports; interactive (event, two-way, host-listener) bindings in server-owned templates fail the build. Dogfooded by Relay (pnpm test:relay:server-components) (README) |
| SC protocol | Client boundary protocol v1 (public experimental package, protocol not stable): [strataClient] props are validated at runtime on the server (flat plain object; string, finite number, boolean, null; __proto__/prototype/constructor rejected; 64 props; 64 KiB UTF-8) and written beside data-strata-protocol="1". The browser preflights every boundary of a Server Component host (protocol, JSON, values, limits, selector, public input names) before creating any island, and rolls back a failed commit. Fixture /server-component-boundaries; pnpm test:server-components (Node, fail-closed matrix) and pnpm test:server-components:cloudflare (positive path) (README) |
| SC defer | Server Components + Angular @defer (public experimental package): @defer inside a [strataClient] component is Angular's (incremental hydration on interaction and viewport, lazy chunk fetched only on the trigger, event replay, SSR DOM reused); @defer in any server-owned template fails the build with a shape-specific diagnostic. Analog 2.7.2 apps need environments.ssr.define.ngServerMode = "true" for SSR to honour hydrate triggers. Fixture /server-component-defer, Relay rollout island; pnpm test:server-component-defer (Node), pnpm test:server-components:cloudflare (workerd) (report) |
| SC firewall | Server-only modules (public experimental package): import "@strata-sc/server-components/server-only"; is a build-time assertion that the module must never enter the browser graph; barrels re-exporting a marked module inherit it; ordinary imports do not propagate. Illegal direct, dynamic, @defer-lazy, barrel, chained-barrel, ?raw and ?url browser imports fail real production builds with a Strata diagnostic. Fixture canary, www and Relay dogfood; pnpm test:server-component-server-only (README) |
| SC protocol v2 | Structured boundary props: readonly authored view models of primitives, dense arrays and plain objects; v1 remains selected for flat primitive props. The shared server/browser validator snapshots values and enforces depth 8, 512 values, 128 array items, 64 nested object keys, 64 root props and 65 536 UTF-8 bytes. Relay Node production dogfood, packed-consumer Node/workerd, security, and the requested local CI matrix pass. Protocol remains experimental; hosted checks are pending after PR push (research). |
| SC security | Server Component data confidentiality (public experimental package, no runtime change): a server-only repository uses a synthetic DATA canary; only a safe verdict and a PUBLIC control cross. The DATA canary is absent from raw SSR HTML, headers, protocol v1 payload, hydrated DOM, public network, browser output, source maps, production errors and build diagnostics on Node and local workerd; present in the server/Worker graphs. Fixture /server-component-security*; pnpm test:server-component-security (report) |
| SC failure | Server Component failure and recovery (public experimental package, buffered SSR + document navigation, runtime change: one diagnostic only): six separate cases (server render, serialization, preflight, commit rollback, error after hydration, navigation to a failing document). One Server Component host is one hydration transaction: a failing host is inert with its SSR DOM while sibling hosts hydrate; Angular's per-callback afterNextRender handling delivers the one error to ErrorHandler, no rethrow, no retry. Server render errors answer HTTP 200 (empty outlet, or a half-rendered component for a template error): the exact current Angular/Analog behaviour, not a success; no supported Strata seam to change it. Streaming unsupported. Fixture /server-component-failures, /server-component-failure/:mode; pnpm test:server-component-failures (Node + workerd) (report) |
| SC release | @strata-sc/server-components@0.1.0 is published: PUBLIC / EXPERIMENTAL, on npm under the next channel with npm provenance (Release run 37791376843; git tag @strata-sc/server-components@0.1.0 and a GitHub Release exist). npm also set latest → 0.1.0 itself, as the package's first real release (a 0.0.0-stage placeholder preceded it); next is the supported channel and no dist-tag was edited. The exact-version registry consumer passes against 0.1.0 (fresh npm install outside the workspace, real non-symlink install, no workspace resolution, production build, graph isolation, SSR, protocol v1, hydration, interaction, server-only firewall; npm audit signatures clean) and is rerunnable through the read-only "Verify published Server Components" workflow. The original post-publish step never ran (a release.yml trigger bug, not a publication failure; PR #56 changed it to a registry pre-check, implemented but not yet exercised by a real next publish). Experimental, document-navigation-only, streaming unsupported (report). |
Current lifecycle facts:
@strata-sc/h3still creates one controller instance per controller at registration and shares it across requests. That instance is never a safe place for request state.@strata-sc/analogcreates a controller per request and provides no injector. With the consumer glue measured in SPEC-003, controllers resolve application- and request-scoped Angular services withinject()at construction. Overlapping requests are isolated (barrier-proven), and request injectors are destroyed on success, throw and rejection. The application injector is separate from Analog's SSR and server-function injectors (no supported seam reaches those), and the Nitro bundle then needs@angular/compilerdeclared innitro.moduleSideEffects.- Server components (experimental, document requests only): the Vite plugin and runtime live in the
experimental, public
@strata-sc/server-componentspackage (0.1.0,nextchannel), consumed by the fixture. The plugin sends@ServerComponent()modules to generated empty-template surrogates in theclientenvironment. Only children marked[strataClient]are client references; they are hydrated as roots from theirnghannotations. The plugin finds them transitively: it walks the Server Component's template and, through each component's ownimports, the templates of the unmarked local components it renders (ordinary children, grandchildren and nested Server Components, all server-owned), stopping at each[strataClient]boundary. The outer surrogate owns every client reference found. An event, two-way or host-listener binding in a server-owned template, a composition cycle, or an ambiguous selector fails the build. Package components are opaque (never crawled). This relies on undocumented Angular 22.1.7 hydration behaviour and on Analog's environment names. Direct load and document navigation work; an Angular Router navigation to the route renders the empty surrogate without error. Leaving through the router destroys the islands. Checked bypnpm test:server-componentsandpnpm test:server-component-navigation(in CI). - Client boundary protocol v1 (preview, not stable), evidence from the Analog fixture's
BoundaryProtocolServerComponent(/server-component-boundaries) and unit tests inpackages/server-components/src/runtime/*.test.ts:- Runtime value validation: unsupported values (undefined, NaN, ±Infinity, BigInt, functions and
signals, symbols, arrays, nested objects, Date, Map, Set, RegExp, class instances), reserved
keys, accessors and symbol keys fail with a Strata error naming the boundary, prop, received
type and allowed values, before
JSON.stringify(unit). - Limits: 64 props pass and 65 fail; 65 536 UTF-8 bytes pass and 65 537 fail, measured with
TextEncoderand a multibyte case that UTF-16 length would pass (unit). - Escaping:
" ' < > &,</script><script>…, U+2028/U+2029, emoji and non-Latin text stay inside the Angular-escaped attribute; the island receives the exact strings andglobalThis.__STRATA_XSS__is never set (Node and workerd). - Boundary identity is the SSR host element: two islands with the same selector and props are two
ComponentRefs on two hosts with independent state (Node and workerd). - Version skew fails closed: a
data-strata-protocol="999"boundary (also invalid JSON, an unknown input, an aliased input under its property name) hydrates nothing in its Server Component host, leaves the SSR DOM and interaction inert, logs one Strata error and does not reload (Node, Playwright response interception). - A commit failure after three islands destroys all three, restores their SSR hosts in place and
leaves
ApplicationRef.viewCountat its zero-island baseline (Node). - An invalid value at SSR logs the Strata error and never reaches the HTML (no
data-strata-propsis written), so the browser refuses that host. Angular reports the binding error to itsErrorHandlerand completes the render: the HTTP status stays 200, owned by Angular/Analog.
- Runtime value validation: unsupported values (undefined, NaN, ±Infinity, BigInt, functions and
signals, symbols, arrays, nested objects, Date, Map, Set, RegExp, class instances), reserved
keys, accessors and symbol keys fail with a Strata error naming the boundary, prop, received
type and allowed values, before
- Structured client boundary protocol v2 is implemented in the 0.3.0 candidate. Flat primitive markup stays v1; v2 carries recursively validated snapshots with finite depth/count/key/array and UTF-8 byte caps. Unit tests cover boundary and above-boundary cases, no-getter/custom-serializer behavior, cycles, dangerous keys, sparse arrays, proxies, repeated references and v1/v2 shape mismatches. Relay proves a nested public incident model hydrates in Chromium and preserves the SSR DOM. Packed-consumer Node/workerd verification passes. Hosted CI is pending; release qualification and protocol stability are not claimed.
- Server Components and Angular
@defer(fixture/server-component-defer, Relay's rollout island; report):- Two layers:
StrataIslandHosthydrates each[strataClient]root on load (unchanged, not lazy); Angular hydrates each@deferblock inside that root's own template on its trigger. hydrate on interaction: the lazy chunk is requested only by the click, the widget is constructed once, one click produces one effect (event replay), and the SSR host, button and text nodes are kept (Node, workerd, Relay).hydrate on viewport: the lazy chunk is requested only when scrolled into view (Node, workerd).- Bundle: each deferred dependency is a chunk reached only by dynamic
import()from the island's chunk, never in the static closure of the entry or the island; server parent and server-owned child stay absent from the browser. - Protocol v1 still gates the island: a tampered version refuses the host, and the deferred chunk
is never requested even on interaction (Angular then logs a
TypeErrorfrom its replay contract, recorded). @deferin a server-owned template (root, ordinary child, nested Server Component; any trigger,hydrate neverincluded, with or without a[strataClient]inside) fails the build. Measured before the rule: the placeholder is never replaceable, hydrate triggers throw or never complete, an island inside is hydrated on load regardless of its trigger, and underhydrate neverit is rendered twice.- Analog 2.7.2 defines
ngServerModeasfalsefor the SSR graph of a production build (and rewrites onlycore.mjs), so SSR renders hydrate-trigger placeholders until the app setsenvironments: { ssr: { define: { ngServerMode: "true" } } }(fixture and Relay do).
- Two layers:
- Server-only modules (fixture
src/app/server-component/, www, Relay; README):- Assertion:
import "@strata-sc/server-components/server-only";, an empty package entry (no imports, no code). The plugin pre-scanssourceDirwith the TypeScript AST atconfig(); the specifier inside a comment, string or template literal does not mark a module (unit). - Re-export taint:
export * from,export { X } fromandexport * as X frompropagate the restriction upward to a fixpoint (chains and cycles, unit); ordinary imports andexport type { … } fromdo not.shared-format.ts, imported by the marked repository and by theAddToCartisland, is bundled for both graphs. - Direct browser import rejected: a temporary
[strataClient]island importing the markedProductRepositoryfailsvite buildwith[strata] Server-only module entered the browser graph: src/app/server-component/product-repository.ts/Imported from:the island /Reason:/ fix. - Dynamic import rejected:
await import("…/product-repository")in the island fails the build with the island as importer; no lazy browser chunk is emitted. The same holds for a dynamic import inside a component loaded by the island's own@defer(importer: the lazy component). - Re-export/barrel rejected: a barrel re-exporting the repository, and a barrel B → barrel A → repository chain, fail the build naming the barrel the browser reached and the full chain.
- Raw-source import rejected:
server-secret.ts?rawand?urlfail the build (query stripped, underlying module checked). A query import of a@ServerComponent()module now fails the same way instead of resolving to its surrogate. - No browser output exists after any of these failures; a legal control (Server Component → marked repository, island → shared module) builds.
- Node graph proof: the canary
STRATA_SERVER_ONLY_CANARY_7F3D9A41C2E5(in the markedserver-secret.ts) is indist/ssrand the Nitro server, absent fromdist/client,dist/analog/publicand every browser source map; the marked modules are in SSR source maps only; the assertion entry is bundled nowhere in the browser; the page renders through the marked repository on Nitronode-server. The plugin-off control leaks the canary and both modules. - workerd graph proof:
pnpm test:server-components:cloudflarefinds the canary in the Worker graph reachable from_worker.jsand not in the public assets; www's marked repository and proof module are in its Worker graph and absent from its Pages assets. - Dogfood: Relay marks
server-operations-intelligence.tsandincident-digest.source.ts; www marksserver-component-facts.repository.tsandserver-component-proof.ts. Their gates check the assertion in source (AST) and the modules' absence from browser source maps. - Superseded for aliases (0.6 candidate, see
the report): aliased re-exports (Vite
resolve.alias, tsconfigpaths, index files) are now followed by the pre-scan. Still not covered: package re-exports in the pre-scan (packages are enforced through their assertion import, not crawled), marked modules outsidesourceDir(rejected only when their assertion import is resolved), data disclosure through HTML, errors or caches (R11: covered for synthetic canaries by the SC security entry below). Security as a whole is not complete.
- Assertion:
- Server Component dev server (
vite; fixture/server-component-dev, report,pnpm test:server-component-dev, real Analog dev server and Chromium, VitehotUpdate):- Initial dev graph: derived at
config()before Analog reads its tsconfig, as in a build; the page SSRs, its island hydrates once and a click has one effect; the server-only repository runs on the server and its canary is in no browser response or HMR frame (positive control: a public module's canary is). - Hot regeneration: after every
.ts/.htmledit undersourceDirthe graph is analyzed whole in memory and then committed (generated surrogates synchronized: unchanged ones not rewritten, stale ones deleted); an analysis error keeps the committed graph and files. 128+ refreshes in the gate: median 6 ms on the fixture. - Server-owned reload: an edit to the Server Component, an ordinary server child, a
templateUrlgrandchild each replaces the JS realm (sentinel gone) with exactly one document request and one Strata reload; SSR HTML has the new text and not the old; the island hydrates once; one click is one effect. - Boundary lifecycle: add, swap (B → C) and remove a
[strataClient]boundary regenerate the surrogate (imports exactly the current client reference), reload once and leave only the current islands; repeated server/boundary cycles leave one island and one ComponentRef per realm. - Client coexistence: a client-island edit leaves the graph unchanged and Strata sends no reload.
Analog's default (
liveReload: false) then has Vite reload the page itself; withliveReload: trueAngular's component HMR updates the island (same realm, no document request, state kept). Server-owned edits underliveReload: trueare not qualified: Analog leaves SSR stale for any edited component there (reproduced with the plugin off). - Live server-only: adding the assertion to a public module, or making a barrel re-export a marked
module, while Vite runs terminates the old realm and fails the new one closed (HTTP 500 with the
Strata diagnostic; dynamic
import(),?rawand?urlrejected; no canary in any response or HMR frame after the edit); removing it recovers without a restart. Bytes already delivered to the old realm cannot be revoked; the reload ends that realm. - Invalid edits:
@defer, an event binding, an unresolved[strataClient]each give the analyzer's own diagnostic in the server log and Vite's overlay, the client graph refuses every app module (including ones Vite had cached) and the fix recovers with a reload, no restart. A later CI failure was traced to the harness taking its log mark before restoring files while child-process output arrived asynchronously. The assertion now starts after observable browser recovery and a bounded quiet-log boundary; scenario K re-invalidates after recovery to prove a new failure still closes the graph (evidence). - Create/delete: a new
@ServerComponent()module gets a surrogate while Vite runs and renders once wired into the page; deleting it removes the stale surrogate. - Dogfood smoke: Relay (
/release: SSR, rollout island hydrates, interaction, one server-owned text edit reloads the document, restored) and www (/: demo island hydrates, one click). - Not covered: Analog
liveReload: truefor server-owned edits, plain helper modules shared with client code (not tracked as server-owned), an app invalid at startup (Vite fails to start), streaming, Router navigation, a deployed environment.
- Initial dev graph: derived at
- Server Component packed-package consumer (tarball qualified before publication, outside the workspace;
report,
pnpm test:server-component-package-consumer):- Packed tarball:
pnpm packof the built private package, 33 files, 72,222 bytes, with a package-local MITLICENSE(added;pnpm packdoes not inherit the root one). Every export target exists; no source, test or config shipped; no workspace ranges, no absolute paths; emitted imports are@angular/core,@angular/compiler,typescript,vite(types),node:fs,node:path, all declared peers; notslibimport. At that baseline the package was0.0.0,private(published later as0.1.0). - External install:
npm installof the tarball into a real Analog app in an OS temp directory, empty npm config; a real directory (no symlink), one copy, no other@strata-scpackage;.,/vite,/server-onlyresolve underconsumer/node_modulesand nothing resolves into the repository; one Angular/TypeScript universe; no UNMET or Strata-relatednpm lsproblem. - Declaration portability: TypeScript 6.0.3 and 5.9.2,
strict,skipLibCheck: false, Bundler resolution, 0 diagnostics, every Strata declaration fromnode_modules/.../dist. - External production build: graph split (three server markers only in the Node server output, the client marker in the browser), 708 browser source-map sources with none of the consumer's server files, no compiler/plugin tooling in the browser; SSR, protocol v1, DOM-identity hydration, one click one effect, document navigation and Back re-entry.
- External firewall: direct, dynamic and
?rawimports of a server-only repository fail the production build with the installed plugin's diagnostic; sources restored, next build clean. - External dev:
vitefrom the consumer'snode_moduleshydrates and, after one server-owned edit, reloads the document with fresh SSR and a new realm. - External workerd: Cloudflare Pages build, server graph in the Worker only, SSR/protocol/hydration and one click on the consumer's own Wrangler 4.135.0.
- Defects found and fixed in the package:
enabledomitted silently shipped the implementation to the browser (now defaults to true); the installed runtime fell back to the JIT compiler in Node SSR (the plugin now setsssr.noExternalfor it); source maps pointed at unshippedsrc/. - Not covered by this gate (the registry install is covered separately by
pnpm test:server-component-registry-consumer): Angular/Vite versions other than the tested tuple, a deployed environment. Router navigation stays NO-GO.
- Packed tarball:
- Server Component confidentiality, in three independent layers (fixture
/server-component-security*, report):- Module confidentiality (PR #49, unchanged): the server-only assertion keeps marked modules out of the browser graph. It says nothing about the values those modules return.
- Data confidentiality: a value that stays server-owned does not cross merely because a
Server Component reads or uses it. A synthetic DATA canary used through a server-only repository
is absent, in raw and encoded forms, from the raw SSR HTML and its comments, inline scripts,
ng-state,nghanddata-strata-*attributes, every response header, the parsed protocol v1 payload, the hydrated DOM and text before and after an interaction, cookies and storage, every public text/script response,dist/client,dist/analog/publicand every browser source map (Node and workerd); it is present indist/ssr,dist/analog/serverand the Worker graph, and changing it in the server output changes the render (it is consumed, not merely present). A PUBLIC control passed through[strataClient]is present in the SSR HTML, the payload, the island's input and the hydrated DOM. A repository instance or a nested object cast into[strataClient]fails withStrataBoundaryErrorbefore serialization, the diagnostic naming only prop and type. Everything passed through[strataClient]is browser-public by definition. - Error-response confidentiality: a plain
Error, anErrorwith a secretcauseand anAggregateErrorthrown by the server-only repository leave the public response (status, headers, body, browser console and rendered document) without the secret, a stack or an absolute path, on Node and workerd. The response is HTTP 200 with an empty outlet (owned by Angular/Analog; pinned by the failure gate). The original error is only in the server's log, which is the operator surface, not a public one. PR #49's illegal-import build diagnostic prints module, importer and reason and never a source line or the canary. - Request isolation: Angular's public
REQUEST_CONTEXTseam is now qualified narrowly for buffered Server Component SSR. The fixture allow-lists synthetic alpha/beta identities, consumes distinct private repository values, proves parent/child service-instance agreement, and passes 24 overlapping requests in both orders plus sequential and failure/recovery checks on Node and workerd (isolation report). This remains fixture-only evidence, not authentication, authorization, tenant/cache isolation or a Strata-owned request API. The runtime holds no global payload registry, process-wide boundary store or secret cache; module-level state shared between Angular SSR and Nitro (Relay'sprocessSingleton) is a domain-state workaround, not request or tenant isolation. - Not covered: authorization, tenant/cache isolation, origin/CSRF, dev-server confidentiality beyond the live firewall below, a real deployed environment, log redaction, third-party error reporters.
- Server Component failure and recovery (fixture
/server-component-failuresand/server-component-failure/:mode, report); buffered SSR and document navigation only, Node and local workerd:- Server render error (constructor, injected provider, template): the error reaches the server
ErrorHandleronce; the response is HTTP 200 with an empty router outlet (construction) or a half-rendered component whose boundary host has nodata-strata-*attribute (template). This is the exact current Angular/Analog buffered behaviour, not an ideal or a Strata success response. No public seam (renderApplicationandBEFORE_APP_SERIALIZEDroute errors toErrorHandler; only a global rethrowing handler would change the status) exists, so Strata ships no recovery. - Serialization error: HTTP 200, one
StrataBoundaryErroron the server, nodata-strata-propsand no value in the HTML; the boundary host keepsdata-strata-clientanddata-strata-protocol. The browser refuses it at preflight and hydrates nothing. - Preflight and commit failures are per host: B inert with its SSR nodes, A and C hydrated and
working, exactly one
ErrorHandlercall and oneconsole.error, nopageerror. Commit failure destroys every island that commit created (constructed = destroyed, views = baseline − B's), restores the same DOM nodes in place and reports the component's ownError. No retry or reload. Stale assets (protocol 999 on a host) behave the same; automatic recovery is not implemented. - After hydration, a throwing handler is Angular's: reported per click, no Strata intervention.
- Document navigation into a failing route, Back (a new
back_forwarddocument, not bfcache) and re-entry are fresh lifecycles, one server report each, no stale client state. - Runtime: no
ErrorHandlercode was needed (Angular already catches perafterNextRendercallback and does not rethrow). The preflight "invalid JSON" error no longer repeats the JS engine's message, which some engines build from the payload. - Streaming (Analog
experimental.streaming) is unsupported and not qualified.
- Server render error (constructor, injected provider, template): the error reaches the server
- Cloudflare Workers (local workerd only, nothing deployed): the same fixture built with
BUILD_PRESET=cloudflare-pagespasses the server-component, navigation and controller assertions underwrangler pages dev. The SPEC-003 Angular DI route fails there, because workerd refuses Angular's JIT code generation. Checked bypnpm test:server-components:cloudflare(in CI). - Published package on Cloudflare Pages (
@strata-sc/server-components@0.1.0from registry.npmjs.org, fresh external app,cloudflare-pagespreset, compatibility date 2026-09-21, no flags): the local build qualification passes (Worker graph holds the three server markers, the browser graph and its source maps none) and the verifier passes against local workerd. The real deployed Server Components qualification is GO (narrow scope): Actions run 38243892864 (mainat2c3f495c, 2026-10-12, deploymenthttps://9b341445.strata-sc-qualification.pages.dev) passed the whole verifier from a GitHub runner with 0 failed checks: Node readiness (5 consecutive 200s on attempt 6, 10.9 s; attempt 1 was Cloudflare's 404), fresh Chromium readiness (3 consecutive on attempt 3, 0 misses), SSR and protocol v1, hydration with SSR DOM reuse,Count: 0→Count: 1, document navigation/product→/about→ back, reload, confidentiality (HTML, headers, scripts, assets, source maps, Worker bundle not served, props payload) and the 5/10/25 request smoke (all 200; p50 35.1/137.4/210.3 ms). It is not a tenant-isolation result, not Angular Router navigation, not streaming. History: four earlier deployments (runs 38055222217, 38055684868, 38133853890, 38137809896) failed on readiness or Chromium receiving Cloudflare's "Deployment Not Found" 404; the cause of that edge disagreement was never proven, and PR #61's fresh-Chromium readiness gate is what the passing run adds. One passing run is one data point. Runnerpnpm test:server-components:deployed-cloudflare, workflowserver-component-deployed-cloudflare.yml(report). The complete Analog deployment criterion (controllers, services, bindings) is still not qualified. - Not yet verified on the Analog track: a deployed Cloudflare Pages project with controllers, services and bindings, abort/timeout cleanup,
streamed-body lifetimes. (Packed
@strata-sc/analogconsumers and non-GET methods are now covered; see the gaps section.)
The first gap bullet of the 2026-09-17 audit below ("No Angular/Analog/Nitro dependency, DI integration … request API") is superseded by the table above. Its other bullets still stand.
Audit of 2026-09-17
Inspected on 2026-09-17 at f258fc3 (main, merge of SPEC-001 / PR #4). Initial working tree was clean.
Scope: all tracked source, tests, manifests, build/test/compiler configuration, CI, Changesets,
root documentation and relevant history; dependency resolution inspected in pnpm-lock.yaml.
Documentation, packed-consumer fixtures and qualification tooling now exist. No production package
files changed between 9d2635e and this checkout.
History
| Commit | Actual increment |
|---|---|
fb5038c | OSS workspace/tooling foundation |
80341ea, merged by 61d0959 | Core controller/GET metadata; TypeScript decorator pre-transform |
b899c59, merged by 9d2635e | Real H3 v2 adapter and integration tests |
1929957, merged by a7fe88c | Architecture/SDD documentation and SPEC-001 |
f865fca, merged by f258fc3 | SPEC-001 tooling, executed evidence and consumer CI invocation |
The next slice must not recreate H3 execution or packed-consumer tooling: both already exist.
Implemented contracts
| Area | Evidence and limitations |
|---|---|
| Core exports | Public barrel: Controller, Get, Post, Put, Patch, Delete, getControllerDefinition, definition types, HttpMethod and STRATA_VERSION |
| Metadata | Standard decorator context.metadata, private symbol keys, guarded Symbol.metadata initialization; frozen route records, definition and route array. Own-property access only: no implicit controller/route inheritance. No H3 or Angular dependency. |
| Paths | Missing/empty paths become /; leading slash added, repeated slashes collapsed, trailing slash removed. HttpMethod was only "GET" at this audit; see the gaps section for the current set. |
| Adapter exports | Public barrel: registerControllers, ControllerClass, StrataH3ConfigurationError |
| Execution | Reads core's public definition, joins paths, calls app.on, returns the original H3 instance. Native H3 routes coexist. Sync/async results pass to H3. |
| Lifecycle | One no-argument constructor invocation per controller entry per registration call; instance reused by its routes and requests. Methods receive no request argument. No DI or request scope. |
| Configuration errors | Missing controller metadata and non-callable handlers throw during registration. This is not an atomic registration transaction. |
| Runtime dependency | Manifest h3: ^2.0.1-rc.1; lockfile resolves 2.0.1-rc.32. H3 and core are externalized in adapter build. |
| Distribution | ESM-only, dist exports, declarations/maps. Changesets versions: core/analog/h3 0.1.0. Only core and analog are published, by the manual publish-packages.yml under next, first publish pending (ADR-004); h3 is excluded. pnpm test:package-consumer qualifies the packed tarballs with TypeScript 5.9.2. |
The dynamic-route test proves matching /users/123; its handler returns a fixed object. It does
not prove parameter extraction. No request, body, query or header API is implemented.
Toolchain and checks
Repository pins pnpm 10.30.1, TypeScript 6.0.3, Vite 8.3.0, Vitest 5.0.1; lockfile resolves
Rolldown 1.2.8. Manifests declare Node >=22; CI selects Node 22 and runs lint, typecheck, tests
and build, then pnpm test:consumer. That configuration does not certify all Node versions.
The merge CI run
failed at lint (23 unresolved-type errors in adapter source/tests); later steps, including consumer
qualification, were skipped. Public core exports target dist, which a fresh checkout has not built
before lint. This is a separate workspace bootstrap issue, not remote reproduction of AC2. Local
pnpm lint passes with existing build artifacts. SPEC-002 must make the qualification reachable
from a clean checkout without disabling rules or aliasing package source.
Both packages contain a private vite-plugin-standard-decorators.ts. It invokes TypeScript
transpileModule with ES2023/ESNext before Vite, excludes dependencies and declaration files,
and only matches IDs ending in .ts. It is used by their Vite/Vitest configurations and is not
exported or shipped as a consumer tool. transpileModule does not replace type checking.
Earlier pre-SPEC-001 local check: pnpm exec vitest run on Node 22.23.0 passed 34 tests in 6 files
(22 core tests, 12 adapter tests). Existing installed dependencies and built core output were used;
this was not a clean build, tarball installation or deployment test. No production source was
changed to obtain this result.
SPEC-001: executed, runtime proven, strict type qualification blocked
The recorded run used baseline a7fe88c, Node 22.23.0,
pnpm 10.30.1, TypeScript 6.0.3, Vite 8.3.0 and H3 2.0.1-rc.32. Its implementation was merged in
PR #4. Review confirms:
tests/consumer/fixtureandtools/consumerbuild/pack both packages, install them outside the workspace and use consumer-local tools and public imports.workspace:*becomes0.0.0; the fixture's local-tarball override yields one core copy. This does not prove registry publication.- Consumer-authored standard decorators compile through
tsc; emitted JS runs in plain Node. Vite SSR bundling of that JS also runs. Both exercise real in-process H3 requests: sync/async controller routes, instance binding and a native route. Public metadata and preservation of an existingSymbol.metadatapass. No listening server or deployment was tested. - Strict consumer typecheck exits 2: H3's
HTTPError.isErrorusesstatic overridewithout the ambientError.isErrorin the fixed lib set (TS4113); H3's root declarations import optionalcrosswstypes even for HTTP-only consumers (TS2307). No diagnostic in this run originates in Strata declarations. Strata nevertheless owns qualification of its exposed dependency contract. - JS was emitted because
noEmitOnErrordefaults to false. Runtime success does not make the typecheck clean. The consumer does not inherit the workspace'sskipLibCheck: true. - Direct stock Vite builds but retains standard decorator syntax; Node rejects it. The reference
tsc → JS → Vite → Nodepath works. This proves a consumer compilation requirement on the measured tuple, not a requirement to publish or reuse Strata's private plugin. - The report records AC1/AC3–AC8 Pass and AC2 Blocked. Those are stage-level observations, not acceptance of the complete spec. Remote execution and exact transitive pinning need correction.
Review found the runner reports workspace Rolldown 1.2.8 but does not pin/measure the external consumer's Rolldown. The retained installation and its npm lock resolve 1.2.9. Runtime results remain useful; an exact 1.2.8 consumer claim is unproven. Version assertions and failure ownership must come from the measured graph/diagnostics, not the runner's hard-coded explanations.
Read-only rechecks in this audit reproduced both diagnostics with consumer-local TS 6.0.3
(--noEmit --skipLibCheck false). Adding only esnext.error removed TS4113 and left TS2307.
Node 22.23.0 reports typeof Error.isError === "undefined": that lib supplies types, not a runtime
polyfill. Registry/source research and alternatives are in
H3 consumer type compatibility.
M1: Blocked, partially proven. SPEC-001 remains Blocked. SPEC-002 has now been implemented and executed (below) with a measured no-go; a clean type contract and reproducible remote qualification are still required. No M2 work is authorized.
SPEC-002: executed, no eligible candidate found (upstream-blocked)
Implemented and executed against 497a23b (main, TypeScript-first tooling cleanup; no packages/
drift from f258fc3). Full measured evidence, regenerated by every pnpm test:consumer:types run:
docs/research/consumer-type-closure.md. Detailed AC-level
evidence is recorded in SPEC-002's Implementation result.
- A reproduces both historical diagnostics (TS4113 on
HTTPError.isError, TS2307 oncrossws) exactly, on the H3-only reproduction and the packed Strata consumer alike. - B (
esnext.error) removes TS4113 only; TS2307 remains. Confirms the lib correction alone is insufficient, as predicted. - C (
crossws@0.4.12declared explicitly) does not close the graph: crossws's own root declarations unconditionally importbunandcloudflare:workers/@cloudflare/workers-typesfrom internal chunk files, producing new TS2307/TS2552 diagnostics. Declaring the peer makes the measured diagnostic count worse, not better. - D-node-only (Bun/Workers type packages installed but not activated in
compilerOptions.types) still fails: crossws's own module resolution for the bare"bun"import pulls inbun-typesregardless oftypesfiltering, conflicting with Node's own declarations;cloudflare:workersremains unresolved since that ambient module needs explicittypesactivation. This isolates the actual mechanism (ordinary module resolution, nottypesinclusion) behind the leak. - D-full-providers (
types: ["node","bun","@cloudflare/workers-types"]) is disqualified by SPEC-002's stop rule regardless of outcome, and independently fails with 125 diagnostics — mostly@cloudflare/workers-typesredeclaring DOM/Node globals that conflict with the existing libs. A genuine double no-go, not merely a policy rejection of an otherwise-clean configuration. - Every diagnostic across all cases traces to
h3,crossws,bun-types/@types/bun, or@cloudflare/workers-typesdeclaration files. Zero diagnostics originated in@strata-sc/coreor@strata-sc/h3in any case — reconfirming SPEC-001's finding that Strata's own declarations are clean. - Negative ambient controls confirm Bun/Workers globals are correctly unresolved under Node-only
typesand correctly resolved once activated, independent of any H3 diagnostic. - The CI clean-checkout lint failure recorded above is fixed by build ordering alone: removing
packages/*/distand runningpnpm lintreproduces the exact 23 unresolved-type errors from the merge CI run; runningpnpm buildfirst resolves it..github/workflows/ci.ymlnow builds before lint/typecheck and runspnpm test:consumer:typesbefore the existingpnpm test:consumergate.
Verdict: upstream-blocked. No candidate satisfies SPEC-002's promotion rules. pnpm test:consumer
(SPEC-001's required gate) is unmodified and remains failing on AC2, as required; no promotion was
applied to its fixture. SPEC-001/M1 remain Blocked. Astra owns acceptance of this disposition and any
further M1 direction (upstream issue, alternate closure hypothesis, or continued block).
Gaps and review findings
- No Angular/Analog/Nitro dependency, DI integration, request API, validation, guards/interceptors, logging contract, component compiler, navigation protocol or deployment fixture.
- Packed-consumer runtime tests exist; strict installed-package types are blocked. No browser/e2e tests, browser bundle assertions or Cloudflare execution.
- Resolved: metadata is class-local (own-property reads/writes). An undecorated subclass has no definition; a decorated subclass gets only its own routes; siblings, parents and overrides stay isolated. Every HTTP route decorator rejects static, private and symbol-named methods at class definition. Covered by core, Analog and packed-consumer (TypeScript 5.9.2) tests. Explicit controller inheritance is not supported.
- Resolved (Analog):
registerControllers()preflights the whole batch before anyrouter.add(), so a configuration error registers nothing and reserves nothing. A second Strata route with the same method + final path on one router fails, within a call or across calls; non-overlapping repeated calls and separate routers are fine. Ownership lives in an adapter-localWeakMapper router. - Open: a
router.add()failure mid-commit propagates and cannot be rolled back (earlier routes stay); duplicates against native Nitro routes are not detected;@strata-sc/h3keeps the old per-controller registration; constructor failures are not specified. - Resolved (Core + Analog):
HttpMethodis"GET" | "POST" | "PUT" | "PATCH" | "DELETE", with publicGet,Post,Put,PatchandDeletedecorators built on one private core primitive (same path normalization, class-local metadata, declaration order, frozen records and member restrictions; errors name the decorator used).@strata-sc/analogmaps every method exhaustively to Nitro's router and runs all of them through the same planning, duplicate check (method + final path) and request invocation path. Verified on H3 v1 unit tests, the Analog fixture (dev and production), Wrangler/workerd (pnpm test:server-components:cloudflare) and the packed TypeScript 5.9.2 consumer. The private@strata-sc/h3routes all five methods on H3 v2 (internal tests only).HEAD,OPTIONSand other methods have no decorator. - Resolved (Analog, workerd):
StrataAnalogRequestbody readers failed under Nitro's Cloudflare presets, which attach the payload to a non-iterable mock Node request (req.body). They now read an attached body first, like H3 v1'sreadRawBody; POST/PATCH JSON bodies are asserted under Wrangler. - Handler exceptions and
Responsepassthrough are tested (later releases); the stream lifetime has dedicated tests since 0.7.0, and client-disconnect cancellation remains NO-GO on the pinned adapters.
These remain inputs to M1/M3, not permission for Astra to implement fixes. SPEC-002 addresses the consumer type-contract blocker; it does not declare the remaining primitive contracts stable.