Skip to content

docs(adr): ADR-0030's cut-over step and ADR-0052's rollout note record the retired migration - #19381

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-17193-adr0030-retired-migration-call
Sep 20, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-17193-adr0030-retired-migration-call

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #17193

Clause-②: no

The defect

docs/adr/0030-notification-platform-convergence.md carried a live operator prescription for
migrateSysNotificationToEvent — a runner the #16194 retirement (decision batch #88) removed
from @objectstack/metadata/migrations. An operator following the cut-over list writes an
import that does not resolve: a copy-the-example-and-it-fails defect, not a stylistic one.

⭐ The discrimination this card is about — two occurrences, ONE defect

occurrence what it is disposition
0030 § Remaining work → objectui cut-over — "Run migrateSysNotificationToEvent during the cut-over…" a prescription addressed to someone about to act corrected
0030 § Shipped (merged to main) → P0 — Seams row — "…idempotent migrateSysNotificationToEvent. | #1434" a record of what a past release shipped, true when written ⛔ left byte-for-byte as it stands

My reading of the file agrees with the card's split, and here is the structural evidence for
it rather than a restatement.
The untouched occurrence sits inside the table under the
heading ### Shipped (merged to main) — one row per phase, each carrying the PR number that
shipped it (#1434). The corrected occurrence sits under ### Remaining work (handed off to a follow-up agent), in an imperative bullet list of steps a follow-up agent is told to perform.
Retro-editing the shipped row would be rewriting history to make a grep pass, and it is the
accrete-a-row-per-release pattern the release guardrail exists to stop.

git diff on this branch contains zero hits for P0 — Seams — the row is not in the diff
at all.

⚠️ The widening request (5611847419) — TAKEN, and declared

The CONTRACT_REVIEW_TIER verdict on PR #17194 (finding F2) named a second line of the same
class in a different ADR. The dispatching seat did not rule it in or out of scope and asked
for the decision to be made out loud. It is taken, and the same test was applied to it
first:

  • docs/adr/0052-audit-is-not-the-activity-feed.md § 6. Rollout → P0b read
    "sys_notification is deferred — it is mid-migration to an event model
    (metadata/.../migrate-sys-notification-to-event.ts, ADR-0030), so moving it now would
    collide with that in-flight work."
  • Present tense, a claim about what the platform does today, and it names a path that is
    deleted from disk. That puts it on the :105 side of the test, not the shipped-history
    side. Both halves of the sentence are false as of the retirement.

Why take it rather than file it. It is the same defect, from the same retirement, in the
same governed tree, and it needs the same sentence written. Leaving it means a second card, a
second governed-surface PR and a second hand-merge for one sentence. The PM comment that
recorded it declined to file it as a second card for exactly that reason.

What the correction deliberately does NOT do. It records that the stated collision reason
is gone — it does not decide whether the sys_notification ownership move now proceeds.
That disposition is a decision for ADR-0052's own record owner, and the line says so in as many
words. ADR-0052's status line is not amended: no decision of ADR-0052 moved, only one
sentence of rationale was false.

The one judgement call — ADR-0030's amended status line

The card's "Suggested shape" asked for a decision on whether ADR-0030 wants an amended status
line naming the retirement. It does, and the PR writes one (one line, appended to the
existing **Status** line).

Prime Directive #13: reversing a recorded decision is itself a decision, and it needs a new ADR
or an amended status line on the old one. The retirement already happened, elsewhere;
because docs/adr/** is governed, PR #17194 correctly could not carry the amendment, so this
PR is where it is owed. Without it, the Status line still reads "P0–P3b2 shipped" with no
trace that a shipped P0 item was withdrawn, and a reader who greps the status learns nothing.
The wording follows the register's own precedents for this exact shape — ADR-0005 (Amended (2026-08-09, #6825 — the Phase-1 overlay-index migration is deleted…)), ADR-0127 and ADR-0045
— and closes with an explicit "nothing else moves" so it cannot be read as a wider reversal.

⚠️ This is the line to strike first if the seat or the maintainer wants a smaller diff:
the :105 correction stands on its own without it.

Premise re-verified on this branch's base (32708262d)

$ git grep -nE 'migrateSysNotificationToEvent' -- . ':!*CHANGELOG.md' ':!content/docs/releases/'
.changeset/retire-adr-0030-notification-event-migration.md:21:- `migrateSysNotificationToEvent` (`@objectstack/metadata/migrations`) — deleted.
docs/adr/0030-notification-platform-convergence.md:80    (the shipped-history row — untouched)
docs/adr/0030-notification-platform-convergence.md:105   (the prescription — this PR)
docs/adr/0052-audit-is-not-the-activity-feed.md:327      (via its file path — this PR)
docs/handoff/adr-0030-notification-convergence.md:126    (the tombstone, already correct)
packages/metadata/src/migrations/index.ts:50             (barrel TOMBSTONE)
packages/spec/src/system/migration.zod.ts:175            (retired id docblock)
+ three retirement pin tests

packages/metadata/src/migrations/ holds no migrate-sys-notification-to-event.ts. Every
remaining occurrence in shipped code is a tombstone or a pin asserting the absence. ⇒ the
premise holds: the prescription names a call that no longer exists.

Local gates — 19 derived, 19 run, 0 NOT MEASURED

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
against the actual changed files, every exit code captured before any pipe, reconciled with
--ran in the command :: exit code form the tool asks for:

✓ dispatch-gates --ran: 19 derived famil(ies) accounted for — 19 run,
  0 NOT-MEASURED (a DERIVED zero — all 19 recorded an exit code and none of them is 3).

All 19 exited 0, the ADR-specific ones among them:
check-adr-links.mjs · check-adr-symbol-anchors.mjs · check:adr-anchors ·
check:doc-authoring · check:pm-governed-merges · check:pm-prior-rulings ·
check:nul-bytes · @objectstack/lint check:doc-formula-expressions.

check:doc-formula-expressions first exited 3 — PREREQUISITE NOT MET (@objectstack/formula
and @objectstack/lint unbuilt). That is not a finding and was not recorded as one: the two
packages were built and the gate re-run, where it exited 0.

Build / test scope. The diff touches no package, so the dependency-closure build is empty
and no package's test/typecheck is affected. The build above was a gate prerequisite only.

Repo-wide eslint — a measured narrowing, not a skipped run. (1) Population, read from
eslint's own config: every files: block in eslint.config.mjs matches only
{ts,tsx,mts,cts,js,jsx,mjs,cjs}; no block matches Markdown. (2) Count, from --format json
over both changed files: 2 files, both File ignored because no matching configuration was supplied, 0 errors. (3) Non-influence: no block sets parserOptions.project or
projectService, so type-aware linting is not enabled and this diff cannot move the verdict on
any untouched file.

Changeset — skip-changeset is warranted, and is NOT applied by this PR

The repo has a definite answer and it is the label, not an empty changeset: AGENTS.md
Post-Task Checklist §3 scopes skip-changeset to "a diff that publishes nothing from any
released package", and the empty-changeset route was ruled shut for new files (#5471) — the
Check Changeset job rejects an empty changeset a PR newly introduces. A docs-only docs/adr/
diff publishes nothing from any package, so the label is the correct instrument.

⛔ The dispatching seat's write budget names no label, so this dev did not apply it. The
Check Changeset gate will be red until the seat applies skip-changeset; that red is the
gate working, not a finding.

Files changed

  • docs/adr/0030-notification-platform-convergence.md — status-line amendment; the cut-over
    prescription struck and replaced.
  • docs/adr/0052-audit-is-not-the-activity-feed.md — the P0b rollout rationale corrected.

Total: 2 files, +16 / −7.

⛔ Landing

docs/adr/** is a governed surface (Prime Directive #14, Tier H). This PR stays draft and
takes no queue: it lands by the maintainer's hand or on an authorised approval, and no agent
seat may flip it ready, enqueue it, arm auto-merge on it, or approve it.


维护者速读(草稿)

席位意见一节留空,待席位填写后贴出终稿评论。

改了什么

两个 ADR 文件里的两句话,共 2 个文件、+16 / −7 行,不含任何代码改动。

  1. ADR-0030(通知平台收敛):「前端切换时运行 migrateSysNotificationToEvent 把历史铃铛数据迁过来」这条操作指令被划掉,改成记录「该迁移已撤回」,并指向 handoff 文档里已有的墓碑说明;切换步骤从三步变两步。同时在文件顶部的 Status 行补了一条 Amended 记录,写明这次撤回。
  2. ADR-0052(审计不是动态流):P0b 那段说 sys_notification「正在迁往事件模型(某某文件,ADR-0030)」——这句话的两半现在都是假的(迁移已退役、文件已从磁盘删除)。改成记录「当初因此推迟」+「该理由已不存在」,但不替记录所有者决定这次搬迁现在要不要做。
  3. ⛔ ADR-0030 里另一处同名调用(P0 已交付表格第 80 行)一个字没动 —— 它记录的是 feat(notifications): ADR-0030 P0 — single ingress + layered model (framework) #1434 当年确实交付了什么,是历史,不是现状。

为什么改

被点名的那个函数在 #16194(决策批次 #88)里已经从 @objectstack/metadata/migrations 删掉了。ADR-0030 那条指令是写给「马上要动手的人」看的:照抄它写出来的 import 解析不了。这属于「照着例子做就失败」,不是措辞问题。

ADR-0052 那句的危害不同但同源:它给一次推迟提供理由,而那个理由已经蒸发;后来的人可能继续拿一条死掉的理由往后推。

顶部 Status 行那条 Amended 是协议第 13 条要求的:推翻一个已记录的决定本身就是决定,必须记在记录上。撤回发生在别处(#16194),而那个 PR 因为受管面规则不能碰 ADR,所以这笔账落在本 PR。

风险与代价(含回滚)

  • 风险很低:纯文档散文改动,无代码、无发布面、无生成物。19 个派生门禁全部 exit 0(含 ADR 链接、ADR 锚点、受管面合并审计)。
  • 唯一需要您拍板的:顶部那条 Amended 状态行算不算「越界替记录所有者做决定」。本 PR 的判断是「不算 —— 它只是把别处已经做出的裁定记到该记的地方」,但如果您觉得 diff 该更小,先划掉这一行,下面 :105 的修正独立成立。
  • 回滚:git revert 一个 commit 即可,不牵动任何运行时。

席位意见

(留空)

你要做的

读完 diff 的 16 行后,人工合并本 PR(或给出授权批准)—— ⛔ 它不进合并队列,agent 席位不会翻 ready、不会入队。另外请给它挂上 skip-changeset 标签,否则 Check Changeset 会一直红(本 PR 不发布任何包,派发席的写预算里没有标签,所以 dev 没有自行挂)。


Generated by Claude Code

…d the retired migration

`migrateSysNotificationToEvent` was removed from `@objectstack/metadata/migrations`
by the retirement ruling in decision batch #88. Two ADR lines still read as live
statements about a runner that no longer exists.

ADR-0030 `:105` — a prescription in the objectui cut-over list ("Run
`migrateSysNotificationToEvent` during the cut-over") — is struck in place and
replaced with the withdrawal, the consequence (pre-ADR-0030 `sys_notification`
rows are not carried by the platform on this line) and a pointer to the handoff
doc's tombstone, which holds the reasoning, the unmeasured-deployment caveat and
the reversal path. The cut-over sequence drops its middle step.

ADR-0052 `:327` said `sys_notification` "is mid-migration to an event model
(`metadata/.../migrate-sys-notification-to-event.ts`, ADR-0030)". Both halves are
false: the migration is retired and the file is deleted. The line now records why
P0b was deferred and states that the collision reason is gone, without deciding
whether the move proceeds — that is a call for that record's owner.

ADR-0030's status line gains an `Amended` entry naming the retirement, per Prime
Directive #13: a reversal of a recorded decision is itself recorded on the record.

ADR-0030 `:80` is deliberately NOT touched. The `P0 — Seams` row states what
#1434 shipped, and it did ship; editing it would rewrite history to make a grep
pass.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Sep 20, 2026
@huangyiirene huangyiirene added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 20, 2026 — with Claude

Copy link
Copy Markdown
Collaborator Author

维护者速读(终稿)

domain:engine#1 席位对照自己读的 diff 校正 PR 正文的草稿并填入席位意见。写于 2026-09-20T15:06Z。⚠️ 本条取代正文里的草稿,以本条为准。

改了什么

两个 ADR 文件里的两句话,共 2 个文件、+16 / −7 行,⛔ 无任何代码改动、⛔ 无发布面、⛔ 无生成物。

  1. ADR-0030(通知平台收敛)第 105 行:「切换时运行 migrateSysNotificationToEvent 把历史铃铛数据迁过来」这条写给马上要动手的人的操作指令,被划掉并改成记录「该迁移已撤回」+ 它的后果(平台在这条线上不再搬运 ADR-0030 之前的 sys_notification 旧数据),并指向 handoff 文档里早已存在的墓碑说明。切换步骤从三步变两步。
  2. ADR-0030 顶部 Status 行:追加一条 Amended 记录,写明这次撤回。
  3. ADR-0052(审计不是动态流)P0b 那段:原文说 sys_notification「正在迁往事件模型(metadata/.../migrate-sys-notification-to-event.ts,ADR-0030),所以现在搬它会撞上这件在飞的活」。⚠️ 这句话的两半现在都是假的 —— 迁移已退役,它点名的那个文件已从磁盘删除。改成记录「当初因此推迟」+「该理由已不存在」,但⛔ 不替记录所有者决定这次搬迁现在要不要做,也 ⛔ 没动 ADR-0052 的状态行(ADR-0052 自己的决定一个都没变)。

⛔ 一个字没动、而且是故意的

ADR-0030 第 80 行也出现同一个函数名 —— 那是 P0 — Seams 已交付表格里的一行,记录 #1434 当年确实交付了什么。⭐ 席位已逐字比对 origin/main 与本 PR 分支的同一行:字节完全相同,它根本不在 diff 里。

⚠️ 这一点是本卡的全部难度所在:同一个名字出现两次,只有一处是缺陷。两处一起擦掉,等于为了让 grep 过而篡改发布记录。

为什么改

被点名的那个函数在 #16194(决策批次 #88)里已经从 @objectstack/metadata/migrations 删掉了。ADR-0030 第 105 行是写给「马上要动手的人」看的:照抄它写出来的 import 解析不了。这属于「照着例子做就失败」,⛔ 不是措辞问题。

ADR-0052 那句危害不同但同源:它给一次推迟提供理由,而那个理由已经蒸发 —— 后来的人可能继续拿一条死掉的理由往后推。

顶部 Status 行那条 Amended 是协议第 13 条逐字要求的:「推翻一个已记录的决定本身就是决定 —— 它需要一份新 ADR(或在旧 ADR 上加一条修订状态行)」。撤回发生在别处(#16194),而那个 PR 因为受管面规则不能碰 ADR,所以这笔账落在本 PR。

风险与代价(含回滚)

  • 风险很低:纯文档散文,⛔ 不碰运行时、⛔ 不碰任何包、⛔ 无用户可见产物。19 个派生门禁全部 exit 0(含 ADR 链接、ADR 符号锚点、受管面合并审计、文档撰写规范)。
  • ⭐ 席位已代您做掉的一件事:skip-changeset 标签已挂(本 PR 不发布任何包)。dev 没有自行挂是对的 —— 派发令的写预算里没有标签,它申报了而不是自己动手。Check Changeset 已随之重跑为 skipped。
  • 本 PR 比卡片当初设想的多了一个文件:ADR-0052 那一处。它来自本卡上的一条扩围请求(5611847419,由 domain:cli 席在 PR refactor(metadata,spec)!: retire the adr-0030-notification-event migration — no operator door, no platform invoker #17194 的 ACCEPT 上登记,并明确说「记在这张卡上,⛔ 不另立卡」)。派发令没有替它裁定在不在范围内,而是要求 dev 出声决定;dev 取了并写明了理由。席位复核过四条邻接判据(同缺陷类 / 机械 / 无他人认领 / 同门禁族),全部成立,其中「无他人认领」是把全部 30 个 open PR 的文件清单逐个读过得出的,⛔ 不是看标题。
  • 回滚:git revert 一个 commit,⛔ 不牵动任何运行时。

席位意见

建议原样合并,包括那条 Amended 状态行。

PR 正文把那条状态行标为「若要更小的 diff,先划掉这一行」。⚠️ 席位不同意这个取舍,理由是具体的:划掉它之后,ADR-0030 的状态行仍然写着 **P0–P3b2 shipped**,而一个 P0 交付项已被撤回 —— 一个 grep 状态行的读者会读到与事实相反的结论。协议第 13 条点名的补救就是这条状态行本身,不是可选装饰。

席位已逐条核对那条 Amended 里的每一句事实,对照的是退役自己的记录(.changeset/retire-adr-0030-notification-event-migration.md):「零生产调用方」「没有任何办法运行它」「两种给它办法的方案(os migrate 子命令、启动期调用)都被否决」「平台在这条线上不搬运 ADR-0030 之前的旧数据」—— 四句全部是那份 changeset 的原话。⇒ 这条状态行记录了一个在别处做出的裁定,⛔ 它自己没有做任何新裁定。

⚠️ 一处席位不替您隐瞒的张力:本卡的分诊裁定(5608380082)写过「本卡的 PR 只能是 ADR-0030 的单文件改动」,而交付是两个文件。席位判断这不构成违背 —— 那条约束的下一句点明了它真正防的东西:「⛔ 不得与任何代码改动同 PR」,即防止把一个本来普通的 diff 拖进只能人工合并的领地。而第二个文件同样是受管的 ADR:整个 diff 本来就已经是 Tier H、本来就只能人工合并、门禁族逐字相同 ⇒ 那条约束要保护的利益一分未损。何况那条扩围请求(2026-09-10)晚于分诊裁定(2026-09-09),且点名路由到本卡。⛔ 席位把这条写出来而不是私下消化,因为动手合并的是您。

你要做的

读完这 23 行 diff,人工合并 PR #19381(或以授权账户给出 APPROVED 审查,届时由本席位落地)。

⛔ 它不进合并队列,agent 席位不会翻 ready、不会入队、不会挂 auto-merge、不会批准 —— 这是受管面 Tier H 的规定路径,不是卡住了。

CI 全绿,已核到终态(全部钉在 head dffae899):check runs 21 success / 10 skipped / 0 红 / 0 pending,两个必查 job(Lint & Repo Gates、TypeScript Type Check)都是 success;Vercel 状态 = success;PR 现答 mergeable_state: **clean**。⛔ 您这边没有别的前置动作。

⚠️ 展开检查列表时会看到的一行,先说明:Check Changeset 有两条记录 —— 旧的那条是 failure,来自挂 skip-changeset 标签之前那一轮;标签落下后重跑的那条是 skipped。⇒ 那条红是门禁正常工作的痕迹,⛔ 不是缺陷,也 ⛔ 不需要重跑;GitHub 的合并判定取同名最新的那条,所以 PR 现在是 clean。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 20, 2026 22:54
@os-zhuang
os-zhuang enabled auto-merge September 20, 2026 22:54
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit 8e368dc Sep 20, 2026
40 of 41 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-17193-adr0030-retired-migration-call branch September 20, 2026 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0030 still prescribes migrateSysNotificationToEvent, a call the #16194 retirement removed — an operator copying step 2 gets an unresolvable import

3 participants