Repository navigation
fix(metadata-protocol,metadata): the legacy-organization read seam's loose ends: paged reads, the loader's count, the rollback's named refusal (ADR-0131 D6/D7) - #23778
Conversation
…ts own The _lock case asserted only lock and editable, which overlayLockLayerAt selects apart from the content; with findServedOverlayRow's filter alone removed, getMetaItem served the organization's body and every case stayed green. It now asserts the served item is not the organization's body. Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg Co-authored-by: Claude <noreply@anthropic.com>
…n rows out before the page is cut
auditMetaItem, listCommits, revertStoredPackage's limit: 1 probe and the
repository's list({ limit }) filtered the legacy organization rows after
limit, so a page they sat in came back short, and the probe answered 409
"never been published" when its one row was an organization's. When a paged
read's page holds a legacy row (or an offset could have counted one), it is
re-read with them excluded in the query itself. A migrated or fresh store
holds none, and its single read is unchanged (ADR-0131 D6/D7).
Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg
Co-authored-by: Claude <noreply@anthropic.com>
…out, as its reads do exists() answered true for an address only a legacy organization row held while load() answered null, and queryHistory's total counted rows no page serves. The count is re-taken with the legacy rows excluded in the query only when it is not zero and the table holds any; a migrated store's count is unchanged (ADR-0131 D6/D7). Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg Co-authored-by: Claude <noreply@anthropic.com>
…nization commit by name It read the commit through the environment-only seam, which leaves a legacy organization's commit out, and answered COMMIT_NOT_FOUND (404) for a commit that is there. revertCommit and rollbackToPackageCommit now read the commit through one helper, readRevertableCommit, so a legacy commit gets the same NOT_OVERRIDABLE refusal from both doors. A migrated store holds no legacy commit and answers as before (ADR-0131 D6/D7). Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg Co-authored-by: Claude <noreply@anthropic.com>
…ng every row has outgrown Every row that reaches these is the environment's since the family reads leave a legacy organization's rows out (ADR-0131 D6/D7), so they carried nothing: mergePackageAwareOverlay's rowsRead flag (the records' own stored place already says it), ServedOverlayRowCandidate.scope / organizationId, StoredRowPlace.organizationId, and the always-null orgId / itemOrgId locals in applyRemoteMetadataMutation, revertCommit, rollbackMetaItem and deleteMetaItem. Behaviour-neutral. Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg Co-authored-by: Claude <noreply@anthropic.com>
…lan-file; E1's refusal reads straight
The four sys_metadata-family ADR-0087 entries and metadata-lifecycle.mdx
named os migrate organization-ownership --apply without --plan-file, which
the ceremony requires (18.cli-organization-ownership-apply-plan-file-required).
They now name the plan run and the reviewed apply. os migrate apply's E1
refusal sentence ("which the organization-ownership ceremony owns that
drop") is made grammatical. Prose only: no entry id, category, version or
field the migration engine reads changes.
Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg Co-authored-by: Claude <noreply@anthropic.com>
… hold ObjectQL's findOne contract and are pinned The DatabaseLoader count pin's engine double opens findOne with assertEngineFindOnePredicate, and the engine-double ledger records the doubles both new pin files declare (check:engine-double-contract --write). Claude-Session: https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 4 package(s): 24 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 150 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 2b2e1def086b0ec6f67b96f198282ff2434f0656 && git checkout 2b2e1def086b0ec6f67b96f198282ff2434f0656
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4fe8b3def02ff9803a52c2fb8b97778d5bf45597 81aa285d588f9eb7e7a42209bb19c34a3d2cdc97 && git checkout -B drift-repro 4fe8b3def02ff9803a52c2fb8b97778d5bf45597 && git merge --no-ff 81aa285d588f9eb7e7a42209bb19c34a3d2cdc97
node scripts/docs-audit/affected-docs.mjs --json 4fe8b3def02ff9803a52c2fb8b97778d5bf45597
|
Contract reviewServed-tier: Read: card #23609 (body; claim 6204823952, dispatch 6204866593, os-dev-report 6205946408, seat ruling 6206299338), PR #23778 (body as edited by the seat, 14-file list, net diff against merge base ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
席位核对 ·
|
| Item | Reading |
|---|---|
| Shape | PR → main. The body opens Fixes #23609 (the seat's edit, after the item-2 ruling 6206299338), its only closing keyword. #23429 / PR #23469 are named without one. |
| Scope | 14 files (+564 / −126). Engine: protocol.ts (the legacy-organization read seam only: findEnvironmentRows, readRevertableCommit shared by revertCommit and rollbackToPackageCommit, and the inert plumbing), sys-metadata-repository.ts (list's limit only), database-loader.ts (_count only), two new pin files, and the engine-double ledger the gate wrote. Cross-lane, as declared on #6024, #18883 and #20163: one cli assertion, the cli E1 sentence, four spec entries' prose, and one docs line. That is the claim's surface (6204823952). saveMetaItem, the D9.9 calls and the draft address are untouched. No governed path (check-governed-merges.mjs --pr 23778: 0 of 14). 690 lines, under the 3,000-line threshold. |
| Items | 1, 3, 4, 5, 6 and 7 done, each measured on main first. Item 2 is measured (a 25 s stall on a v17-shape file against 97 ms migrated) and ruled C by the seat, left latent (6206299338). The seat read the engine diff: a paged read re-reads with id $nin only when the page held a legacy row or an offset could have counted one; _count re-counts only a non-zero count over a table that holds legacy rows; the rollback reads the commit through the one helper revertCommit already used. On a migrated or fresh store, every path takes the single original read. |
| Pins and controls | protocol.legacy-organization-read-seam.test.ts (16 cases) and database-loader.legacy-organization-count.test.ts (4 cases) run each fix on a legacy-shape store beside a migrated CONTROL, whose answer does not change. Item 1's new assertion is red under the filter-only ablation; the file was 7/7 green there before. Ablations for items 1, 3 (two legs), 4 and 5 each turn exactly their own cases red and restore clean. Item 6 is proved by an identical full suite with and without the commit (237 files, 20593 tests). |
| Contract review | The four packages/spec migration entries are the reviewFace hits: record 6206405366 at CONTRACT_REVIEW_TIER, PASS on this head. The rest of the diff is this table, the seat's review of record at TIER_DEFAULT. |
Clause-② |
no, in the claim, the PR body and the changeset. The answers change only on an unmigrated database (rollbackToPackageCommit's 404 becomes revertCommit's named 403; pages and counts agree with the environment-only reads). No accept set widens, and no export moves: the removed fields are module-private. patch for metadata-protocol, metadata, cli and spec. |
| Gates | The dev's derived union at 81aa285d: 123 derived, 119 run, 4 NOT MEASURED (shallow-clone and dist prerequisites; CI hosts those families). One real red on the first run (check:engine-double-contract) was fixed in 81aa285d. CI on this head: 48 check-runs, 41 success, 7 skipped, none failed. Build Docs ran for the docs line and is green. The skips are the path-filtered roster (Console Pin Gate, the Dogfood shards, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke) and the body-edit re-runs of Auto Label and Check PR Size, each of which also has a success run. |
Disposition:
open_questions[0](item 2): C, ruled in 6206299338. It is transitional code for a databaseos serverefuses, no caller reaches the path, and A's public engine accessor would widen the public surface for code meant to retire. A future reader of the family inside an ambient transaction, off the served boot, carries route A in its own card.out_of_scope_findings: both go to Acceptance notes, not filed (6206299338).queryHistory's page cut is latent like the card.listCommits'limithas no in-repo caller and no spec declaration.- The record's note: the bare
--applyspelling stands elsewhere (content/docs/permissions/*.mdx,sys-metadata*.object.tscomments,docs/qa). This is outside item 7's surface. It goes to Acceptance notes, not filed: the CLI refuses that spelling withplan-file-required, which names the missing flag. deviations: accepted. Model-free trailers per AGENTS.md; the PR left unsubscribed (the seat holds it); the checkout detached for the worktree; item 3's ablation legs run outside the verify lock; 106 gate results from042ba4c5, which differs from the head only in one test fake and the ledger; the exit codes read from vitest's ownTestsline.
Generated by Claude Code
Fixes #23609
Clause-②: no
Six of the card's seven items are closed here, one commit each, measured on
mainfirst. Item 2 is measured but stopped: its fix needs files outside the claimed surface (below). Every path matters only on an unmigrated (v17-shape) database, whichos serverefuses to boot untilos migrate organization-ownershiphas verified. Each fix acts only where legacy organization rows are present, and each pin runs beside a migrated CONTROL whose answer does not change.The items come from the PASS record on PR #23469 (#23429, C7b S2 + S5); neither is closed by this PR.
Items
0546039d).findServedOverlayRow's legacy filter gets its own pin: the_lockcase inmeta-stored-legacy-organization.integration.test.tsnow asserts that the serveditemis not the organization's body.main2906cd8. With only that filter removed (the rawfindOne, rebuilt, marker verified indist/), the file stayed 7/7 green.expected 'Org A locked' not to be 'Org A locked', 1 failed / 6 passed).protocol.publishPackageDraftsfor a package with one draft had not answered after a 25 s bound.outcome: published).txStoreandprivilegedReadDriverOptionsare private, andengine.executedoes not thread the ambient handle.legacyOrganizationRowsOf) needs a public ObjectQL accessor inpackages/objectql, outside the claim.trxCtxinstead goes throughpromoteDraftForPublish, its lock read and the repository'sget/promoteDraftdraft reads.repo.getalone keeps the hang, because the repository's own legacy read has the same gap. Those are outside the claimed surface, and next to the promote door's D9.9 call.open_questions.os serverefuses, and no caller reaches the path. A future reader of the family inside an ambient transaction off the served boot carries the fix (route A) in its own card. So this PR closes the card. (Body edit by thedomain:engineseat, 2026-10-16T01:58Z.)a6f23cd9). A paged family read leaves the legacy rows out before the page is cut. This coversauditMetaItem,listCommits,revertStoredPackage'slimit: 1probe (it answered 409 "never been published") and the repository'slist({ limit }).offsetcould have counted one, the read is repeated withid $ninof them in the query. Otherwise the single read is the answer, unchanged.list, and its 1 v17-shape case goes red. The control cases stay green in both.7a52179d).DatabaseLoader._countcounts what_findserves. Before,exists()answered true whereload()answered null, andqueryHistory'stotalcounted legacy rows.exists()is untouched.c39c8c8e).rollbackToPackageCommitanswers a legacy commit withrevertCommit's named refusal (NOT_OVERRIDABLE, 403, the same sentence) instead ofCOMMIT_NOT_FOUND(404).readRevertableCommit.revertCommit's behaviour is unchanged: the same read, refusal and text, moved.findEnvironmentRowwith its old 404, and only the v17-shape "one answer from both doors" case goes red.8052436a). The inert plumbing is removed:mergePackageAwareOverlay'srowsRead,ServedOverlayRowCandidate.scope/organizationId,StoredRowPlace.organizationId, and the always-nullorgId/itemOrgIdlocals.rowsReadwas a presence flag, not dead. It is dropped because every caller that passed it also sends each record'sstoredplace, and every caller that omitted it sends none.servedStoredRowreturnsundefinedover records with no place, so the merge's answer is identical.@objectstack/metadata-protocolsuite gives 237 files passed / 3 skipped, 20593 tests passed / 19 skipped, both with this commit and withprotocol.tsrestored to its parent (blobea9fa58d).StoredOverlayEntry.organizationIdand theorganizationIdparameters ofrestoreArtifactRegistryView/hydrateOverlayIntoRegistry. They are the An org-scoped metadata DELETE evicts the ENV-WIDE registry entry for every org —restoreArtifactRegistryViewis(type, name)-addressed and org-blind #6780 registry-scope API and outside the card's list.6bdaa0e0; changeset042ba4c5). Prose only.18.sys-metadata{,-audit,-commit,-history}-organization-column-retiredentries andmetadata-lifecycle.mdxname the ceremony's reviewed apply:--out PLAN, then--apply --plan-file PLAN.os migrate apply's E1 sentence now reads "a drop the organization-ownership ceremony owns, and the ceremony has not verified on this database". Its--jsonpayload is unchanged.pnpm --filter @objectstack/spec check:generatedreports all 14 artifacts up to date, so nothing checked in derives from the entries' prose.Verification (head
81aa285d)@objectstack/metadata-protocol: full suite 237 files passed / 3 skipped, 20593 tests passed / 19 skipped;typecheckexit 0. The new fileprotocol.legacy-organization-read-seam.test.tspasses 16/16.@objectstack/metadata: full suite 63 files, 892/892 passed;typecheckexit 0. The new filedatabase-loader.legacy-organization-count.test.tspasses 4/4.@objectstack/cli:--project unit: 297 files, 4377/4377 passed;--project integration:meta-stored-legacy-organization7/7, andapply-organization-ownership-gate+organization-ownership-platform-global-column-drop11/11 (on a rebuiltdist/);typecheckexit 0.@objectstack/spec:build+check:generatedexit 0 (14/14 up to date);typecheckexit 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, after the last commit): 123 derived;--rangives 119 run, 4 NOT MEASURED, 0 unrun (exit 0).042ba4c5. It found one real failure,check:engine-double-contract: the loader pin's fakefindOneskippedassertEngineFindOnePredicate, and the ledger lacked the new doubles.81aa285dfixes both.81aa285d, every family reading the two files that commit changed was re-run, plus the 8 the newscripts/path adds: 13/13 exit 0.check-plugin-teardown-shape --self-test(needs unshallow history),spec check:skill-examples(client-reactunbuilt),check:dual-build-cjs-loads(whole-workspace build) andcheck:i18n-coverage(connector-slackunbuilt).scripts/ablation-replace.mjs: anchor hit, blob moved, restore proved blob == HEAD andgit diff HEADempty. The CLI ablation (item 1) also passedablation-dist-preflighton both legs.Acceptance notes
DatabaseLoader.queryHistory's page read (_findwithlimit+offset) still filters after the cut, the item 3 shape on the loader. It is not in the claimed surface (_countonly), and it is latent like every item here. Carrier: none.listCommitspasseslimitwith noorderByand sorts afterwards, so itslimitpicks store-order rows, not the newest. This predates the card and is unrelated to the legacy filter.🤖 Generated with Claude Code
https://claude.ai/code/session_01TekAoE8QbLrxFVJsdNuRgg
Generated by Claude Code