Repository navigation
Commit fe98cc6
feat(objectql)!: positions, permission sets and capabilities hold one name per deployment — a second holder is refused at registration, naming both (#22197)
Fixes #22135
Clause-②: no
Executes the maintainer's ruling Q4 = A on #15196 (ruling record
6050490870): positions, permission sets and capabilities each hold one
name per deployment. A package registering a name that an installed
package, the environment catalog or a built-in already holds is refused,
and the error names both holders. ADR-0048 §3.4's coexistence stands for
every other metadata type.
The ADR-0048 §3.4 narrowing note was Tier H and rode its own PR, #22198,
now on `main`. This PR carries no `docs/adr/**` file.
## What changed
- **`packages/objectql/src/security-catalog-namespace.ts` (new).** The
rule in one place: the three types, the built-in names, the holder
vocabulary (`package` / `environment` / `built-in`), and the reader of a
manifest's declared names. It reads the same sources the engine's
registration seams read: the manifest's own `positions` / `permissions`
/ `capabilities` and each nested `plugins[]` entry's, arrays only. A
manifest-stage `permissions` grant block is never read as permission
sets.
- **`SchemaRegistry.installPackage` — the package door.** It refuses
ahead of every mutation, beside the namespace gate, so a refused package
leaves no record, no namespace ownership and no claim. Every conflict is
listed in one refusal. The package's claims are recorded after a
successful install and released by `uninstallPackage`. The claims are
what the door reads for names no registered item records, and
`installPackage` itself registers no items. Through
`ObjectQL.registerApp` (every boot and hot-install door), a package's
permission sets and capabilities are also registered items under the
package, and so are its positions since #22262 landed on `main`. There
the claims agree with the items. With the claims ablated, the
`registerApp` doors still refuse and the direct `installPackage` door
does not (Patch round 3), so the claims stay.
- **`SchemaRegistry.registerItem` — the item seam.** A package-bound
registration of a catalog type over a name another holder holds is
refused before anything is stamped or stored. Built-ins are not asked
here: the platform registers its own built-in positions at this seam,
under its own package id (the S2 stage, now on `main`), and that
registration is the built-in holder's own. For a built-in name the
environment holder is not asked either; see Patch round 2. A
registration with no package is the bare slot, which is what every
`sys_metadata` hydration and metadata write-through writes. It is never
judged: an environment save over a package-held name is outside the
ruling.
- **The envelope** reuses the namespace gate's shape and registered
code: `code: 'NAMESPACE_CONFLICT'` (`NAMESPACE_CONFLICT_CODE`, already
exported), `status: 422`, `httpStatus: 422`. The condition is the same
one, a name in a deployment-wide namespace already taken, and so is the
remedy: rename, or uninstall the other holder. The class
(`SecurityCatalogNameConflictError`) stays unexported, as the registry's
other refusal classes are. It is not the namespace gate's class, whose
message names a `manifest.namespace` and offers the
`OS_METADATA_COLLISION=warn` downgrade. Neither is true here, and
`collisionPolicy: 'warn'` does not downgrade this refusal (pinned).
- **No new error code; no `packages/spec` change.**
### Where the doors are, measured
The card names three doors. What this PR measured is that all three are
reached through ONE: `ObjectQL.registerApp` →
`SchemaRegistry.installPackage`, which every package registration hits
in the kernel's Phase 1, before any `start()`.
- `AppPlugin`'s security registrar (`registerInMemory`, the
`'app-plugin'` registrar) and the artifact door
(`MetadataPlugin._registerArtifactBodyCollections`, the
`'artifact-door'` registrar) both run in Phase 2.
- Neither runs for a package the engine has not installed:
`AppPlugin.init` registers every package of its bundle through the
`manifest` service first, a multi-package artifact package by package.
- So the producer-side fix is the package door, and
`packages/metadata/src/plugin.ts` and
`packages/runtime/src/app-plugin.ts` are unchanged. The runtime pins
boot both registrars' real compositions and see the boot refused before
either runs.
## Door table: base vs head
"Base" is the same tree with both gates ablated, at 1604e09 (rows 1,
2 and 6 were also measured on the untouched base 7ef50a4, with the
same answers). "Head" is 8ad6385. Boots go through
`@objectstack/verify`'s `bootStack`; the artifact rows go through
`createStandaloneStack`.
| Door | Base | Head |
|---|---|---|
| Boot, door-less (`new AppPlugin(stack)`): two stacks sharing a
position, a permission set and a capability name | boots. The by-name
read answers the position from the LAST stack (metadata-service slot)
and the set and the capability from the FIRST (registry order) | boot
refused: `422 NAMESPACE_CONFLICT`, 3 conflicts, second stack vs first
stack |
| Boot: an app declaring `everyone` / `manage_users` /
`admin_full_access` | boots. The by-name read of `admin_full_access`
answers the APP's set | refused. Holder `built-in` for the first two.
For `admin_full_access` the app registers before `plugin-security` in
`bootStack`, so the platform's registration is the one stopped, naming
the app |
| Artifact boot: two packages of one artifact sharing names; a package
declaring `everyone` | boots (runtime pins red under ablation) | refused
in Phase 1, before the artifact door registers anything |
| Hot install: post-boot `manifest.register` over a held name |
accepted, package record written | refused, no record |
| Hot install: `POST /api/v1/marketplace/install-local`, inline manifest
| `200`, installed | `422 PLUGIN_REGISTER_FAILED`, the route's own code,
with this refusal's message in `error.message`; no record |
| `POST /api/v1/packages` | `400`: the strict body refuses `positions`,
the retired `capabilities` and a flat `permissions` list | unchanged. No
catalog collection can arrive here |
| Environment catalog holds a permission set, then a package declaring
it is hot-installed | accepted | refused, holder `environment` |
| Same-package hot reload | accepted | accepted |
| Environment save over a package-held permission set (`PUT
/api/v1/meta/permission/NAME`, with or without `?package=`) — not
covered by the ruling | `403 NOT_OVERRIDABLE` (the packaged
permission-set lock) | unchanged |
| Environment save of a position over a package-held position name (`PUT
/api/v1/meta/position/NAME`) — not covered by the ruling | `200`, and
the saved position then answers the by-name read ahead of the package's
| unchanged by this PR. Since #22262 landed on `main`: `403
NOT_OVERRIDABLE` (see Acceptance notes) |
Named but not measured:
- **The artifact door's HMR reload**
(`MetadataPlugin._reloadAndAnnounce`). It re-registers into the metadata
service without `registerApp`, so a dev-loop edit giving a package a
held name is served until restart. The restart's boot refuses it.
- **`install-local`'s cloud-sourced install.** Its existing code
tolerates a register failure: it warns, persists the ledger entry, and
answers success. The next boot's rehydrate logs the refusal at `error`
and skips the package. That is code reading only (it needs a control
plane).
## In-repo collision census (M2)
**Instrument.** A tsx census over `examples/app-crm`,
`examples/app-showcase`, `examples/app-todo` and
`examples/app-multi-package`: each config's top level, its `packages[]`
bodies and its nested `plugins[]`. Against those it reads the built-ins:
`BUILTIN_IDENTITY_NAMES` + `AUDIENCE_ANCHOR_POSITIONS`,
`PLATFORM_CAPABILITY_NAMES`, and `plugin-security`'s
`securityDefaultPermissionSets`.
**Result at 1604e09:** 50 declarations — crm 3 positions / 2 sets;
showcase 10 / 9 / 2 capabilities; todo 0; multi-package 0; built-ins 6
positions / 10 capabilities / 8 sets. Names with more than one holder:
**0**. Same-holder repeats: 0.
**The guard, measured with the gate in place at 8ad6385:** `crm`,
`showcase` and `multi-package` boot through `bootStack`, and
`security-catalog-showcase.dogfood.test.ts` (3 postures) and
`multi-package-artifact.dogfood.test.ts` are green. `app-todo` declares
no catalog name. Deployed and marketplace packages are NOT MEASURED.
## The P1.2 pin, flipped
S1's shared-name pin is the `security catalog read — a name two packages
ship` describe in
`packages/objectql/src/protocol-boot-hydration-scoped.test.ts`. It added
no `P1.2` label, which is why a `git grep` misses it. It now pins the
ruled answer at the same seams:
- a second package registering the name is refused (envelope + both
holders), and every reader answers the one holder;
- an override the holder stored for itself answers for every caller;
- a position two packages declare is refused at the package door, so one
stack's declaration reaches the metadata service.
`core`'s `security-catalog.test.ts` pointed at a non-existent
`security-catalog-shared-name.test.ts`. It now names that describe and
the new door pins. `security-catalog.ts`'s module doc said the
shared-name answer was "pinned until it is ruled", and is rewritten to
the ruled answer. `engine-capability-provenance.test.ts` pinned two
packages' same-named capabilities coexisting, the exact behaviour the
ruling removes. It flips to the refusal.
## Tests (at 8ad6385)
- `@objectstack/objectql`: `registry-security-catalog-namespace.test.ts`
(new, 28 cases), `protocol-boot-hydration-scoped.test.ts`,
`engine-capability-provenance.test.ts`,
`registry-collision-order.test.ts` and
`registry-artifact-co-ownership.test.ts`: 5 files, 64 passed. Full
objectql suite before the merges: 382 files, 7553 tests. The one red was
the coexistence pin flipped above; it is green after the flip.
- `@objectstack/runtime`:
`standalone-stack-security-catalog-one-holder.test.ts` (new, 4) and
`standalone-stack-security-registrar.test.ts`: 2 files, 6 passed.
- `@objectstack/core` `security-catalog.test.ts`: 14 passed.
`@objectstack/plugin-security` `builtin-positions.boot.test.ts` +
`builtin-positions.test.ts` (S2's): 18 passed.
- dogfood: `security-catalog-showcase`, `multi-package-artifact`, plus a
local door probe that is not committed: 3 files, 44 passed.
- Downstream sweep before the merges, against the rebuilt `objectql`
dist: runtime 337 files / 5465, plugin-security 172 / 3663, rest 266 /
5120, verify 18 / 133, cloud-connection 41 / 505. All green.
- `typecheck` for objectql, core and runtime (with
`check:test-typecheck`): exit 0. No new test-typecheck debt.
## Ablation
Both gates were ablated together through `scripts/ablation-replace.mjs`,
which wraps the run and restores on exit:
- the package-door call became a `globalThis` marker write;
- the item-seam condition gained an always-false marker conjunct.
Both mutations landed on disk: anchor 1 → 0, blob `b96099a12688` →
`12c018d406ab`. `objectql` was rebuilt and `ablation-dist-preflight`
found both markers in `dist/`. The DTS step failed on the now-unused
private method, and the JS bundle the suites read was emitted.
- **objectql pins (read from `src`):** 22 failed / 21 passed of 43.
Every refusal pin went red, including both flipped P1.2 cases and the
flipped capability pin. The controls stayed green: same-package reload,
uninstall releases the name, the environment-registration carve-out,
non-catalog coexistence, the grant-block reader, and the platform's own
built-in registration.
- **runtime boot pins (read from `dist`):** 3 failed / 1 passed. The
control stayed green.
**Restore:** the blob is back to `b96099a12688` and `git diff HEAD` is
empty. After a rebuild, `ablation-dist-preflight --absent` is green for
both markers (dist and whole tree).
## Gates
`node scripts/pm/dispatch-gates.mjs --commands` derived 78 commands on
8ad6385; all 78 were run, and `--ran` reconciles 78/78 with exit
codes recorded. All 78 exited 0. On the pre-merge tree 053cc2e two
needed a prerequisite first: `check-engine-split-ratio` refused the
shallow clone (deepened with `git fetch --shallow-since=2026-07-03`),
and `check:dual-build-cjs-loads` answered PREREQUISITE NOT MET until
eight unrelated packages were built. On 8ad6385 both ran green with
the rest. CI's own lanes (Test Core shards, Temporal Conformance, the
Dogfood shards, Build Core, the workspace type-check) are declared to CI
and are NOT MEASURED here. After the run, `origin/main` moved 6 commits,
none of which touches a file in this PR.
## Acceptance notes
- **Environment save of a position over a package-held position name.**
Before #22262 it was accepted (`200`), and the saved position then won
the by-name read; permission sets were protected on the same door by the
packaged permission-set lock (`403`). Since #22262 landed on `main`, a
package's positions are registered items under the package, and the same
save answers `403 NOT_OVERRIDABLE` ("'position' is not allowOrgOverride
in the registry"), measured on f16fcd0 with `PUT
/api/v1/meta/position/shared_pos?package=w`. Outside this ruling either
way; this PR changes nothing there.
- **Cold boot vs the environment-catalog holder.** A package registers
through `ObjectQL.registerApp` in Phase 1, and `sys_metadata` hydrates
in Phase 2 (`ObjectQLPlugin.start`). A package added to a deployment
whose environment catalog already holds one of its names is therefore
NOT refused at cold boot: the env row hydrates over it, with the
registry's existing collision warning. It is refused on a hot install.
From the registry's seat, that arrival is indistinguishable from an
environment save over a package-held name, which the ruling leaves out.
The `CONTROL` case in `registry-security-catalog-namespace.test.ts` pins
that the bare slot is not judged. A plugin's own `start()` is different:
every plugin that depends on the engine starts after that hydration, so
a package-bound registration it makes at the item seam DOES meet the
environment holder, and is refused (holder `environment`). The exception
is a built-in name, which the platform declares there itself (Patch
round 2). A `git grep` for literal catalog-type `registerItem` calls in
production source finds one such registration: `plugin-security`'s
built-in positions. Carrier: #22307 (ruled A: the cold boot refuses too;
it lands separately).
- **Order and the platform's permission sets.** `plugin-security`
declares the platform's sets on its own manifest (configurable through
`defaultPermissionSets`), so they are package-held. When an app
registers before it, as `bootStack` composes, the platform's
registration is the one refused, naming the app. The boot fails either
way, and both holders are named.
- **`install-local` inline import** answers its own
`PLUGIN_REGISTER_FAILED` for any register refusal (this one and the
namespace gate's alike), so `error.code` does not carry
`NAMESPACE_CONFLICT` there. The refusal's text is in `error.message`.
Not changed here.
- **The ledger row comment for `NAMESPACE_CONFLICT`** in
`packages/spec/src/api/error-code-ledger.zod.ts` describes the
manifest-namespace condition only. The spelling, owner key and face are
unchanged, and the provenance gate is green. A one-line comment noting
the second condition is a spec-lane follow-up, not made here.
- **Files outside the engine lane:**
`packages/runtime/src/standalone-stack-security-catalog-one-holder.test.ts`
(new test; `runtime` is `domain:cli`'s package);
`scripts/adr-anchors/packages__objectql__src__security-catalog-namespace.ts.json`
(new ADR anchor); and, from patch round 1, five `domain:cli` dogfood
files: `packages/qa/dogfood/test/showcase-security.ts`,
`showcase-d7-default-profile.dogfood.test.ts`,
`authored-row-write-scope.dogfood.test.ts`,
`bulk-widener-probe.dogfood.test.ts` and
`owd-public-read-write-write-floor.dogfood.test.ts` (each: the
`SecurityPlugin` construction, with its comment and imports).
## Patch round 1 — the dogfood fixtures declared one permission set
twice
The Dogfood Regression Gate (all 3 shards) was red on 8ad6385. In
every failing boot, `plugin-security` registered a permission set that
the app package already held.
The fixtures handed an app-declared set to `SecurityPlugin`'s
`defaultPermissionSets`, which plugin-security declares on its own
manifest, while the app declared the same set too:
- `showcase_member_default`, through `showcaseAppDefaultSecurity()` and
the D7 test;
- `wscope_*`, `probe_widener` and `owdw_*`, in three fixtures.
Measured:
- `os serve` / `objectstack dev` never composes this. It hands the
plugin only the default's NAME (`appSecurityPluginOptions`), and the app
registers the set. A real `objectstack dev --fresh` boot of
`examples/app-showcase` came up with the gate in place: health 200.
- The two copies were the same definition: 101 of 101 leaves equal.
Fixed at the producer: each fixture declares the set once, as the app's,
and wires the default by name, as the CLI does. A runtime pin holds the
refused composition.
All three dogfood shards are green locally on 089b1c8 (74 + 74 + 74
files) and in CI.
## Patch round 2 — the platform's built-in positions met an environment
row at boot
The merge queue removed this PR (record 6056019838). `plugin-security`'s
`registerBuiltinPositions` was refused at the item seam: position
`org_admin`, incoming `com.objectstack.plugin-security`, holder
`environment`. That refusal failed `SecurityPlugin.start`, and with it
the boot. S2b's pins went red: `builtin-positions.boot.test.ts`, "a
stored definition under a built-in name" (3 postures), and
`bootstrap-declared-positions.test.ts`, "a stored definition shadowing a
built-in name is neither seeded nor restamped".
Measured on 19c86b7 (this branch with `main` merged, before the fix):
- **Boot order.** `SecurityPlugin` depends on the engine. So
`ObjectQLPlugin.start` hydrates `sys_metadata` into the bare slot BEFORE
`SecurityPlugin.start` declares the built-in positions. Through a real
door: with `OS_METADATA_WRITABLE=position`, `PUT
/api/v1/meta/position/org_admin` answered `200`, and the cold restart
failed ("Plugin com.objectstack.security failed to start", with this
refusal). Without that setting the save answers `403 NOT_OVERRIDABLE`.
- **Who registers.** `registerBuiltinPositions` registers exactly the
six static built-in names (`BUILTIN_IDENTITY_NAMES` +
`AUDIENCE_ANCHOR_POSITIONS`), under the platform's own package id. That
is the built-in holder declaring its own names, not a second holder.
- **What S2b needs.** The stored definition keeps answering first from
the bare slot (ADR-0005), and the platform's declaration sits beside it.
Fixed at the producer, the item seam in `SchemaRegistry.registerItem`:
for a built-in name, it no longer asks the environment holder. An
environment item under a built-in name exists only because an
environment save went over the platform's name, which is outside the
ruling. Unchanged:
- a second PACKAGE registering a built-in name at the item seam is
refused, in either order;
- the package door refuses a package declaring a built-in name (holder
`built-in`);
- for any other name, an environment item still refuses a package-bound
registration at the item seam (holder `environment`).
No same-definition exception, no `collisionPolicy` change, and S2b's
pins are untouched. `registry-security-catalog-namespace.test.ts` gained
three cases, one per behaviour above (the third is a `CONTROL`).
**Reverse verification.** The new condition was mutated through
`scripts/ablation-replace.mjs` to ask the environment holder again (blob
`c60d9bad21bc` → `eeca074e4f80`). `objectql` was rebuilt, and
`ablation-dist-preflight` found the marker in `dist/`. The queue's
signature came back: S2b's 3 boot postures and the
bootstrap-declared-positions case went red with
`SecurityCatalogNameConflictError` (`org_admin` held by the environment
catalog), and so did the new admit case. Restored: blob == HEAD, and
`git diff HEAD` is empty. After a rebuild, `ablation-dist-preflight
--absent` is green.
All suites, the three dogfood shards and the 82 derived gates were green
at ffa6d51, and so was CI.
## Patch round 3 — #22262 landed on `main` first
#22262 (squash 0b997ea) adds `positions` to the engine's
`METADATA_ARRAY_KEYS`, so `ObjectQL.registerApp` now registers a
package's positions under the package. This branch merged `main` at
fbcbcf1. The merge touched none of this PR's files, and
`registry.ts`'s logic is unchanged.
**Comments only.** Four comments this PR added said a package's
positions never reach the engine registry's item store. Each now reads
true on `main`: the `securityCatalogClaims` doc and the `installPackage`
comment in `registry.ts`, the declared-names reader's note in
`security-catalog-namespace.ts`, and the header of
`standalone-stack-security-catalog-one-holder.test.ts`. No behaviour
changed, so no reverse leg was re-run.
**Measured with #22262 in the tree** (f16fcd0, this branch with
`main` merged; through `bootStack`; local probes, not committed):
- A package's own positions arrive both as claims and as registry items
under the same package, and stay one holder. The showcase boots with 10
positions under `com.example.showcase` and 6 under
`com.objectstack.plugin-security`, and 10 claims. Re-registering the
showcase is not refused. A second package declaring `contributor` is
refused, holder `com.example.showcase`.
- The built-in case is unchanged. With environment saves under
`org_admin` and `everyone` (`OS_METADATA_WRITABLE=position`), the cold
restart boots, and both names resolve to the environment's saved
definitions.
- `PUT /api/v1/meta/position/shared_pos?package=w` over a package-held
position answers `403 NOT_OVERRIDABLE`.
- The door probes behind the table above answer as before: crm, showcase
and multi-package boot; the two-stack boot, the built-in names, the hot
install and `install-local` are refused, each naming both holders; the
same-package reload is accepted.
**The claims, ablated.** This was measured on a throwaway local merge of
#22262's head 7ed88a6, never pushed. All seven files #22262 landed
are byte-identical to that head's. The claim recording was replaced by a
no-op (`scripts/ablation-replace.mjs`), `objectql` was rebuilt, and the
marker was proven in `dist/`. The runtime boot pins stayed green (5 of
5): every `registerApp` door refuses through the registered items alone.
Two objectql pins went red: the P1.2 position case and the
`collisionPolicy: 'warn'` pin. Both reach the package door through a
direct `installPackage` call, which registers no items. So the claims
stay. Restored: blob == HEAD; after a rebuild, `ablation-dist-preflight
--absent` is green.
**Tests at f16fcd0**, all under `os-verify-lock`:
- `@objectstack/objectql`, whole suite: 383 files / 7572 passed.
- `@objectstack/plugin-security`, whole suite: 179 files / 3775 passed,
45 skipped.
- `@objectstack/runtime`, whole suite: 340 files / 5505 passed, 19
skipped.
- Dogfood, the CI split: shard 1/3, 74 files / 557 passed; 2/3, 74 files
/ 535 passed, 1 skipped; 3/3, 73 passed + 1 skipped files / 661 passed,
8 skipped.
- `typecheck` for `objectql` and `runtime` (`tsc --noEmit` +
`check:test-typecheck`): exit 0.
- Gates: `dispatch-gates --commands` derived 82 on f16fcd0. All 82
ran and exited 0, and `--ran` reconciles 82/82, 0 NOT MEASURED.
CI on f16fcd0: 32 checks success, including Dogfood Regression Gate
1/3 to 3/3 and Test Core 1/6 to 6/6. Three were skipped (Build Docs,
Console Pin Gate, Packed-tarball smoke).
## Patch round 4 — the changeset level, one comment, the cold-boot
carrier
The contract review on f16fcd0 (record 6061710772) failed two texts
and one missing carrier. The code stands; this round changes text only.
- **The changeset level.**
`.changeset/22135-security-catalog-one-holder.md` graded
`@objectstack/objectql` `minor`, on the premise that Changesets pre mode
was not yet on `main`. It is: `.changeset/pre.json` (mode `pre`, tag
`next`) landed with #22084 (a87d8be), an ancestor of this branch's
merge base. The changeset now grades `major`, and its BREAKING sentence
says the change ships as `major` on the v18 pre-release line. The
ADR-0087 marker and `Clause-②: no` stay. `pnpm changeset status`
resolves `@objectstack/objectql` to `18.0.0-next.0`.
- **The cold-boot carrier.** One sentence in the changeset states the
boundary: at cold boot, packages register before the environment catalog
loads from `sys_metadata`, so a package newly added over a
permission-set or position name the environment catalog already holds is
not refused at cold boot, and the registry's existing collision warning
fires. A hot install of the same package is refused. #22307 has since
been ruled A (the cold boot refuses too); see Patch round 5. Measured on
9e4ed5d with a local probe (not committed): the cold boot with the
package added came up with no refusal and two `[Registry] Collision`
warnings, one for the permission set and one for the position. The hot
install answered `422 NAMESPACE_CONFLICT`, both names held by
`environment`, and left no package record. A capability cannot be saved
in the environment (`403`, a code-only type), so the sentence names the
two types an environment can hold.
- **One comment.** The runtime pin's comment called the position one "no
registry slot holds". That stopped being true when #22262 put a
package's positions into the registry under the package. The comment now
says the refusal reports the position, the permission set and the
capability the first package holds. A sweep of this PR's added lines
finds no other sentence saying positions do not reach the registry.
- **The `start()`-time half** of the round-2 question is settled as A
(keep): the ruling names the environment catalog as a holder and does
not distinguish phase. No change.
**Checks at 9e4ed5d:**
- The changeset gates: `check-changeset-no-major` `--self-test` and
`--base`, exit 0 (pre mode, tag `next`, so the no-major guard stands
aside; given this PR's body, the level axis reads `Clause-②: no`);
`check-empty-changeset` `--self-test` and `--base`, exit 0;
`check-changeset-fixed`, exit 0; `check:adr-0087-registration`, exit 0
(1 declared-breaking changeset, carrying its ADR-0087 disposition);
`check:changeset-gate-self-tests`, exit 0.
- `pnpm --filter @objectstack/runtime exec vitest run
src/standalone-stack-security-catalog-one-holder.test.ts`: 1 file / 5
passed. `pnpm --filter @objectstack/runtime typecheck`: exit 0.
- Gates: `dispatch-gates --commands` for the two touched paths derived
61 commands. All 61 ran and exited 0, and `--ran` reconciles 61/61, 0
NOT MEASURED. `git merge-tree` against `origin/main` 4e4111c is
clean, so `main` was not merged.
## Patch round 5 — #22307 was ruled
The maintainer ruled #22307 A while round 4 ran: a cold boot is to
refuse too. That work lands in its own PR, not in this one. The
changeset's cold-boot sentence keeps its measured clauses and now ends
"a cold-boot refusal is ruled and tracked on #22307, which lands
separately", which is true on this PR's merge and stays true after
#22307 lands. Nothing else changed.
**Checks at 99fba80:** `check-changeset-no-major --base`, exit 0 (pre
mode, tag `next`; given this PR's body, the level axis reads `Clause-②:
no`); `check-empty-changeset --base`, exit 0;
`check:adr-0087-registration`, exit 0. `dispatch-gates --commands` for
the one touched path derived 20 commands; all 20 exited 0, and `--ran`
reconciles 20/20, 0 NOT MEASURED. `git merge-tree` against `origin/main`
4e4111c is clean, so `main` was not merged.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01EUBvqtauTDmHi2ZgY759p2)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 34dba5a commit fe98cc6
15 files changed
Lines changed: 1080 additions & 103 deletions
File tree
- .changeset
- packages
- core/src/security
- objectql/src
- qa/dogfood/test
- runtime/src
- scripts/adr-anchors
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
18 | 20 | | |
19 | 21 | | |
20 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
| 53 | + | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
| |||
Lines changed: 18 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
| |||
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
115 | | - | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
116 | 123 | | |
117 | 124 | | |
118 | | - | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
119 | 134 | | |
120 | 135 | | |
121 | | - | |
122 | | - | |
| 136 | + | |
123 | 137 | | |
124 | 138 | | |
125 | 139 | | |
| |||
0 commit comments