docs(qa): invert four more activation-ledger read-backs to key-set assertions - #15359
Merged
hotlong merged 1 commit intoSep 4, 2026
Merged
Conversation
…sertions `sys_metadata_activation` dropped its reserved `organization_id` column before it ever shipped (#15024): the object declares `systemFields: { tenant: false }`, the opt-out that REMOVES the column rather than leaving it unwritten. PR #15251 re-grounded the two items #15154 named. Four siblings still read that column back and asserted it NULL — a VACUOUS leg, not merely a wrong value: once the column is gone, `row.organization_id ?? null` answers `null` either way, so the read passes rather than failing and a runner sees green for exactly the reason it should be red. Each is inverted into a KEY-SET assertion, the shape #15024 used on the dogfood pin and PR #15251 carried into step 2. - platform-core.activation-ledger-registration-home — step 2's row capture and the no-automation clause's "ONE install-level row (organization_id NULL)". The row is re-said as DEPLOYMENT-level: with no tenant column there is no second scope for install-level to be distinguished from. The schema-side probe stays with the sibling item that owns it. - api-backend.packaged-action-disabled-dispatch — the disable step's row confirmation, plus `automated.ref`, which described the dogfood pin as asserting "ONE install-level row with organization_id NULL". That pin has been a key-set reading named "writes ONE deployment-level row, and the TABLE has no tenant column at all" since #15024; the ref now quotes its live name. - api-backend.action-activation-door-contract — the happy-path step, its acceptance clause and `automated.ref`. The clause also glossed the column as "the per-org dimension is reserved and unwritten": ADR-0131 D7 WITHDREW that ADR-0126 §5 D3 bullet ("a reserved nullable tenant column is the shape D1 forbids") and names this table as reverted before 17.3 and not returning, so the gloss told a runner the tenant dimension was coming back. The source list now cites ADR-0131 D7 and marks which half of §5 survives. - automation.packaged-flow-disable-durable — the ledger read step, the row clause and its "§5 reserves the column" gloss, the verify line's NULL-collapsed 'organization' index (it is a plain `unique: 'global'` over two required key parts; with no tenant column `normalizeDeclaredIndex` has no part to prepend), and the `source` line still citing "org-carrying rows skipped on read" and "organization_id never written". The store citation splits so `#list` carries the read-path correction where the read path lives. Each item bumps `revision` and appends a `history` entry, per the checklist's change lifecycle. No item outside this defect class is touched, and no clause of the four contracts themselves moved — only what a runner records about the row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
hotlong
deleted the
claude/issue-15253-more-activation-ledger-checklist-items
branch
September 4, 2026 13:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15253
sys_metadata_activationdropped its reservedorganization_idcolumn before it evershipped (#15024, in 17.3.0): the object declares
systemFields: { tenant: false }— theopt-out that REMOVES the column rather than leaving it unwritten, since
resolveInjectedSystemColumnswould otherwise provision it by injection with no fielddeclared at all. PR #15251 re-grounded the two items #15154 named. Four siblings still read
that column back.
The population, verified here rather than taken from the card
The card named four items. I re-derived the population on
origin/main(c64e65f, whichalready carries PR #15251) instead of trusting that list, and the card's four are exactly
right — but the file-level counts in the hold comment are line counts, not occurrence
counts, so they do not add up against a per-item triage. Both numbers, per file, and the
item-level split:
organization_idaccess-security.jsonactivation-write-operator-gate, rev 2), two unrelated to the ledgerapi-backend.jsonautomation.jsonplatform-core.jsonactivation-ledger-row-contract, rev 2), one in scopestudio-authoring.jsonFOLLOW-UPS.mdTriaged item by item, eight items across the family spell
organization_id. Four are thecard's four. Two were repaired by PR #15251. The remaining two —
access-security.public-form-intake(6) andaccess-security.no-active-org-session-semantics(1) — are about ordinary tenant-stamped records and HTTP-created rows, not this ledger, and
are correct as they stand. Two more sites outside the four but inside the same wording class
are also fixed, both in items already in scope:
automated.refon eachapi-backenditemdescribed the dogfood pin with the old spelling.
So: four items, three files, no fifth item found, and nothing outside the defect class
touched.
Why this is not a value swap
These legs were vacuous, not merely wrong. They read
organization_idback and assertedit NULL. With the column gone, a value check of the shape
row.organization_id ?? nullanswers
nullwhether or not the column exists — the read passes instead of failing, soa runner sees green for exactly the reason it should be red. Every one is inverted into a
key-set assertion (the column is ABSENT from the row's keys), which is how #15024 fixed
the dogfood pin and how PR #15251 fixed step 2 of the row-contract item.
Two of the four were also wrong on the decision, not just the value. They glossed the
column as reserved and unwritten, citing ADR-0126 §5 D3 — a bullet ADR-0131 D7 withdrew
("a reserved nullable tenant column is the shape D1 forbids"), naming this table as reverted
before 17.3 and not returning. Left as they stood they told a runner the tenant dimension was
coming back. Both now cite ADR-0131 D7 and mark which half of §5 survives: the operator
gate stands, the reserved column does not.
What changed, per item
platform-core.activation-ledger-registration-home(rev 1 → 2) — step 2's row captureand the no-automation clause's "ONE install-level row (
organization_idNULL)". Re-said asDEPLOYMENT-level: with no tenant column there is no second scope for install-level to be
distinguished from. The schema-side probe stays with the sibling item that owns it
(
platform-core.activation-ledger-row-contract), cited rather than restated. ADR-0131 D7added to
source.api-backend.packaged-action-disabled-dispatch(rev 1 → 2) — the disable step's rowconfirmation, plus
automated.ref, which described the dogfood pin as asserting "ONEinstall-level row with
organization_idNULL". That pin has been a key-set reading namedwrites ONE deployment-level row, and the TABLE has no tenant column at allsince fix(platform-objects,core): sys_metadata_activation ships tenant-less — drop the reserved organization_id before 17.3 is cut (ADR-0126 amended by ADR-0131 D6/D7) #15024;the ref now quotes its live name, verified against
packages/qa/dogfood/test/packaged-activation-ledger-reach.dogfood.test.ts.api-backend.action-activation-door-contract(rev 1 → 2) — the happy-path step, itsacceptance clause (which carried the "reserved and unwritten" gloss) and
automated.ref("one org-NULL row").
sourcegains ADR-0131 D7 and an annotation on the ADR-0126 linesaying which half of §5 survives.
automation.packaged-flow-disable-durable(rev 1 → 2) — the ledger read step (whichlisted
organization_idamong the fields to capture), the row clause and its "§5 reservesthe column" gloss, the evidence line's "all five columns", the verify line's NULL-collapsed
'organization'index, and thesourceline still citing "org-carrying rows skipped onread" plus "
organization_idnever written". Grounded against source: the index is a plainunique: 'global'over two required key parts (with no tenant columnnormalizeDeclaredIndexhas no part to prepend, so there is no COALESCE arm to reach), andthe read-side skip is retired —
list()is scoped bymetadata_typeand by nothing else.The store citation splits into
#setActiveand#listso the read-path correction sitswhere the read path lives.
Each bumps
revisionand appends ahistoryentry, per the checklist's change lifecycle(
docs/qa/platform-checklist/README.md). No clause of the four contracts themselves moved —only what a runner records about the row.
Gates
pnpm check:platform-checklistis deliberately not in the lint workflow, so it was run byhand, before any edit and again on the final commit.
Same five, byte for byte —
batch_endpoints,crud_endpoints,metadata_endpoints,realtime_subscription,route_generation, allcoverage.json · UNCLASSIFIED. None added,none removed.
The card and the hold comment both name four (
batch_endpoints,crud_endpoints,metadata_endpoints,route_generation) and omitrealtime_subscription, which is presenton
origin/mainbefore this branch exists. Measured, not recalled.Symbol-anchor floor — non-shrinking, and not lowered.
scripts/checklist-symbol-anchor-baseline.jsonis untouched by this PR (its last commit isstill #15159). Census,
node scripts/check-platform-checklist.mjs --anchor-census:The one movement is upward: splitting the
automationitem's store citation addspackages/core/src/utils/metadata-activation-store.ts#list, and it resolves. Every otherfamily file is unchanged.
Derived gate family.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstacklet the script derive its own change set (3 paths vs merge base c64e65f) and named 10
runnable families. All 10 re-run on the final commit 41afe8c, all exit 0:
Exit codes captured by redirect-then-read, never across a pipe. One NOT MEASURED result
worth recording rather than hiding:
check:doc-formula-expressionsfirst answered exit 3 —PREREQUISITE NOT MET, twice in sequence (
@objectstack/formulaunbuilt, then@objectstack/lintunbuilt). Exit 3 is that gate's own "nothing was measured" code, distinctfrom a finding's 1; it is not a red and not a green. Building both packages
(
turbo run build --filter=...) turned it into a real measurement, and the verdict lines itthen printed are the ones cited above (58 self-test cases; 22 record-scoped examples across
427 files; 9 spec TSDoc examples; 14 field-level predicates).
Nothing else was NOT MEASURED.
check:platform-checklistis a real red-by-design onorigin/mainand stays at the identical five.Verify-lock declaration. The two builds went through
bash scripts/pm/os-verify-lock.sh -c '...', which reportedUNLOCKED (declared) · no usable flock on this host, so the shared verify lock was NEVER taken and NOTHING was serialized. Declaring it here as the script's own output instructs;neither build is a measurement this PR rests on.
Anti-vacuity ablation
A checklist edit's gate can go green by never reading the edited file. Driven against the one
anchor this change adds — the new
#listcitation inareas/automation.json— re-pointedat a symbol that does not exist:
The mutation is confirmed on disk by occurrence counts on both the removed and the injected
text and by a changed blob hash — never by the editor's exit code. The restore is proved
by
git hash-objectequalling the HEAD blob plus an emptygit diff HEADand a cleangit status, not by the restore command's exit code. The script carriedtrap restore EXIT INT TERMwith absolute paths resolved fromgit rev-parse --show-toplevel.existence. The mutation used
perl -0pi -ewith the em dash spelled\x{2014}inside a\Q...\Eblock, which quotes the escape's own backslash — soperlsearched for a literalthat is not in the file, exited 0, and left the blob hash unchanged. The on-disk
occurrence check caught it (
original=1 injected=0after the mutation) and the run abortedbefore reading a single gate number. Had the reading been taken from the exit code, this
ablation would have reported a healthy-looking mutation over an unmodified file. The anchor
was re-spelled in ASCII and the run above is the real one.
Scope
Three files, all under
docs/qa/platform-checklist/areas/. No source file, nothing undercontent/docs/releases/, no checklist item that does not carry this defect, andscripts/checklist-symbol-anchor-baseline.jsonuntouched.Changeset: none — docs/QA only, publishing nothing from any released package, so the
skip-changesetlabel applies. ⛔ Deliberately not an empty-frontmatter changeset (#4898).Not a governed surface: the diff is
docs/qa/**only, soGOVERNED_SURFACESis not hit.Auto-merge is not armed and the PR is not queued — the PM enqueues.
Refs: #15154 · PR #15251 · #15024 · ADR-0131 D7 · ADR-0126 §5
Generated by Claude Code