Repository navigation
fix(spec): register sys_package_version in CLOUD_PROVIDED_OBJECT_NAMES - #17214
Conversation
The curated cloud list carried sys_package and sys_package_installation but not the release-snapshot table between them, so the shipped sys_metadata.package_version_id lookup target was classed as a platform-prefixed name nothing registers. Add the one entry, pin it by name beside sys_license, and add the patch changeset. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 134 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 27b16955a2361c440efae489eac4948cecf7b2c1 && git checkout 27b16955a2361c440efae489eac4948cecf7b2c1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bccf311100cad7affccd6cbfcddbd81fe734d97d df37e65647f1100a615485a8850a6db549cc7afb && git checkout -B drift-repro bccf311100cad7affccd6cbfcddbd81fe734d97d && git merge --no-ff df37e65647f1100a615485a8850a6db549cc7afb
node scripts/docs-audit/affected-docs.mjs --json bccf311100cad7affccd6cbfcddbd81fe734d97d
|
…oud-provided-package-version
The clause-② declaration for this PR is `yes`: `CLOUD_PROVIDED_OBJECT_NAMES` is a closed accept set and this diff adds a member to it. A PR that declares clause ② must grade at least one package whose published source it moves at `minor` or above (`check-changeset-no-major.mjs`, level axis), and a purely additive widening of a published public surface takes at least `minor` by the `Check Changeset` "WHICH LEVEL" rule. `major` is refused by the same script's launch-window guard, so `minor` is the only legal grade. The changeset prose no longer frames the addition as restoring something the published header already promised — that reading was withdrawn: the header states why the list exists, not that this name is a member of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
…oud-provided-package-version
…CT_NAMES (objectstack-ai#18851) Fixes objectstack-ai#18309 Clause-②: yes (widening) Scoped to **item 1 of the card only**. Item 2 — grading `sys_tenant_database` under enforce-or-remove — is decision-shaped and is not touched here; `packages/spec/src/cloud/tenant.zod.ts` is unmodified. ## What changed `CLOUD_PROVIDED_OBJECT_NAMES` gains one member, `sys_environment_credential`, plus a named pin test and a `minor` changeset. Three files, 69 insertions, nothing removed and no other member moved — the same shape as PR objectstack-ai#17214, which added `sys_package_version`. ## The cloud half is the filer's reading, not a measurement taken here The registration chain — `objects/sys-environment-credential.object.ts` exported through `objects/index.ts`, listed in `tenantObjects` in `manifest.ts`, spread into `manifestService.register({ objects })` by `tenant-plugin.ts` — was read **in the cloud repository at `cb8ee7ff60`** and is carried here on its filer's name. It is not re-checkable from this repo, which is what the list's own header means by "they cannot be conformance-tested from this repo". Nothing in this PR re-took it, and the pin comment and changeset both say so in as many words. What IS measured here is the objectstack half: the contents of the list, the predicate built from it, and the ladder verdict that consumes it. ## Re-taken readings **Reference census, taken on this branch at `d18f7d78a8`** (the card's own count was taken 2026-09-15 and is unchanged): | query | files | |---|---| | `sys_environment_credential` in `*.object.ts` | **0** | | `sys_environment_credential` anywhere tracked | 11 (2 ADRs, 7 `CHANGELOG.md` rename records, and this PR's two spec files) | | `sys_package_version` anywhere tracked (positive control) | 34 | | `sys_definitely_not_real` anywhere tracked (negative control) | 0 | So **no shipped `*.object.ts` references the name**, the card's `priority:p3` grading stands on its stated reason, and nothing here promotes it. **Does the existing pin test redden?** No. `platform-object-names.test.ts` pins the list neither by length nor by full set: it loops `CLOUD_PROVIDED_OBJECT_NAMES` asserting each member resolves and is not declared in this repo — both of which a new member satisfies on arrival — and then pins `sys_license` and the `sys_package*` family **by name**. A member added without a named pin is therefore invisible to the suite, which is exactly the gap the file's own comments say the by-name pins exist to close, so this change adds one in the same style. Suite went 9 tests to 10, green. ## Controls Both legs rebuild `packages/spec` and prove the state reached `dist/` with `scripts/ablation-dist-preflight.mjs` before the reading is allowed to mean anything — `@objectstack/lint` resolves `@objectstack/spec/system` through `exports`, so a verdict taken without rebuilding is a fact about the previous build. Probe: an object declaration whose lookup field targets the name, through `validateObjectReferences`. | name | BEFORE (member ablated out of `dist`) | AFTER (`d18f7d78a8`) | |---|---|---| | **`sys_environment_credential`** (LIT) | `predicate=false`, **1 finding** — `warning:object-reference-unregistered-platform` | `predicate=true`, **0 findings** | | `sys_app`, `sys_environment`, `sys_environment_member`, `sys_license`, `sys_package`, `sys_package_installation`, `sys_package_version` (DARK-A) | `predicate=true`, 0 findings — each | `predicate=true`, 0 findings — each | | `sys_definitely_not_real` (DARK-B) | `predicate=false`, 1 finding | `predicate=false`, 1 finding | | `sys_approval_process` (instrument control) | `predicate=false`, 1 finding | `predicate=false`, 1 finding | | `sys_user` / `crm_deployment` (instrument controls) | 0 findings | 0 findings | The lit control is non-zero and every dark reading is unchanged. Preflight confirmed the marker absent from all 216 built files on the ablated leg and present in 8 on the restored leg; the restore was proven by whole-tree `git status --porcelain` (0 lines) and by `git hash-object` matching the HEAD blob `c10c88f1e0…`. No probe file was left in the repo. **One correction to the card's wording.** The card says "the first shipped reference to it would be refused". The measured verdict is a **`warning`**, not a refusal: rung ④ of `validate-object-references`'s severity ladder is advisory by design, because a third-party package may legitimately provide a platform-prefixed name. `defineStack`'s own hard throw uses a prefix test (`isPlatformObjectName` in `stack.zod.ts`), which accepted the name both before and after this change. So the flip is real and the direction the card describes is right, but what flipped is a diagnostic, not a rejection — the name was never hard-refused, and this PR does not manufacture a refusal to make the stronger reading true. ## The two `merge=os-regen` artefacts do not move `packages/spec/api-surface/system.json` and `packages/spec/export-origins/system.json` were pre-declared on the claim as regeneration targets. Measured: they record export **names and origins**, not values, so the list's contents are invisible to them — `git grep -c sys_package` over both directories returns 0 hits. The repo's own generator wrapper confirms it: `pnpm --filter @objectstack/spec check:generated` reports **all 15 generated artifacts up to date**, `check:api-surface` and `check:export-origins` among them, so `--fix` had nothing to regenerate. Neither file is hand-edited and neither is in this diff. PR objectstack-ai#17214 touched the same three-file set for the same reason. ## Verification Run at `d18f7d78a8` unless noted. - `pnpm --filter @objectstack/spec build` — green (through the shared verify lock) - `pnpm --filter @objectstack/spec test` — **487 files / 14055 tests passed** - `pnpm --filter @objectstack/spec typecheck` — green (incl. `check:scripts-typecheck`, `check:test-typecheck`) - `pnpm --filter @objectstack/lint test` — **104 files / 3879 passed, 5 skipped**, after building lint's dependency closure. The first run showed 41 files failing to collect on `@objectstack/formula` / `@objectstack/sdui-parser` having no `dist/`; that is unbuilt-closure state, not this diff, and it cleared on `pnpm --filter '@objectstack/lint^...' build`. - `pnpm --filter @objectstack/spec check:generated` — all 15 up to date - Gate families: `check:nul-bytes`, `check:merge-driver`, `check:cross-package-test-inputs`, `check:test-source-alias`, `check:published-files`, `check:dts-closure`, `check:type-check-coverage`, `check:entry-nameability`, `check:browser-reachable-entries`, `check:dual-source-exports`, `check:exported-any`, `check-empty-changeset`, `check-adr-0087-registration`, `check-platform-object-tenancy-census`, `check-spec-docblock-symbol-anchors`, `check-comment-mask-adoption`, `check-comment-mask-corpus`, `check-keyed-text-bounds` — all exit 0. - eslint, narrowed and declared: `eslint --no-inline-config` over the diff's two lintable files, **0 errors / 0 warnings**, file count read from `--format json` (2 — the third changed path is a `.changeset/*.md`, which no `files` entry in `eslint.config.mjs` matches). The narrowing is safe because this config runs **no type-aware linting** — `eslint.config.mjs:328` records "no `parserOptions.project`, no typed `@typescript-eslint` rules", and there is no `projectService` anywhere in it — so each file's verdict is a function of that file alone and a two-file diff cannot move an untouched file's verdict. The repo-wide run remains CI's. NOT MEASURED, with reasons: - `check:type-check-debt` exited **3**, which the script itself prints as `PREREQUISITE NOT MET` — it needs the whole-repo build closure that `lint.yml` builds before the step. Its own text: "This is NOT a pass and NOT a finding: nothing was measured." Recorded as unmeasured, not as red. - `check-changeset-no-major.mjs` ran green but reports its **level axis NOT APPLICABLE** locally: it reads the clause-② declaration from a `pull_request` payload, which a local run has not got. The axis gets its input once this PR exists. - `dispatch-gates --ran` reconciles 27 of 81 derived families run locally. The remainder are checker `--self-test` invocations and repo-wide scans that CI owns. ## Acceptance notes Observations from the surrounding code, filed nowhere and deliberately not fixed here: - `PLATFORM_OBJECTS_BY_PACKAGE` carries a per-package conformance scan that reddens on drift; `CLOUD_PROVIDED_OBJECT_NAMES` has no such half by construction, so every member it holds rests on a cross-repo reading with no local ratchet behind it. That is the documented design, not a defect, and it is why this PR adds a by-name pin rather than claiming conformance. Noted, not filed — no PR or person is queued against this file. - ADR-0007's inventory table lists `sys_environment_credential` as existing while the registry that decides whether a reference resolves said nothing registers it. The two now agree. Noted, not filed: an ADR stating a true fact about the cloud runtime is not a contract violation, and nothing in this repo consumed the ADR table as metadata. ## Ownership `needs:contract-review` is the PM seat's to place and the at-tier review is the PM seat's to run; this PR removes no label and flips no state. Left as a draft. --- _Generated by [Claude Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #16745
CLOUD_PROVIDED_OBJECT_NAMEScarriedsys_packageandsys_package_installation— the head and tail of the three-table package familypackages/spec/src/cloud/package.zod.tsdeclares — but notsys_package_version, the release-snapshot table between them, whose row schema this repository ships ascloud/package-version.zod.ts. Platform metadata that ships with the product references the name (sys_metadata.package_version_idis aField.lookup('sys_package_version', …)in@objectstack/metadata-core), so against the list's own stated purpose that shipped lookup target was being judged a platform-prefixed name nothing registers.This PR adds the one entry and pins it by name in the constant's test, beside the
sys_licensepin that landed for the same class of drift (#13842). Nothing underpackages/lintmoves: the reference ladder is correct, the list was short.Clause-②: yes
CLOUD_PROVIDED_OBJECT_NAMESis a closed accept set and this diff adds a member to it, so the lint ladder now accepts a name it previously warned on. That is a widening, and it takesyes.node scripts/pm/check-clause2-carriers.mjs --pair 17214fires C5/T2 atplatform-object-names.ts:156— "a new member of a closed set" — and the declaration now agrees with it.An earlier revision of this body declared
no, citing the list's header as contract text that already promised the name. Thedomain:specseat withdrew that reading: the header states why the list exists, not thatsys_package_versionis a member of it, and a purpose is not an enumeration. The carve-out it invoked — deleting a refusal the published text itself denies — therefore does not apply, and no machine-readable waiver exists for a prose argument in its place.needs:contract-reviewis hung on both carriers and is the review seat's to clear; this PR waits outside the queue until it is.--pairstill exits 4 on this head, and the reason is a carrier this PR cannot reach. C5 reads the declaration limb from the card's governing claim comment (cardDeclaration()over the card's comment thread), never from a PR body — the level axis incheck-changeset-no-major.mjsis the reader that takes the PR body, and that is the one this edit serves. Card #16745's claim comment still carries a line-startClause-②: no, so C5 reports the pair as illegible until the claiming seat re-declares it there. The checker's own header forbids anyone else from doing that on the seat's behalf — "the declaration IS the judgement" — so it is left, reported, and not written around.Premise re-derived on
origin/main, then re-derived again after the mergeorigin/main:git show origin/main:…/platform-object-names.tslistedsys_app,sys_environment,sys_environment_member,sys_license,sys_package,sys_package_installation— six members,sys_package_versionabsent, head and tail of the family present.git grep sys_package_version origin/main -- packages/metadata-core/src/objects/sys-metadata.object.ts→ line 76package_version_id: Field.lookup('sys_package_version', {still present.Both halves held; nothing had been landed since triage.
origin/mainwas merged in twice throughscripts/pm/os-regen-merge.sh— merge commits87751f84eand, this round,df37e6564— with no os-regen path needing a side taken and no generated artifact moving either time.Changeset —
@objectstack/specatminor, derived between a floor and a ceilingplatform-object-names.tsis not a.zod.ts, so it does not ship as source underfiles[]— butsrc/system/constants/index.tsre-exports it (export * from './platform-object-names'), so the constant ships compiled insidedist, which is infiles[]. Measured after the rebuild, with controls:sys_package_versionsys_licensedist/system/index.mjsdist/system/index.jsdist/index.mjsdist/browser/system/index.mjsapi-surface/system.jsonandexport-origins/system.jsonboth listCLOUD_PROVIDED_OBJECT_NAMES. So published behaviour moves ⇒ a changeset is owed andskip-changesetdoes not apply. The level was derived, not taken on anyone's word, and the floor and the ceiling meet at one value:pr-automation.yml'sCheck Changesetprose ("WHICH LEVEL", maintainer ruling 2026-09-04, batch [WIP] Add query enhancements and advanced validation features #35 on finding(changeset): two independent contract reviews read the repo's own history to opposite bumps for "add an exported symbol to a published index" #15294): "a purely additive widening of a published package's public surface (a new exported symbol on anindex, a new accepted key or value) takes at leastminor". A new accepted value is exactly what this is.check-changeset-no-major.mjs: a PR declaring clause ② "must grade AT LEAST ONE package whose published source it movesminoror above".@objectstack/specis the only package this diff moves, so it is that package.patchacross the board is verdictenforce, exit 1 — the red this grade change exists to prevent.majoris refused outright by the same script's launch-window guard, and noallow-majorlabel is on this PR..changeset/pre.jsonis absent, so the RC exemption that would stand the guard down is not in effect either.minoris therefore the only legal grade, and it is also the semantically right one: nothing is removed or narrowed, so no member's meaning changes for an existing consumer. No ADR-0087 disposition is owed — that gate reads breaking signals, which are amajorbump or a**BREAKING/BREAKING CHANGEmarker in the body, and this changeset carries neither.Locally the level axis reports
NOT APPLICABLE("nopull_requestpayload to read a declaration from") because the declaration it reads is PR-scoped; its real reading is CI'sCheck Changeseton this PR. The three changeset gates that DO have a local reading ondf37e6564all pass and print their own verdicts:Verification — final head
df37e6564Heavy runs went through
bash scripts/pm/os-verify-lock.shon slotissue-16745-resume.The full package suite, in one run, no
--projectfilter and no file list — so both vitest projects (localandrepo) are selected by the config, not by me:Re-taken on
df37e6564after this round's merge, ⛔ not carried over from the previous head.Proof the pin file was actually in that run — the reason this matters is that
platform-object-names.test.tsis one of the 28 entries invitest.repo-tests.json, so thelocalproject excludes it andpnpm testalone would never have run it. From the verbose reporter's own output, all nine cases of the file, tagged with the project that ran them:The last line is this PR's new case. The line above it is the pre-existing
sys_licensesibling, lit in the same run as the control.Ablation — the pin can fail, and fails for the right reason. Direction predicted before running: turns red. It was run on head
87751f84eand is ⛔ not re-run here, because the two files it exercises are byte-identical at the final head —platform-object-names.tshashesf691ad7939dac77330995e3d9db49e95182ad5dband its testb2a4cde719761206871181f1ff358753b2371ab9at BOTH87751f84eanddf37e6564, and this round's diff is one.changeset/*.md. Both legs ran under the lock, each proving its edit reached disk before the run was read:Exactly one of the nine cases moved, and it is the new one — the pin is load-bearing and precisely scoped. The restore is anchored at
HEAD, not a bare checkout, and is proven by hash equality plus an emptygit diff HEAD, not by an exit code.Other package-level runs, same lock acquisition, exit codes landed to disk before being read:
pnpm --filter @objectstack/spec build→BUILD_EXIT=0pnpm --filter @objectstack/spec typecheck(tsc + scripts tsconfig +check:test-typecheck) →TYPECHECK_EXIT=0. Both exit codes were echoed individually rather than read from the lock wrapper's batch verdict, which covers only the last part of a;-sequenced command and says so.pnpm --filter @objectstack/spec check:generated→ exit 0, andgit statusclean — no generated artifact moved. Consistent with the mechanism: nothing outsidepackages/specreadsCLOUD_PROVIDED_OBJECT_NAMES, andscripts/platform-object-tenancy-census.jsoncontains nosys_packagerow at all.@objectstack/specdeclares no@objectstack/*workspace dependency, sopnpm --filter '@objectstack/spec^...' buildis empty by construction; the package itself was built.Gate families ③/④ — derived mechanically, never from a hand-fed path list:
73 exited 0. Four exited 3 =
PREREQUISITE NOT MET, recorded as NOT MEASURED — neither green nor red — all four because they read built output for packages this worktree has not built, which CI builds fresh:@objectstack/lint check:doc-formula-expressions@objectstack/formulais not built"check:dual-build-cjs-loadscheck:lean-entry-closurepackages/objectql/dist/core.mjsabsentcheck:type-check-debtcheck:nul-bytesexited 0, and a separategrep -naPsweep for control characters over the three changed files found none. Repo-widepnpm lintis CI's run and was not attempted here.Not measured, by design of the contract
That
@objectstack/service-tenantactually registerssys_package_versionlives in thecloudrepository, which is not reachable from this session. The list's header says that half is owned there and cannot be conformance-tested from this repo. It is NOT MEASURED here — not verified, and not substituted with anything this repository can prove instead. Reported on the card as an out-of-scope finding for the seat to file cross-repo.验收备注
PLATFORM_PROVIDED_OBJECT_NAMES: 112 shipped*.object.tsfiles underpackages/+examples/, 135 reference targets, 109 of them platform-prefixed ⇒ 0 unregistered. Controls:sys_userappears as a target 61 times (lit),registry.has('sys_definitely_not_real')isfalse,registry.has('sys_package_version')istrue— the last also proving the dist read was the rebuilt one. This reproduces the card's measurement, which found exactly one finding in the whole universe, and brings it to zero.packages/spec/src/cloud/environment.zod.tsnamessys_environment_credentialas a control-plane table andtenant.zod.tsnames the deprecatedsys_tenant_database. Neither is declared here nor listed inCLOUD_PROVIDED_OBJECT_NAMES, and both are referenced by zero shipped*.object.tsfiles, so no false refusal reproduces today — which is what separates them from this card. Whether the cloud runtime registers them is the same unmeasurable half as above. 承接者: the seat filing the cross-repo card carries the question alongside.Generated by Claude Code
Generated by Claude Code