Skip to content

feat(platform-objects): declare sys_metadata_activation, the ADR-0126 §4 activation ledger - #12185

Merged
os-support-ai merged 3 commits into
mainfrom
claude/issue-12155-metadata-activation-ledger
Aug 25, 2026
Merged

feat(platform-objects): declare sys_metadata_activation, the ADR-0126 §4 activation ledger#12185
os-support-ai merged 3 commits into
mainfrom
claude/issue-12155-metadata-activation-ledger

Conversation

@claude

@claudeclaudeBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Closes#12155
Part of #12150 (Epic: ADR-0126 implementation, v17 line)

L1 of the ADR-0126 program: declare the activation ledger. Declaration only — the enable/disable actions that write it and the per-runtime consult points that read it are separate legs (L2/L3), and nothing in the tree reads the object yet.

The diff (5 files)

FileWhat
packages/platform-objects/src/system/sys-metadata-activation.object.tsnew — the object declaration
packages/platform-objects/src/system/sys-metadata-activation.object.test.tsnew — the pin test (15 assertions)
packages/platform-objects/src/system/index.tsexport + the dir docblock's roster
packages/spec/src/system/constants/platform-object-names.tsone name added to the platform-objects census group
.changeset/sys-metadata-activation-ledger.mdminor on @objectstack/platform-objects and @objectstack/spec

Zero packages/spec schema/contract surface, as the card requires — the ledger is an ordinary platform object, not a metadata type, so it carries no zod or protocol surface of its own (ADR-0126 §4).

The one packages/spec file touched is the mechanical platform-object name census every new platform object owes. That census is a curated set of real names rather than a sys_-prefix pattern — the whole point is that a cross-reference check can tell sys_user (real) from a fictional platform-prefixed name — and its module contract states the obligation outright: "adding an object to a platform package means adding its name here. The owning package's conformance test fails otherwise." Its user-visible effect is that isPlatformProvidedObjectName('sys_metadata_activation') now answers true, so lint stops reading a reference to the ledger as a typo.

