docs(qa): re-ground the activation-ledger checklist items on the tenantless sys_metadata_activation - #15251
Merged
hotlong merged 1 commit intoSep 4, 2026
Conversation
…ntless table `sys_metadata_activation` dropped its reserved `organization_id` column before it ever shipped (#15024): the object declares `systemFields: { tenant: false }`, the declared index is `unique: 'global'` over `(metadata_type, name)`, and `ObjectStoreMetadataActivationStore` no longer filters reads to NULL-organization rows or skips org-carrying ones. Three legs of `platform-core.activation-ledger-row-contract` were written around that column. - Step 2 asserted five declared columns with `organization_id` NULL on every row. It now asserts the column is ABSENT from the row's key set. The value spelling passes vacuously once the column is gone (`row.organization_id ?? null` is `null` either way), which is why the dogfood pin was inverted into a key-set assertion rather than carried forward. - Step 6 (uniqueness) still works and still refuses; only its stated mechanism was wrong. It now names the plain `unique: 'global'` over two required key parts, with ADR-0120 D3's COALESCE collapse recorded as history that is no longer reachable here. - Step 7 INSERTed a row carrying `organization_id`, so the leg could not be run at all. It is REPLACED by a no-tenant-column probe — `PRAGMA table_info` plus an INSERT naming the column, refused, bracketed by a control INSERT that lands without it — not retired, because ADR-0131 D7 makes "this ledger has no organization column" a load-bearing platform property. `access-security.activation-write-operator-gate` changes only its two ledger read-backs; the operator gate and its unit pins are untouched. ADR-0126 §5's operator-gate half stands, and the source citation now says so, so the next reader does not infer the gate moved with the withdrawn column bullet. Both items bump `revision` and append a `history` entry, per the checklist's change lifecycle. Fixes #15154 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hotlong
deleted the
claude/issue-15154-platform-checklist-activation-ledger
branch
September 4, 2026 08:33
os-zhuang
pushed a commit
that referenced
this pull request
Sep 4, 2026
…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>
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 #15154
sys_metadata_activationdropped its reservedorganization_idcolumn before it evershipped (#15024, PR #15155). Verified against
origin/mainrather than inferred from thecard:
sys-metadata-activation.object.tsdeclaressystemFields: { tenant: false }, thedeclared index is
{ fields: ['metadata_type', 'name'], unique: 'global' }, andObjectStoreMetadataActivationStore.list()is scoped bymetadata_typeand nothing else —no NULL-organization filter, no org-row skip. The premise holds.
platform-core.activation-ledger-row-contract(revision 1 → 2)organization_id, "NULL on every row"idand the audit family;organization_idabsent from the row key setunique: 'global'over two required key parts; D3's collapse kept as history that is no longer reachableorganization_id— impossible, no such columnStep 2 is a real strengthening, not a wording fix. The old spelling cannot be carried
forward at all: once the column is gone, a value check of the shape
row.organization_id ?? nullanswersnulleither way, so it passes while measuring nothing — green for exactlythe reason it should be red. The key-set assertion is the same inversion #15024 already
applied to the dogfood pin (
expect(Object.keys(row)).not.toContain('organization_id'),with an
arrayContaininganti-vacuity control beside it).Step 7 is replaced, not retired — the judgement call the card flagged, ruled by the PM
on dispatch. ADR-0131 D7 names
sys_metadata_activationas reverted before 17.3 and notreturning, which makes "this ledger has no organization column" a load-bearing platform
property; dropping the leg would lose coverage rather than retire an obsolete assertion.
The replacement stays inside the item's established idiom — the same direct-sqlite fixture
class steps 6 and 7 already required, no new checklist mechanism:
PRAGMA table_info(sys_metadata_activation)— the column list carriesmetadata_type/name/package_id/activebeside the driver'sidand the injected audit family,and nothing tenant-shaped;
organization_idis refused by sqlite (has no column named organization_id);refusal names the missing column rather than a malformed statement. The
(metadata_type, name)pair is fresh on purpose, so step 6's unique index cannot be whatanswered.
The negative entries follow the same correction. A resurrected tenant column is now worse
than the shape it replaced: the store no longer filters or skips on read (there is no column
to filter on), so an org-carrying row would be merged straight into the deployment-wide
answer. And a duplicate accepted by the index is no longer diagnosable as the #5030
NULL-distinct hole — that hole needed a nullable tenant column inside the key.
sourcegains anchors for#systemFields(the opt-out that removes the column ratherthan leaving it unwritten —
resolveInjectedSystemColumnswould provision it by injectionwith no field declared) and
#list, plus the ADR-0131 D7 citation. ADR-0126 §5's "the orgcolumn is reserved" bullet is marked withdrawn, and §5's operator-gate half is explicitly
recorded as standing.
access-security.activation-write-operator-gate(revision 1 → 2)Read-back detail only, as the card scoped it. Two sites (step 7 and the inert-gate clause's
verify) asked the runner to seeorganization_id NULL; they now read the key set, with thesame vacuity warning. The gate itself is untouched and its unit pins are unmodified — the
source citation now says so explicitly, so the next reader does not infer the gate moved with
the withdrawn column bullet.
Both items bump
revisionand append ahistoryentry, per the README's change lifecycle.Gates
pnpm check:platform-checklist(deliberately not inlint.yml— maintainer decision;run by hand). Baseline captured at
1bc3c092abefore any edit, re-run at96fc81042after: the output is byte-identical,
diffempty. The 4 pre-existingUNCLASSIFIEDcoverage problems (
batch_endpoints,crud_endpoints,metadata_endpoints,route_generation) are unchanged — this change adds none and removes none.Anti-vacuity for that green. Because a checklist edit's gate can go green by not being
read at all, one of the two anchors this change adds was re-pointed at a symbol that does
not exist, proven on disk by grep counts before and after, then measured: the gate went
4 problems → 5, naming
sys-metadata-activation.object.ts#thisSymbolDoesNotExistas anABSENT SYMBOL. Restored withgit checkout HEAD -- (absolute path)and proven byte-identical(
git hash-objectequals the HEAD blob3cb88d94;git diff HEADandgit status --porcelainboth empty; zero residual mutation markers).
Symbol-anchor floor (
scripts/checklist-symbol-anchor-baseline.json, shrink-never):areas/platform-core.json58 → 60 (two anchors added, the safe direction),areas/access-security.json44 → 44. No floor lowered; the baseline file is untouched.Derived family.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstacklet the script derive its own change set (2 committed paths vs merge base
1bc3c092a,three-dot). It reconciles to 10 runnable families, harvested with
--commands. All 10green at
96fc81042, exit codes captured before any pipe (cmd > file 2>&1; EXIT=$?):check-ci-filter-parity·check-closing-keyword-parity(+--self-test) ·check-comment-mask-corpus·check:doc-formula-expressions·check:cross-package-test-inputs·check:doc-authoring·check:nul-bytes·check:refd-timer-probe·check:watch-hint-literalOne NOT MEASURED, then measured.
check:doc-formula-expressionsfirst exited 3 —PREREQUISITE NOT MET, its own words: "Nothing was measured … It is NOT a finding." It needed@objectstack/formulaand then@objectstack/lintbuilt. Both built, gate re-run, exit 0.Recorded here rather than reported as a red, since exit 3 says nothing about the tree.
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 as the entry point instructs: the wall-clock numbers above are shared-box
readings, not quiet-machine promises.
Not run, deliberately: the repo-wide
pnpm lintsweep and the full test suite. This diffis two JSON data files under
docs/qa/; no package source, no test, no gate script changed —dispatch-gatesderived no test family for it, and CI runs the farm exactly once regardless.Changeset
None, and none is owed: the diff publishes nothing from any released package. The
skip-changesetlabel is applied on this PR (⛔ not an empty-frontmatter changeset — the gaterejects those).
Generated by Claude Code