Uh oh!
There was an error while loading. Please reload this page.
feat(spec): schema-free /meta-spelling entry + sideEffects: false with measured purity (#10096, #10031) - #10201
Conversation
…greement moves to a build-time gate (#10096) The /meta URL-spelling contract (META_URL_TO_SINGULAR, canonicalMetaUrlType, metaUrlSpellingRefusal, unrecognisedMetaTypeRefusal) gets its own entry whose module graph is two pure modules — no zod, no registry closure. The map is materialized at build time by gen:meta-url-spelling into src/meta-spelling/meta-url-data.generated.ts (merge-driver registered, check:generated GATED); check:meta-url-spelling re-derives the three-limb union per CI lap and is the build-time enforcement home of the former module-load assertMetaUrlSpellingsAgree() (maintainer ruling 2026-08-20: moved, never dropped). /shared keeps all four symbols as re-exports of the one declaration. Standing principle written into README.md and the root module doc. Measured: the one fold costs 1110 B min / 530 B gz via the new entry vs 578.4 KB min / 136.8 KB gz via /shared on origin/main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gcKVsiywU9CcS96S5t9qD
…on pure in emitted bundles (#10031) Module-scope evaluation purity measured, not assumed: per-entry fresh-process probe of every built entry (globalThis / process.env / console / listeners) — the only observation is zod core's own globalThis-keyed __zod_globalConfig / __zod_globalRegistry init (zod itself ships sideEffects:false); no spec module writes a registry or mutates a global at module scope, and the one former module-scope effect (the metadata-url-spelling agreement assertion) moved to the build-time gate in the previous commit. A tsup esbuild onLoad transform injects PURE annotations before lazySchema/strictObject/defineForm call sites at parse time (esbuild preserves input annotations but does not re-emit 'pure'-option marks — measured), so consumer bundlers can drop unreached schema consts; no source file is edited, keeping the two files held by sibling claims untouched. Measured: the root + /security one-predicate marginal falls from +261.5 KB min / +82.4 KB gz to +177.8 KB / +56.6 KB, identical in both graph directions, reproducing the card's delta shrinking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gcKVsiywU9CcS96S5t9qD
📓 Docs Drift CheckThis PR changes 1 package(s): 24 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 115 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9e7879f97ff071b39021b371e6022daa87cf633e && git checkout 9e7879f97ff071b39021b371e6022daa87cf633e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 04096f17e14dccd9c8c9f1e5e924e1fc4dd99398 a9be45a86f421a2a59c7d391217c60e64c80cf44 && git checkout -B drift-repro 04096f17e14dccd9c8c9f1e5e924e1fc4dd99398 && git merge --no-ff a9be45a86f421a2a59c7d391217c60e64c80cf44
node scripts/docs-audit/affected-docs.mjs --json 04096f17e14dccd9c8c9f1e5e924e1fc4dd99398
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#10096
Fixes#10031
Family landing per the maintainer ruling recorded 2026-08-20T09:17Z on #10096 (one authoritative copy; #10031's ruling comment defers to it). Two per-member commits so each member is independently verifiable. Unblocks objectui#5359.
What landed (per ruling clause)
@objectstack/spec/meta-spelling(@objectstack/spec/sharedcosts a consumer 60.1 KB gzipped to import one string fold — the /meta spelling contract has no fine-grained export #10096, commit 1) — the/meta/:typespelling contract:META_URL_TO_SINGULAR,canonicalMetaUrlType,metaUrlSpellingRefusal,unrecognisedMetaTypeRefusal. The map is materialized at build time bygen:meta-url-spellingintosrc/meta-spelling/meta-url-data.generated.ts(registered with the merge driver and thecheck:generatedGATED ledger), so the entry's module graph is two pure modules — zero imports, zero zod content indist/meta-spelling/index.mjs(verified by grep and by a fresh-process evaluation probe: PURE, 1 ms)./sharedkeeps all four symbols as re-exports of the one declaration (additive; reference-identity pinned in the test suite).objectui/scripts/check-spec-symbol-derivation.mjstreats any@objectstack/spec/*import as sanctioned derivation (isSpecModuleprefix match) and enumerates export names frompackage.jsonexportsentries carryingtypesconditions — the new entry has both conditions, so its symbols enter the guard's name set like every existing entry's (verified by reading the guard's resolution logic; objectui itself untouched).sideEffects: false+ PURE-annotated schema construction (spec: importing one function from a@objectstack/specsubpath pulls its whole module graph — measured +237 KB minified, nothing tree-shakes #10031, commit 2) — purity proven by measurement (below). The annotation is a tsup esbuild onLoad transform (lazySchema/strictObject/defineFormcall sites), not a source sweep — measured: esbuild preserves input annotations in output but does not re-emit marks made via itspureoption, so the annotation must exist at parse time; injecting at load keeps ~1700 call sites out of the diff and edits no source file, sopackages/spec/src/data/object.zod.tsandpackages/spec/src/automation/flow.zod.ts(held by in-flight sibling claims spec: widen the #8778 scope-pin annotation on the audit-stamping declaration to name the cloud#1395 ruling and its three sanctioned consumers #10110/bug(service-automation): a flow's declarederrorHandling.strategy: 'retry'silently stops applying once the run pauses — the resume path has no retry loop and the continuation carries no attempt state #9705) are untouched.assertMetaUrlSpellingsAgree()moved to a build-time check —check:meta-url-spellingre-derives the three-limb union from the livePLURAL_TO_SINGULAR+DEFAULT_METADATA_TYPE_REGISTRYon every CI lap (new step in lint.yml's required typecheck job) and fails on disagreement or staleness. Moved, not dropped. Both failure directions ablation-verified (mutation confirmed on disk by grep before each run, restore confirmed after): a mutated artifact fails with the staleness prescription; a mutatedPLURAL_TO_SINGULARentry fails with the assertion's own "One spelling may not name two types" message. The registry-quantified vitest invariants additionally pin the agreement and the generated data's freshness as attributable tests.packages/spec/README.md(new "Export surfaces" section) and thesrc/index.tsmodule doc, ruling text quoted verbatim, untranslated. Mechanizing it as a gate is filed unassigned as Mechanize the schema-free-export principle as a gate: a declared browser-reachable spec entry must link no zod in its module graph #10199.Measurements
esbuild
--bundle --minify --format=esm --platform=browser --external:fs; probes resolve the workspace spec through the exports map. Before = origin/main @cc21aad8e built in a pristine worktree; after = this branch @a9be45a86. (--external:fs:pg-connection-string, reached through the datasource schemas, requiresfs; a browser build shims it — that weight is already on record as #9771.) KB below = 1000 bytes.canonicalMetaUrlTypevia/shared, alonecanonicalMetaUrlTypevia/meta-spelling, alone/ui+/kernelbaseline graph/shared(marginal)/meta-spelling(marginal)import * as spec(root)postureEnforcesWallvia/security(marginal)/ui+postureEnforcesWallvia/security(marginal)postureEnforcesWallvia/security, alone@objectstack/spec/sharedcosts a consumer 60.1 KB gzipped to import one string fold — the /meta spelling contract has no fine-grained export #10096 verification: the new subpath costs ~1.1 KB minified / ~0.5 KB gz — the card's "few hundred bytes" — vs +246.9 KB min / +69.7 KB gz the same symbol cost through/sharedon the same baseline graph (the card measured +213.4/+60.1 on 17.0.0; the surface grew since).@objectstack/specsubpath pulls its whole module graph — measured +237 KB minified, nothing tree-shakes #10031 verification: the card's "+237.3 KB identical in both directions" reproduces at +261.5 KB on today's main, identical in both directions — and shrinks to +177.8 KB (−32%), still identical in both directions. The remaining residue (top-level chainedz.*constructions that callee-name PURE-marking cannot reach) is measured and filed as [finding] After sideEffects:false + PURE-marked lazySchema/strictObject/defineForm, a /security one-predicate import still retains ~174 KB minified — top-level z.* chain constructions are the unshakeable residue #10200.Module-scope purity measurement (#10031, ruling clause 2)
Method: per-entry fresh-process probe of the built
.mjs(diff ofObject.getOwnPropertyNames(globalThis),process.env, console output, process listeners) + a module-scope call-statement scan ofsrc/**.assertMetaUrlSpellingsAgree()inshared/metadata-url-spelling.ts— and this PR moves it to the build-time gate. (The*-type-assertions.tsprobes are compile-only files referenced by no entry.)__zod_globalConfig,__zod_globalRegistry— which are zod core's own globalThis-keyed initialization, not a spec module's write;contractsevaluates with zero observations. zod itself ships"sideEffects": false, so the library already declares that initialization droppable-when-unreachable.z.globalRegistry/z.config()/.register()call at module scope anywhere insrc/**;.meta()registrations sit inside deferredlazySchemafactories and are read back only through the same schema object.meta-spellingprobes PURE with zero observations.No impure spec module surfaced, so the ruling's hard-stop condition was not met and
sideEffects: false(not an allowlist) is the measured declaration.Verification at head (all quoted from each gate's own verdict line, run at a9be45a)
check:generatedaggregate: "✓ All 14 generated artifacts are up to date" — includingcheck:meta-url-spelling("35 spellings, 27 registry-declared types; manifest/derived agreement holds"),check:api-surface,check:export-origins,check:docs,check:authorable-surface,check:test-typecheck.pnpm --filter @objectstack/spec test: "Test Files 414 passed (414) · Tests 11043 passed (11043)" (full suite, includes the new identity/freshness pins).pnpm --filter @objectstack/spec typecheck: exit 0 (src + scripts + test programs).--re-measure("33 ledger entr(ies) re-measured … none above its recorded number"), engine-double-contract ("OK — 329 pinned"), where-matcher ("264 matcher(s) … correctly"), query-options-erasure ("ratchet holds") — all green.canonicalMetaUrlTypethrough the exports map undernodenextresolves the fresh.d.mts(reverse-verified: a wrong-type assignment goes red with TS2322).Fences respected
@objectstack/specships 18 self-contained entry bundles with no shared chunks, so a consumer mixing entry points gets differentConnectorSchema/defineConnectorobjects (measured) #8133's chunk-splitting rework untouched — per-entry self-contained bundling unchanged; the new entry is one more (tiny) self-contained bundle.object.zod.ts/flow.zod.tsuntouched (annotation applied at build load time, not in source).Out-of-scope findings filed: #10199 (mechanize the principle as a gate — the ruling's welcomed follow-up), #10200 (residual retention after PURE marking).
Generated by Claude Code