This was missed in the first push and caught by CI (Test Core 2/6, platform-object-names.test.ts:115registry group "platform-objects" is out of date: expected 38 names, received 39). Recorded rather than quietly amended: my local gate derivation ran over the change set at the time, which touched no packages/spec path, so it never named the spec-owned conformance pin that scans packages/**. A gate owned by a package your diff does not touch is exactly the shape a path-derived local run cannot reach.

Schema — exactly ADR-0126 §4

metadata_type · name · package_id · organization_id (nullable, reserved — NULL on this entire line; no writer sets it in any leg here) · active, plus the primary key id that every data-plane sibling declares and the driver provisions unconditionally.

The designation columns an earlier ADR draft carried (replaced_by, cloned_from) are absent per amendment ruling 2 — there is no recorded linkage between a clone and its base, matching the landed #11513 posture. The pin test asserts the column set by equality, not membership, and additionally names both removed columns, so a reader who deletes the equality assertion still trips the named one.

Why the uniqueness is spelled unique: 'organization'

Row identity is (metadata_type, name, organization_id NULL-collapsed). That is declared as:

indexes: [{fields: ['metadata_type','name'],unique: 'organization'},]

Both obvious alternatives are wrong here, and the test pins against each:

'organization' is the arm that closes exactly that hole: the driver prepends COALESCE(organization_id, '__global__') at registration (ADR-0120 D3), which is the "NULL-collapsed" of the §4 sentence.

No lifecycle block, deliberately

The absent block is the back-compat record class. Unlike the telemetry siblings sys_flow_dispatch / sys_automation_run, a row here is durable configuration — reaping one would silently re-arm an artifact an administrator disabled. That would be a data-loss bug wearing a tuning knob's clothes, so the omission is pinned by the test rather than left to be "fixed" later.

Acceptance: "an empty ledger changes nothing"

Pinned as five separate facts rather than asserted in prose — no seed rows (no seeding key exists on the object schema), active defaults to true so absence-of-row and row-without-flag agree, no lifecycle (the Reaper does no work), no generic write door (apiMethods: ['get', 'list'], engine-owned per ADR-0103), and no shipped app or nav contribution names the object (so a stock boot's UI is unchanged).

Verification

Gate families derived mechanically from the real change set (scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), not recalled — and re-derived after the spec file joined the diff, which pulled 9 further families (check:liveness, check:empty-state, check:strictness-ledger, check:variant-docs, check:merge-driver, check:spec-parsed-alias, check:doc-formula-expressions, check-ci-filter-parity, check-dev-prereqs).

At the current HEAD e539438a — 19/19 green, each read from the gate's own verdict line with the exit code captured before any pipe: spec census test · platform-objects test · platform-objects typecheck · check:liveness · check:empty-state · check:strictness-ledger · check:variant-docs · check:merge-driver · check:spec-parsed-alias · check-ci-filter-parity · check:cross-package-test-inputs · check:changeset-gate-self-tests · check:objectui-changeset · check-adr-0087-registration · check-changeset-no-major · check-empty-changeset · check:published-files · check:type-check-coverage · check:nul-bytes.

The CI-named failure is fixed at source: platform-object-names.test.ts now reports Test Files 1 passed (1) · Tests 7 passed (7), including the very assertion that was red — "registers exactly the objects each package declares".

The sweep one commit earlier (99590074) additionally ran check:slot-lookup, check:test-source-alias, check:type-source-resolution, check-plugin-teardown-shape, release-rehearsal-clone --self-test, check:engine-double-contract, check:where-matcher, check:query-options-erasure, check:i18n and check:i18n-stale-fill — all green, and untouched by the one-line census edit.

Two gates are recorded as not measured rather than as verdicts, because each said so itself. check:i18n refuses without a built CLI ("PREREQUISITE NOT MET … Nothing was checked"); after building @objectstack/cli it renders a real verdict — check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys). check:dev-prereqs reports "The workspace is not built — 1 unmet precondition, not a list of problems", naming 12 packages whose dist/ this targeted build never produced; its remedy is a full pnpm build, which CI does. Neither is a defect in this diff, and neither is claimed as green.

check:i18n needed its prerequisite built first (@objectstack/cli); its first run reported "PREREQUISITE NOT MET — nothing was checked", which is not a verdict. After building the CLI it renders a real one: check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys). The new object is not in the extract config, so it emits nothing into the bundles and the bundle-ownership guard stays balanced.

Package suite:Test Files 31 passed (31) · Tests 490 passed (490). The new file was confirmed to actually execute — the package's test script carries --passWithNoTests, so a zero-match run would read green: Test Files 1 passed (1) · Tests 15 passed (15) under --reporter=verbose, with all 15 named.

Reverse-verification (the pin is not vacuous). The mutated module is imported relatively by the test, so vitest resolves it from source — no dependency exports hop and no dist, so no rebuild leg applies. Each mutation was confirmed on disk by an anchored count before its reading was accepted, and restore was trapped on EXIT INT TERM:

MutationOn-disk anchorResult
regrow replaced_byreplaced_by: Field 0 → 12 failed / 13 passed — the set-equality and the named-linkage assertion
downgrade to unique: truedeclared-index line 1 → 0, bare-true line → 11 failed / 14 passed — the scope-equality assertion
both restore legsanchors back to baseline15 passed (15)

Both matched the direction predicted before the run.

Premise note

The card names sys_metadata_history / sys_automation_run / sys_flow_dispatch as siblings to study "beside" — worth recording that on main those three no longer share a package: sys_metadata_history moved to @objectstack/metadata-core, and sys_automation_run / sys_flow_dispatch live in service-automation. packages/platform-objects/src/metadata/ is now a back-compat re-export shim with nothing declared in it. The deliverable location was unambiguous regardless (packages/platform-objects, per both the card and ADR-0126 §4), so this landed in src/system/ — the package's home for cross-cutting system objects — and the declaration idiom was matched against all three siblings plus the in-package sys_setting.

Generated by Claude Code


Generated by Claude Code

…on ledger
Implements ADR-0126 §4 (D2): one data-plane platform object for the
disable+clone family, declared beside its siblings so it needs zero
packages/spec surface.
Schema is exactly §4's five columns plus the primary key. The designation
columns an earlier draft carried (replaced_by, cloned_from) are absent per
amendment ruling 2 — there is no recorded linkage between a clone and its
base — and the pin test asserts the column set by equality so re-growing
the linkage is loud.
Row identity is (metadata_type, name, organization_id NULL-collapsed),
spelled as a declared index with unique: 'organization' (ADR-0120 D1). A
hand-written composite naming organization_id verbatim would be
NULL-distinct, and this line's organization_id is NULL on every row by
construction, so that spelling would enforce nothing (#5030). The
'organization' arm makes the driver prepend
COALESCE(organization_id, '__global__') at registration (ADR-0120 D3).
No lifecycle block on purpose: a row is durable configuration, not
telemetry, and reaping one would silently re-arm a disabled artifact.
No consumers in this leg — the enable/disable actions and the per-runtime
consult points are separate legs. An empty ledger changes nothing.
@github-actions

github-actionsBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec, touching 13 documentable anchor(s). ⚠️1 changed file(s) yielded no anchor (packages/platform-objects/src/system/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

52 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json c02fa2214049789405ff93316fdcae6e3df38ab3.

5 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/platform-objects/src/system/index.ts) — pages documenting those are invisible to this run
  • 1 cross-cutting symbol(s) contributed no route anchor: isSystem (5 routes)
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c02fa2214049789405ff93316fdcae6e3df38ab3packageMentionDocs.

Which tree this was computed on

This run read content/docs from 0d3f2c427031d6af1da8c600b73d6434a3ef97a4 — the merge of head 4ea01e5d9ae1b7e22500f9b01ca5620343d98245 into base c02fa2214049789405ff93316fdcae6e3df38ab3, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0d3f2c427031d6af1da8c600b73d6434a3ef97a4 && git checkout 0d3f2c427031d6af1da8c600b73d6434a3ef97a4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c02fa2214049789405ff93316fdcae6e3df38ab3 4ea01e5d9ae1b7e22500f9b01ca5620343d98245 && git checkout -B drift-repro c02fa2214049789405ff93316fdcae6e3df38ab3 && git merge --no-ff 4ea01e5d9ae1b7e22500f9b01ca5620343d98245
node scripts/docs-audit/affected-docs.mjs --json c02fa2214049789405ff93316fdcae6e3df38ab3

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs c02fa2214049789405ff93316fdcae6e3df38ab3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-support-aiClaude

Copy link
Copy Markdown
Collaborator

CI note: "Type Check · consumer gates" is red here, and it is red on the base too — the base commit 22c42c9b (#12062) closed ObjectStackClient.packages.update's any-return but left its entry in the shrink-only exported-any-returns ledger, so the client consumer gate fails on any tree that runs it. This PR's diff (packages/platform-objects only) does not touch that surface. Fix in flight: #12193 (one-entry ledger deletion). Once it lands on main, this branch gets the base merged in to re-run CI. — session session_01KWRU3s15AJz7PGW7a7wdCh


Generated by Claude Code

…me census
The platform-object name census is a curated set of REAL names rather than a
sys_-prefix pattern, so that a cross-reference check can tell a real platform
object from a fictional platform-prefixed one. Its module contract states the
obligation outright: adding an object to a platform package means adding its
name here, and the owning package's conformance pin fails otherwise.
Caught by CI (Test Core 2/6, platform-object-names.test.ts:115 — registry group
"platform-objects" is out of date: expected 38 names, received 39). The local
gate derivation could not reach it: the change set at the time touched no
packages/spec path, so nothing named the spec-owned pin that scans packages/**.
This is a one-name roster registration, not protocol or schema surface — the
ledger stays an ordinary platform object with no zod/contract surface, exactly
as ADR-0126 §4 requires. User-visible effect: isPlatformProvidedObjectName
now answers true for the name, so lint stops reading it as a typo. The
changeset gains @objectstack/spec accordingly.
…-metadata-activation-ledger
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KWRU3s15AJz7PGW7a7wdCh
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:systemsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

L1: sys_metadata_activation platform object — the activation ledger (ADR-0126 §4)

2 participants

@os-support-ai@claude