Repository navigation
fix(cli): under a wall, the ownership ceremony's rewrite gate names the act that clears a position binding, not a step that reports done - #23576
Conversation
…ding names the act the wall allows Under group/isolated no ceremony step carries binding rows, so the gate's pointer at authored-conversion named a step that reports done. The walled remedy now names the act by where the position's definition lives: declare an undeclared position with the bound sets through one door (the deployment's code, or the metadata door as the platform administrator); add the missing sets to an editable declared one through the metadata door; for a definition the door reports not editable (sealed, ADR-0131 D6), state the fact and name no act. The single-posture pointer and the uncomposed-plugin remedy are unchanged. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
…ble, sealed, conflicting, single control — and the served boot's sealed read The sealed branch names the package that ships the definition when its provenance names one. The served-catalog fixture now reads the metadata door's lock verdict as the command does, and declares two positions in the artifact: one whose declaration names the bound set (clears the gate) and one that lacks it (sealed, fact and no act). Changeset: @objectstack/cli patch, FROM -> TO per remedy sentence. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
…lled-binding-remedy
📓 Docs Drift Check3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 28 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 e1ebfa83dec261cd58036814744dab3a1b2ae035 && git checkout e1ebfa83dec261cd58036814744dab3a1b2ae035
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin fa6a6085c07df22274fa56bfc93141f75f50d25b 47cec88f799788a248f429dded7762dbb7bea40c && git checkout -B drift-repro fa6a6085c07df22274fa56bfc93141f75f50d25b && git merge --no-ff 47cec88f799788a248f429dded7762dbb7bea40c
node scripts/docs-audit/affected-docs.mjs --json fa6a6085c07df22274fa56bfc93141f75f50d25b |
席位复核: REWORK, patch round 1 (two items) · 2026-10-15T01:17Z
What holds (read on GitHub, not from the report):
Item 1: the undeclared remedy contradicts itself. The error is this seat's.
Item 2: one unit pin asserts an act the real door forbids.
Everything else is unchanged. Generated by Claude Code |
…code door contradicts; the uncomposed control pins only the pointer's absence Patch round 1 of the seat's review. 'Declare the position NAME in the environment catalog ... through the deployment's code' said two opposite things: the environment catalog is the environment-authored items, and a code declaration is a package declaration. The sentence now reads 'Declare the position NAME with permissionSets naming SETS, through one door only: ...', in the source and in the changeset's TO line. The uncomposed-block walled control no longer pins an act for everyone: its fake catalog declares no everyone, while a composed boot declares it sealed. It asserts only that the pointer and the composition are gone. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
席位核对✅ ACCEPT: PR #23576 at
Checklist, read against GitHub and
This seat's corrections, in public:
Accepted deviations:
Out-of-scope, Acceptance notes, 「承接者:无」:
Next in this family: #23555 (the 18.0.0-next.0 release text) can be written against this remedy once this lands. Landing: Generated by Claude Code |
Fixes #23542
Clause-②: no
What changes
Under
group/isolated, the rewrite gate ofos migrate organization-ownership --applyrefuses everyconvertedposition binding. That is a row binding a set the position's catalog definition does not name. The gate's remedy pointed atauthored-conversion, but under a wall that step reportsdonebefore it reads a binding. The gate still refuses, and nothing is deleted or guessed. Only the remedy changes. It now depends on where the position's definition lives, as the seat ruled in its review of the readings (comment 6180941823):permissionSetsnaming the bound sets, through ONE of two doors: the deployment's code (compiled into the artifact the ceremony boots), or the metadata door as the platform administrator (PUT /api/v1/meta/position/NAME). A cold boot refuses a name that both a package and the environment catalog hold.permissionSetsthrough the metadata door. The code door is not named, because the environment already holds the name.conflictingUnchanged:
authored-conversion's walled note.conflictingtext.The exact FROM → TO for each sentence is in
.changeset/23542-walled-binding-remedy.md(@objectstack/cli, patch).Text on the real served boot, from the walled case:
File surface (as claimed)
packages/cli/src/utils/organization-ownership-apply.ts:verifyReferences' binding half: the posture branch.walledBindingRemedy.ORGANIZATION_OWNERSHIP_MIGRATION_IDare untouched (PR feat(runtime,cli)!: os serve refuses a v17-shape database until the organization-ownership ceremony has verified it (ADR-0131 D10 item 5, C7b S5) #23461's lines). For that reason the helper reads metadata-core'sisCodeArtifactBodythrough a dynamic import.organization-ownership-apply.test.tsandorganization-ownership-apply.served-catalog.integration.test.ts..changeset/23542-walled-binding-remedy.md.convertPositionBindingRows, spec, and the released CHANGELOG entries (release text (18.0.0-next.0): "Existing junction rows are converted into definitions by the ADR-0131 upgrade ceremony" is false under a wall, where the ceremony converts no binding and its rewrite gate refuses until the operator declares the position #23555 remains open for those).Mechanism readings (the dispatch's hypotheses)
verifyReferencesgives one remedy whatever the posture. The walled branch sits after the uncomposed-plugin remedy and branches onposture !== 'single'.resolveName. Sealed: the metadata door's own item read,editable. The door derives it frompackagedBaseRefusal, which asksisSealedManagedItem(ADR-0131 D6). The apply already holds that read asMetadataLockPort.verdict, and the command wires it toprotocol.getMetaItem. Measured on the apply command's served boot (bootSchemaStackwith its four composition flags, at96918d65a1):zz_shipped, declared in the artifact, resolves. Its definition carries_packageIdcom.example.ownership-catalogand_provenancepackage. The door read answerseditable: falseandlock: no-overlay. A door save of it is refused: "provided by a managed package and is sealed against in-place edits".everyoneandorg_admin(plugin-security built-ins) read the same way:editable: false,lock: no-overlay, packagecom.objectstack.plugin-security. A save ofeveryoneis refused the same way.editable: truebecause it has no item, so the helper never asks the door about one.isSealedManagedItemdoc in protocol.ts). So the sealed text names the package and prescribes nothing, as ruled.sys_metadatatable). It is pinned at the unit layer with the door's verdict stubbed.Tests (head
cac34a830d, after merging origin/main7ffc57846a)pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 src/utils/organization-ownership-apply.test.ts src/utils/organization-ownership-apply.served-catalog.integration.test.ts: 2 files, 100 passed.zz_shippedgets the sealed fact with no act.pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 289 files, 4317 passed. The rest of the integration tier is declared to CI.pnpm --filter @objectstack/cli typecheck: tsc exit 0, andcheck:test-typecheckOK.tsc --listFiles -p tsconfig.jsonhas all 3 changed.tsfiles in the program.Ablation (head
96918d65a1)node scripts/ablation-replace.mjsreplaced the walled branch's condition withtrue, which routes every posture to the pointer. Anchor 1 → 0, blob50c2d1148b93→06372902d5dd, marker on disk 1.git diff HEADempty, marker 0.distis involved: both test files import the module by its relative source path.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatcac34a830d: 65 commands. All 65 exit 0.--ran: 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN.96918d65a1, the first pass hadcheck:dual-build-cjs-loadsandcheck:i18n-coverageexit 3 (PREREQUISITE NOT MET: no full build yet). Both exit 0 afterpnpm build.pnpm lint(eslint . --no-inline-config, whole repo) atcac34a830d: exit 0.Acceptance notes
editable: false,lock: no-overlay). The door's own refusal text says "Edit the source artifact and redeploy". The gate names the shipping package, so an operator can recognise their own app. It names no act, as ruled. Not filed.authored-conversionis checkpointed, a binding that arrived since gets "resolve its blocker, then re-run", but the checkpointed step does not run again. No v18 producer writes binding rows, because no runtime declares the object, so nothing reaches this. Noted, not filed. Carrier: none.Patch round 1 (head
47cec88f79; the seat's review, comment 6182144654)everyonenow pins only that the pointer is gone, beside the existing check that the composition is gone. It no longer pinsPUT /api/v1/meta/position/everyone. That act came from the undeclared branch: the unit fake catalog declares noeveryone, so the door's verdict is never asked. A composed boot declareseveryone, sealed.47cec88f79:pnpm --filter @objectstack/cli typecheck: exit 0.dispatch-gates --commands: the same 65 families, run under the lock after a full build. All 65 exit 0.--ran: 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN.git merge-tree --write-tree HEAD origin/main(cdc42eb495) is clean.Generated by Claude Code