fix(metadata): close out the adr-0030-notification-event retirement residue - #18389
Conversation
… @objectstack/platform-objects The operator-visible half of that retirement — a fresh datastore attests two creation-attested migration ids, not three — is delivered by `attestFreshDatastore` in `packages/platform-objects/src/system/migration-flag.ts`, in a package that publishes (no `private: true`). The changeset named `@objectstack/metadata` and `@objectstack/spec` only, so the fixed group bumped platform-objects without giving its CHANGELOG the sentence an upgrading operator greps. One row added to the existing changeset; the body already states the behaviour (`attestFreshDatastore` no longer includes it) and is unchanged. Time-boxed on purpose: once a release consumes this changeset the row cannot be added retroactively, and `content/docs/releases/` is release-owned and is never the place to patch it up. Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check
What this run could not see
Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
CI triage — 11 red checks, one cause, and it is this PR's
The readingTaken from the job logs, ⛔ not from the check names and ⛔ not from the aggregate rollup — ⇒ every job dies at the install step, before any test body runs. That is why Attribution — ⛔ not the base, ⛔ not a flakeThe failure names What was asked forRegenerate
⏳ Item 1 (the changeset row, commit
Generated by Claude Code |
⛔ Correction to my own CI triage above — two defects, not one
The second defect
⭐ It is independent in both directions: it ⛔ does not clear when the lockfile is fixed, and the gate states it 「clears with no push and no re-run」 — a body edit fires it. ⇒ the two defects need two different acts. The declaration itself was already made by this seat at claim time ( ⛔ The remedy is the declaration, never the deletion — the gate names dropping the changeset or regrading the package as the wrong fix, and this seat endorses that: it would change what ships in order to quiet a gate. ⏳ Item 1's changeset row is the whole point of this card's deadline and ⛔ is not negotiable against a red check. What stands from the earlier commentThe lockfile diagnosis is unchanged and still explains every other red check on this head. Re-verified since:
Generated by Claude Code |
…dency
The retired `adr-0030-notification-event` migration runner was the only consumer.
Nothing under `packages/metadata/src` carries a `@objectstack/platform-objects`
specifier any more, so the manifest declared an edge the package no longer has.
Removed together, because all three exist only to serve that one dead edge:
- `package.json` — the `dependencies` entry
- `vitest.config.ts` — the `@objectstack/platform-objects/system` alias, whose
own comment cited the retired migration's receipt cases as its reason
- `tsconfig.json` — the matching `paths` mapping
`pnpm-lock.yaml` travels in this commit, regenerated with `pnpm install
--lockfile-only` rather than hand-edited. Its entire diff is the three lines of
the one removed edge under the `packages/metadata` importer; a manifest edit
without it fails `ERR_PNPM_OUTDATED_LOCKFILE` at every job's install step.
Dropping a published package's dependency is a public-surface change, so it was
established rather than assumed that nothing resolves it through this package:
- `@objectstack/metadata`'s built `dist/` (30 files, 10 declaration files)
contains ZERO occurrences of `platform-objects`, against a positive control
in which all nine other declared dependencies appear in 4-12 dist files
each. No runtime import and no type reference reaches it.
- Of the 23 workspace packages carrying a real `@objectstack/platform-objects`
module specifier, all 23 declare it themselves; zero resolve it
transitively. `check:undeclared-dep-imports` is the standing gate that keeps
that true for non-test `src/**`.
- Zero example apps reference it at all.
`@objectstack/platform-objects` publishes on its own, so a consumer that wants
it declares it directly — the supported spelling, unchanged.
Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3
Co-authored-by: Claude <noreply@anthropic.com>
Both are comment-only; no assertion, no harness and no gate input moves.
`plugin-audit/src/read-audit-view-instant-preservation.integration.test.ts`
named `migrate-sys-notification-to-event.test.ts` and
`packages/runtime/src/notification-migration-audit-preservation.integration.test.ts`
as files a reader could go open. Both are absent from the tree (measured, with a
sibling that IS present as the positive control). They are now marked retired;
the first is kept because the SHAPE it demonstrated is the paragraph's point.
`metadata/src/migrations/raw-exec-operator-detail-16657.test.ts` said "at the
three `err?.message ?? String(err)` sites". Two remain. Measured rather than
carried: the directory holds two `catch (err: any)` sites recording through
`operatorFacingErrorText` and one `catch (error)` site, matching the docblock's
own two-expression split; and the pre-merge tree of `d64bcb6377` shows the third
in the deleted `migrate-sys-notification-to-event.ts`, which returned
`{ outcome: 'failed', reason: err?.message ?? String(err) }`.
Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3
Co-authored-by: Claude <noreply@anthropic.com>
fd28ee6 to
466593d
Compare
|
| defect | state |
|---|---|
stale pnpm-lock.yaml |
✅ fixed — pnpm-lock.yaml is now in the diff (+0/-3) |
missing line-initial Clause-② |
✅ fixed — the body now carries exactly 1 such line, and ⛔ no duplicate footer came with the edit |
Check runs on this head: 14 success · 19 in progress · 2 failure, and ⛔ both failures are this one gate (two runs of it). ⇒ every other red from the first push is gone. ⭐ The 19 in_progress rows mean this PR is ⛔ not cleared yet — that is an honest reading, not a green one.
Which class this is, and why the COLLISION remedy must NOT be run
The gate names two classes with opposite remedies. This is the DELIBERATE CORRECTION class, and the gate's own source says what follows (scripts/check-empty-changeset.mjs:599-606), verbatim:
For the DELIBERATE CORRECTION class there is no second command to run, and step 1 above is the one thing not to do … say so on the PR, naming the note and what changed under it, and get it confirmed. That is the existing human path; this gate stays red either way, and staying red is what puts the decision in front of a person instead of routing around it.
⛔ git checkout <merge-base> -- .changeset/retire-adr-0030-notification-event-migration.md must not be run here, and neither must a rename: both are the COLLISION remedy, and either one deletes item 1 — the single item on this card carrying a release deadline.
The declaration — naming the note and what changed under it
- The note:
.changeset/retire-adr-0030-notification-event-migration.md, created by PR refactor(metadata,spec)!: retire the adr-0030-notification-event migration — no operator door, no platform invoker #17194 (card Theadr-0030-notification-eventmigration has no operator path: no production caller and noos migratesub-command, while its two sibling attested ids have both #16194, mergedd64bcb6377). It is pending, ⛔ unconsumed —.changeset/holds 334 entries onorigin/main, so no release has taken it. - What changed under it: exactly one line added —
'@objectstack/platform-objects': minor. ⛔ Nothing was rewritten, ⛔ nothing removed; the diff on that file is+1/-0. - Why: the note grades
@objectstack/metadataand@objectstack/speconly, while the operator-visible behaviour change it describes — a fresh datastore attesting 2 rows, not 3 — is delivered byattestFreshDatastoreinpackages/platform-objects/src/system/migration-flag.ts, which isprivate: false. The fixed group bumps the package regardless, so ⛔ only its CHANGELOG is short a row. - Who asked for it: triage
5692961190, verbatim: 「Dispatch instruction to whoever takes it: item 1 FIRST, in its own commit if need be.」 It is797caf0c, its own commit, taken first.
⇒ The note as it stands is incomplete rather than false, which is why this is an addition and not a rewrite.
⏳ The clock, stated plainly because it decides the cost of waiting
Once a release consumes this changeset the row can never be added — the card itself prescribes closing item 1 as missed at that point, and ⛔ content/docs/releases/ is release-owned and is never the place to patch it up. PR #17076 (chore: version packages) is open and awaiting a human merge, so the window closes the moment it lands.
What this seat is and is not doing
- ⛔ This seat does not self-confirm a release decision. 〈红线〉: 「⛔ 永不跑版本发布、不合并 Version Packages PR」, and the manual floor covers release actions.
- ⛔ The delivering dev has been told to stop touching this check; there is nothing it can do from its side.
- ⛔ No re-run was spent — a re-run cannot change a by-design verdict.
- ✅ The maintainer has been asked for the confirmation this gate requires.
⭐ Recorded for the enqueue decision, since this red may have to ride: the gate does ⛔ not run on merge_group — .github/workflows/pr-automation.yml declares on: pull_request with types [opened, synchronize, reopened, labeled, unlabeled, edited] and ⛔ no merge_group trigger — and its source self-describes as staying red for this class. ⇒ two of the three 「by-design red」 conditions are established by reading, and this comment is the third.
domain:cli execution PM seat · #6024 · session session_01DvvamiacK328idtBYJBxV3 · R76 · changeset-gate declaration · reading time 2026-09-16T08:25Z
Generated by Claude Code
裁定 — 维护者选 B:另开新 changeset,⛔ 不动既有发布说明维护者原话(逐字):
对应本席在 2026-09-16T14:16Z 之前提出的三选一,B 的原文是:
⇒ 这是 这条裁定改变了什么
⛔ 两条前提,交付方必须先证伪,⛔ 不得假定
顺带更正本席自己的一处读数此前本席报本 PR 「40 绿 + 3 条 by-design 红」。重读 head Ruling recorded and readings taken 2026-09-16T14:16Z. Generated by Claude Code |
…tirement note is restored Ruling `5698965201` on PR #18389 chose route B: do not edit a changeset that exists on the merge base. `.changeset/retire-adr-0030-notification-event-migration.md` is restored byte-for-byte to its merge-base blob (2ae6527), and the `@objectstack/platform-objects: minor` bump it was carrying moves to a new, issue-scoped changeset. The new note is written for a release reader rather than for this PR's reviewer: a datastore created from empty now attests two creation-attested migration ids instead of three, because `attestFreshDatastore` defaults to `CREATION_ATTESTED_MIGRATION_IDS` and that tuple lost `'adr-0030-notification-event'` in the retirement. Declared cost of route B, named by the ruling: the CHANGELOG gets a separate `platform-objects` entry rather than a row on the retirement note. Claude-Session: https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3 Co-authored-by: Claude <noreply@anthropic.com>
Fixes #17281
Clause-②: no — this PR puts no new key on a published payload. It adds one changeset row, removes a dead workspace dependency (a narrowing, never a widening) and corrects stale prose; triage's grading
5692961190states it directly: "nothing widens an accept set or a public surface". Declared by the dispatching seat in claim5693760798.Residue from the
adr-0030-notification-eventretirement (#16194 / PR #17194, mergedd64bcb6377). Four items, four commits, item 1 first and alone as the triage grading5692961190directed, then re-cut on the maintainer's ruling5698965201.Item 1 — the
platform-objectsrelease note (re-cut on ruling5698965201)797caf0cfirst put'@objectstack/platform-objects': minoron the existing.changeset/retire-adr-0030-notification-event-migration.md.d2544585undoes that andtakes route B instead. Two acts, one commit:
git rev-parse 8b81ab60d:.changeset/retire-adr-0030-notification-event-migration.mdandgit hash-objecton the restored working-tree file both read2ae652749b40dec974fdfb5fcf5fe0990267fe53, andgit diff 8b81ab60d HEADon that pathprints nothing. The file is no longer in this PR's diff at all.
.changeset/17281-platform-objects-attest-fresh-datastore.md, declaring exactly'@objectstack/platform-objects': minor, named the way the gate itself prescribes(
.changeset/{issue}-{slug}.md).The note is written for a release reader rather than for this PR: a datastore created from
empty now attests two creation-attested migration ids instead of three, because
attestFreshDatastore(packages/platform-objects/src/system/migration-flag.ts) defaultsto
CREATION_ATTESTED_MIGRATION_IDSand that tuple lost'adr-0030-notification-event'inthe retirement. No row is written under the retired id any more; a deployment that already
holds one keeps it, and
NOTIFICATION_EVENT_MIGRATION_IDsurvives as that row's name.The bump is owed at all because that operator-visible half lives in
@objectstack/platform-objects, a package with noprivate: true, and the retirement's ownrelease note never declared it.
The ruling, and its one declared cost
Maintainer ruling
5698965201chose B: open a new changeset file forplatform-objects, and do not touch the release note that already exists on the merge base.That is the
confirmedwhichcheck:empty-changeset's DELIBERATE CORRECTION class asks for— and what it authorises is this route, not an edit in place.
platform-objectsentry instead of a row under the adr-0030 retirement note. The samerelease semantics, told in two places instead of one.
Two premises the ruling asked to be falsified before acting
Both were put to a measurement that could have come out the other way. Neither broke.
Every claim in that note is about the retirement itself — the deleted runner, its three
types,
CREATION_ATTESTED_MIGRATION_IDSgoing from three members to two,NOTIFICATION_EVENT_MIGRATION_IDbeing kept. This PR's diff touchespackages/metadataandpackages/plugins/plugin-auditand no file underpackages/platform-objects,packages/specorpackages/metadata/src/migrations'sdeleted runner. The control that could have falsified it: grepping the base note for
this PR's own subject matter —
platform-objects,dependen,vitest,tsconfig,package.json,alias,paths— returns zero hits, so there is no sentence in itthat this diff could have made false. The note is under-declared, not false: the
platform-objectshalf moved in the original retirement (d64bcb6377, PR refactor(metadata,spec)!: retire the adr-0030-notification-event migration — no operator door, no platform invoker #17194), whosefile list carries
packages/platform-objects/src/system/migration-flag.ts,migration-flag.test.tsandplugin.test.ts.It is not — measured for
minorspecifically, not inferred from thepatchassertion.check-changeset-no-major.mjsrun against a synthesisedpull_requestpayload carryingthis PR's real
Clause-②: noexits 0, printing✓ LEVEL AXIS: this PR declares clause-② 'no'. The firing control, the same command onthe same tree with the payload flipped to
Clause-②: yes, exits 1 — so the axis waslive and capable of failing, and what it then prints is the sharper reading: the only
package it lists is
@objectstack/metadata: patch, becausepackages/platform-objects/src/**is not in this diff. Aminoron a package the diffdoes not move is invisible to that gate in both directions — never refused, and never
credited either.
bccdf6c1b)@objectstack/metadatano longer declares@objectstack/platform-objects. Three files, removed together because all three exist only to serve one dead edge: thedependenciesentry, the@objectstack/platform-objects/systemalias invitest.config.ts, and — not named in the card, found while measuring — the matchingpathsmapping intsconfig.json, which pointed at the same dead specifier.The card required that no consumer resolving the dependency transitively be established before removing it. Established, not assumed:
@objectstack/platform-objectsspecifiers underpackages/metadatasrc, tests and scripts — only the three config/manifest sites removed hereplatform-objectsin@objectstack/metadata's builtdist/(30 files, 10 declaration files)dist/platform-objectsmodule specifier⇒ No runtime import and no type reference reaches
platform-objectsthrough anything@objectstack/metadatapublishes, so no consumer can arrive at it via this package.check:undeclared-dep-importsis the standing gate that keeps the in-repo half true for non-testsrc/**, and it is green here.Residual, declared rather than hidden: an external consumer that installs
@objectstack/metadata, imports@objectstack/platform-objectswithout declaring it, and relies on npm's flat hoist would lose that accidental resolution. That is unmeasurable from here; it is also the class this repo's own gate treats as a defect, and@objectstack/platform-objectspublishes on its own, so the supported spelling exists. The changeset states the one-line fix.pnpm-lock.yamltravels in the same commit, regenerated withpnpm install --lockfile-onlyand never hand-edited. Its entire diff is the three lines of the one removed edge under thepackages/metadataimporter. An earlier push carried the manifest edit without it and failedERR_PNPM_OUTDATED_LOCKFILEat every job's install step; that is fixed here, and it is ⛔ not evidence about whether the removal itself is safe — the readings above are.Graded
patch— no export moves, no accept-set widens, no behaviour changes — and deliberately notskip-changeset:package.jsonis shipped bynpm pack, and a consumer's install tree is what changes.Items 3 and 4 — the two stale prose lines (
466593dc0)Comment-only; no assertion, harness or gate input moves.
plugin-audit/src/read-audit-view-instant-preservation.integration.test.tscitedmigrate-sys-notification-to-event.test.tsandpackages/runtime/src/notification-migration-audit-preservation.integration.test.tsas files a reader could open. Both measured absent, with a sibling that IS present as the positive control. Marked retired; the first citation is kept because the SHAPE it demonstrated is the paragraph's point.metadata/src/migrations/raw-exec-operator-detail-16657.test.tssaid "at the threeerr?.message ?? String(err)sites". Two, measured rather than carried: the directory holds twocatch (err: any)sites recording throughoperatorFacingErrorTextand onecatch (error)site, matching the docblock's own two-expression split. The third is visible in the pre-merge tree ofd64bcb6377, in the deleted runner, as{ outcome: 'failed', reason: err?.message ?? String(err) }.063efde2andfd28ee6f, andgit merge-base --is-ancestorputs NEITHER on this branch (control: the merge base8b81ab60dexits 0 in the same checkout, so the negative is real). The branch was re-cut after that text was written. The live commits arebccdf6c1b(item 2) and466593dc0(items 3 and 4), each mapped by its own file list. Found by the delivering agent and corrected by the dispatching seat.Gates
Derived from the actual changed files with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon headd2544585e, each run with its exit code written to disk before anything read it, thenreconciled by feeding the codes back:
74 of 75 green, plus
pnpm lint— the full repo-wide union, which the derivation doesnot name and therefore never asks for — at exit 0 on
d2544585e.⭐
check:empty-changesetis the point of this commit, and it has turned over. On466593dc0it exited 1:On
d2544585eit exits 0:check-changeset-no-major.mjsexits 0 on the same head, on both of its axes.The one family that is not green is not a defect introduced here:
check:cross-package-test-inputs— exit 1, pre-existing ([finding] check:cross-package-test-inputs passes in CI and fails on a built tree — its verdict is a function of gitignored build state #18353 / [regression] check:cross-package-test-inputs 的裁决取决于被 gitignore 的空目录 packages/spec/dist 存不存在 —— 构建过的工作树一律红,CI 绿只因那一步跑在构建之前(#18340 引入) #18348). It names@objectstack/clidescendingpackages/spec/dist/frompackages/cli/test/init-created-files-summary.e2e.test.ts; neither package is in thisdiff. Both readings were taken on this head: exit 0 before the build (that tree has no
packages/spec/dist, so the walk had nothing to descend — a vacuous green) and exit 1after it. The exit 1 is the reading that counts, and it is somebody else's card.
exit 3PREREQUISITE NOT MET— neither pass nor failure —because they load built output. After
turbo run build --filter=!@objectstack/docsall sixare green:
check:dts-closure,check:dual-build-cjs-loads,check:i18n,check:lean-entry-closure,check:sourcemap-no-sources-content,check:type-check-debt.behind
origin/main, and 10 of the files the families are derived from moved across thatrange. Discharged by measurement rather than by argument — the same derivation, run in a
throwaway worktree checked out at
origin/main(8fe5cb8e5) with this branch merged inuncommitted, derives the same 8 paths and a byte-identical 75-command list (
diffexit0). The staleness does not move the family set for these paths.
Package-level test and typecheck readings are unchanged from
466593dc0(
@objectstack/metadata53 files / 788 tests,@objectstack/plugin-audit24 files / 346tests,
typecheckgreen for both):d2544585eadds nothing on top of that head but two.changeset/*.mdfiles, which no test or typecheck program reads.Acceptance notes
packages/metadata/vitest.config.tskeeps oneplatform-objectsmention, deliberately: the comment on the@objectstack/specnamespace rule contrasts spec's uniform export map with the FILE-shaped@objectstack/platform-objects/pluginsubpath. That prose is still accurate and load-bearing about spec, and is not residue..changeset/operator-facing-raw-exec-cause-text.mdis untouched — another PR's changeset, as the card directs.docs/adr/0052-audit-is-not-the-activity-feed.md:327is untouched — governed surface, and ADR-0030 still prescribesmigrateSysNotificationToEvent, a call the #16194 retirement removed — an operator copying step 2 gets an unresolvable import #17193 is its route.Clause-②line, and the restored base note does not carry one either; 4 of the 334 changesets in the tree do. Noted, not filed — out of scope here.Claim:
5693760798. Triage:5692961190.Generated by Claude Code