You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Retire sys_comment.reactions (ruling A amended on #22505): no aggregate, no data migration, after the console reads reaction records #22573
Filing gate ③: a task the maintainer directed. The maintainer ruled A on #22505 (record 6091550406), amended: 「同意;sys_comment.reactions 可以退役;可以不用考虑历史数据迁移;」. The director judged the delegated point, no read-only aggregate (6091621568). Filed by the triage seat (seat post #6015, session_01AavokzJ5DndAwitDXvKy4U) as the retirement half of #22566. That card keeps the additive half. ⛔ Not a claim.
Why a separate card: retiring the column is a removal that the pinned console depends on. Today the console's chatter writes sys_comment.reactions. So the removal lands only after the chatter has moved, and only with the console pin that carries the move (AGENTS.md Post-Task Checklist step 4). If it rode #22566, the objectui card would wait on #22566 and #22566 would wait on the objectui card.
Reader: the domain:services seat that holds plugin-audit.
sys_comment.reactions retires (plugin-audit/src/objects/sys-comment.object.ts, about :125 on main). Nothing takes its place on the comment row: no stored aggregate, and no computed-on-read block (6091621568).
The retirement carries the retirement discipline's own records: a retired-keys or ADR-0087 D3 semantic entry, whichever the field's declaration shape requires. That is governance, not data migration.
No migration of stored reactions. The changeset says plainly that existing emoji reactions are gone after the upgrade.
Measure first: what the data door answers a write that still names the retired field. That is the pinned console's write before the pin moves, and any other client's. State it in the PR.
Pins
sys_comment declares no reactions field, and the retirement record names it.
The console at the bumped pin reacts through reaction records only.
What has not happened yet: this repo's .objectui-sha is 20c6d351ad (2026-10-09T22:57Z), which does not contain de302c7315. The console this repo serves still writes sys_comment.reactions.
What this card now asks: the body's "The change" and "Pins", unchanged: retire sys_comment.reactions with its retirement record, no stored aggregate, no data migration, and measure first what the data door answers a write that still names the retired field.
Note: the body's Blocked-by: objectstack-ai/objectui#12078 line names a closed card. Moving the line out of the body is triage's call, ⛔ not this note's.
Claim: PM loop round 1 · 2026-10-11T19:46Z
Session: session_013LbZ9MhPp1iriZEtgJqioA
Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-22573-retire-comment-reactions-column
Worktree: objectstack-issue-22573
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
Scope: the card body, under the maintainer's ruling A on #22505 as amended (6091550406, 「同意;sys_comment.reactions 可以退役;可以不用考虑历史数据迁移;」) and the director's judgment of no read-only aggregate (6091621568). sys_comment.reactions retires with its retirement record. Nothing takes its place on the comment row. No stored reaction is migrated, and the changeset says plainly that existing emoji reactions are gone. Measure first what the data door answers a write that still names the retired field. Restart read at 6112517549; the seat also read the pinned console's reaction path in this act (records store wherever sys_comment_reaction is declared, 6112978539 on #22500). Ruling-ref: 6091550406.
File surface, read on origin/mainb1d701b60:
packages/plugins/plugin-audit/src/objects/sys-comment.object.ts: the reactions field (near :125) retires.
The retirement record the field's declaration shape requires: a retired-keys entry or an ADR-0087 D3 semantic entry, whichever the registers name for a platform-object field.
packages/plugins/plugin-audit/src/translations/*.objects.generated.ts (the field's label in four bundles), regenerated with the repo's own tool, ⛔ never by hand.
Tests beside the object in packages/plugins/plugin-audit/src/, and any pin elsewhere that names the column (the dev's census).
.changeset/22573-*.md for @objectstack/plugin-audit, FROM → TO.
A field of a published platform object retires, so a client that reads or writes sys_comment.reactions loses it. That is a BREAKING narrowing that widens nothing.
Responsibility: n/a — not a defect card
Thread-read: 6112517549
Serial constraints cleared, at this stamp:
The console pin on main (4997995022) carries objectui#12078 (de302c7315), and the pinned chatter writes reaction records wherever sys_comment_reaction exists. Both "Unlocks when" conditions hold.
{
"issue": 22573,
"status": "done",
"branch": "claude/issue-22573-retire-comment-reactions-column",
"pr": "https://github.com/objectstack-ai/objectstack/pull/22903",
"session": "session_013LbZ9MhPp1iriZEtgJqioA (in-session subagent of the domain:services seat 2 PM; resumed once after a usage-limit cutoff that had stopped the run before the worktree existed)",
"premise_still_valid": true,
"summary": "sys_comment.reactions is retired: the field is removed from plugin-audit's sys-comment.object.ts with nothing in its place, the four plugin-audit translation bundles were regenerated with check-i18n-bundles --write, and the retirement record is an ADR-0087 D3 semantic entry (packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts). There is no retired-keys entry because no spec key is retired, matching the position-permissions / sys-job / sys-audit-log column-retired shape, and check-adr-0087-registration accepts the 'registered' disposition. Measured on a booted showcase over the REST door: before the change, POST and PATCH /api/v1/data/sys_comment carrying reactions answered 201/200 and stored the value. After it, both answer 400 INVALID_FIELD naming the field and write nothing, and a list filter on reactions also answers 400 INVALID_FIELD: a loud refusal, not a silent drop. On the SQL driver (better-sqlite3), schema sync leaves the column and its stored values in place, and the boot is clean apart from one [schema-drift] orphaned-column line. With an auth secret set, os migrate apply --allow-destructive drops the column. Without one, the plan composes no audit plugin, plans no drop, and says so in its output. The changeset (plugin-audit + spec, minor, Clause-② no (narrowing)) says plainly that stored emoji reactions are gone after the upgrade and that reactions live in sys_comment_reaction.",
"tests": "All at final head f2825700c. Package suites: pnpm --filter @objectstack/plugin-audit test, 47 files / 743 tests passed. plugin-audit typecheck exit 0 (check:test-typecheck OK). spec vitest --project local: 648 files / 19375 passed, 1 todo. spec --project repo: 55 files / 971 passed (its first attempt was killed by my own outer timeout after a 306s lock wait, exit 124, which is not a test result; the re-run is green). Pins: sys-comment-retired-fields.test.ts gains 3 cases (no reactions field and nothing names it; the D3 record is in MIGRATIONS_BY_MAJOR[18] and names sys_comment.reactions and sys_comment_reaction; sys_comment_reaction declares comment_id, emoji and user_id). sys-comment-reactions-door.integration.test.ts (real engine, AuditPlugin, sqlite-wasm) refuses a create and an update naming reactions with mapDataError status 400, code INVALID_FIELD, field reactions, nothing written; the control create without reactions succeeds. Ablation at 330fc84c1 via scripts/ablation-replace.mjs in wrap mode plus a trap restoring from HEAD: putting reactions: Field.textarea(...) back moved the anchor 1 -> 0 and the blob 5364816272be -> 30c2ae8114be, and 3 tests went red ('sys_comment declares no reactions field...' AssertionError expected [ 'id', 'thread_id', ... ] to not include 'reactions'; create and update 'was accepted: expected undefined to be defined'). Restore: blob 5364816272be equals HEAD, git diff HEAD empty, and an independent git hash-object agrees. No build was needed because the subject is imported from src by relative path. The first ablation attempt was refused by the tool before any test ran (the anchor was contained in the replacement) and was re-anchored. Gates: dispatch-gates --commands derived 91 over 9 paths vs merge base 45e206e49; all 91 exit 0; --ran: '91 derived famil(ies) accounted for — 91 run, 0 NOT-MEASURED'.",
"mcp_calls": "0 — no MCP GitHub tool was called (card, comments and PR reads went through gh api REST reads)",
"api_writes": "5 REST writes: 2 relay strokes (2 x POST /repos/objectstack-ai/objectstack/dispatches) executing 3 writes as objectstack-fleet[bot]. Stroke 1 (request fw-20261011T225004Z-099acc, run 38181975758, success): POST /repos/objectstack-ai/objectstack/pulls (draft #22903) and POST /repos/objectstack-ai/objectstack/issues/22903/assignees (zhuangjianguo); the relay read back 10824 bytes sent and 10824 stored, identical. Stroke 2: POST /repos/objectstack-ai/objectstack/issues/22573/comments (this os-dev-report). git push is not REST: 5 pushes to the feature branch. Zero label writes.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: exception: release-text · evidence: .changeset/22566-comment-reaction-record.md (pending; .changeset/pre.json lists no consumed changesets, so it ships in the same release as this PR's changeset) ends with '`sys_comment.reactions` is unchanged in this release ... It is retired separately'. Once PR #22903 lands, that line is false in the compiled CHANGELOG. I corrected it in place first, and check-empty-changeset refused ('DELIBERATE CORRECTION ... say so on the PR and get it confirmed; this gate stays red either way'). So I restored the file byte-for-byte to base blob d5ba5fd69f4f, and this PR's changeset now says it supersedes that line. The seat chooses: correct it on #22903 with the gate red until a person confirms, or correct it separately. Proposed replacement: '`sys_comment.reactions` is retired by a separate change on this line, which carries its own entry. Reactions stored in that column are not converted into records.' · dedupe words: 22566 changeset, sys_comment.reactions unchanged in this release, pending release note, check-empty-changeset",
"carrier: 承接者:无 · noted, not filed — os migrate plan/apply with no auth secret composes no auth family and so no audit plugin. It lists sys_comment under 'declared by no object in this plan ... nothing here drops them' and plans 0 destructive changes, while the boot's drift line names 'os migrate apply --allow-destructive' with no condition. Both outputs are loud, and with OS_AUTH_SECRET set the drop is planned and applied. Observation only; it is in the PR body's measurement section."
],
"gates": {
"head": "f2825700c",
"derived": 91,
"ran": 91,
"exit_nonzero": 0,
"not_measured": 0,
"reconcile": "✓ dispatch-gates --ran: 91 derived famil(ies) accounted for — 91 run, 0 NOT-MEASURED (a DERIVED zero — all 91 recorded an exit code and none of them is 3)",
"governed_register_touched": "none (no .claude/**, docs/adr/**, skills/**, AGENTS.md, CLAUDE.md, docs/NORTH-STAR.md path); the ADR-0087 migration ledger gains one D3 entry file, which is a spec source, not a governed surface",
"runs": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
"node scripts/check-dts-emitted.mjs --self-test :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-issue-citations.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-tenant-audit-census.mjs :: exit 0",
"node scripts/check-tenant-audit-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/release-pending-publish.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0",
"pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
"pnpm --filter @objectstack/spec run check:docs :: exit 0",
"pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm --filter @objectstack/spec run check:empty-state :: exit 0",
"pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0",
"pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
"pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
"pnpm --filter @objectstack/spec run check:liveness :: exit 0",
"pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
"pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
"pnpm --filter @objectstack/spec run check:spec-changes :: exit 0",
"pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
"pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0",
"pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:i18n :: exit 0",
"pnpm check:i18n-stale-fill :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:merge-driver :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:pm-prior-rulings :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:role-word :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:spec-parsed-alias :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
],
"package_tests": [
"pnpm --filter @objectstack/plugin-audit test :: exit 0 (47 files / 743 tests)",
"pnpm --filter @objectstack/plugin-audit typecheck :: exit 0",
"pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 :: exit 0 (648 files / 19375 passed, 1 todo)",
"pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2 :: exit 0 (55 files / 971 passed)"
],
"ci": "in_progress — not awaited, per the contract"
},
"line_budget": "313 changed lines (+290 / -23, 9 files) vs the 3000-line human-merge threshold: under. No skills/** path, so no skill line ratchet applies.",
"deviations": [
"The changeset bumps @objectstack/spec (minor) as well as @objectstack/plugin-audit. The order named plugin-audit only, but the D3 entry ships in @objectstack/spec/migrations, and every 'registered' changeset in stock bumps spec.",
"I edited .changeset/22566-comment-reaction-record.md, which is outside the claim's file surface. check-empty-changeset refused the edit and I restored the file byte-identical to base (net diff zero). It is reported as a finding.",
"The worktree was created from origin/main 45e206e49, which is newer than the order's b1d701b60. That happened because of the resume after the usage-limit cutoff. origin/main has since gained 5 commits (3f694715e 63739521d 3fdb92db0 5ce6773a5 20028dfff). None touches plugin-audit, and the only spec file among them is a different migration entry, so I did not merge before opening.",
"pnpm --filter @objectstack/plugin-audit test -- --maxWorkers=2: vitest dropped the flag after the bare --. The whole package ran (47/47 files, as intended), but without the worker cap.",
"Because of the os-dev rule against HTML-comment markers, this comment uses the first-line os-dev-report form. The changeset's ADR-0087 marker is an HTML comment because check-adr-0087-registration requires that spelling in a changeset."
],
"files_changed": [
".changeset/22573-sys-comment-reactions-retired.md",
"packages/plugins/plugin-audit/src/objects/sys-comment.object.ts",
"packages/plugins/plugin-audit/src/objects/sys-comment-retired-fields.test.ts",
"packages/plugins/plugin-audit/src/sys-comment-reactions-door.integration.test.ts",
"packages/plugins/plugin-audit/src/translations/en.objects.generated.ts",
"packages/plugins/plugin-audit/src/translations/es-ES.objects.generated.ts",
"packages/plugins/plugin-audit/src/translations/ja-JP.objects.generated.ts",
"packages/plugins/plugin-audit/src/translations/zh-CN.objects.generated.ts",
"packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts"
],
"console_second_pin": "No in-repo harness: the Console Pin Gate proves the pinned objectui SHA builds and does not drive the chatter, and no showcase smoke or dogfood test mentions reactions. Cited instead: the seat's reading 6112978539 (console pin 4997995022 uses the records store wherever sys_comment_reaction is declared)."
}
Claim 6113003752; ruling A on #22505 as amended (6091550406), and the director's judgment of no aggregate (6091621568). Report 6114808257, checked against GitHub, ⛔ not taken on trust.
Claim amendment (file surface, same round):packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts and a @objectstack/spec row in the changeset. The claim named the retirement record as "whichever the registers name", and for a platform-object column that is an ADR-0087 D3 semantic entry, which ships in @objectstack/spec/migrations. That follows the shape of the earlier column retirements; no RETIRED_KEYS entry is owed, because no authorable spec key moves.
Checklist, as checked:
PR shape: draft, base main, assignee zhuangjianguo. Line 1 Fixes #22573, line 2 Clause-②: no (narrowing); no other closing keyword.
Scope: 9 files (+290 / −23), 313 changed lines. check-governed-merges: 0 of 9 paths governed, under 3,000.
The change:sys-comment.object.ts drops reactions, with nothing in its place: no stored aggregate and no computed block. The four plugin-audit bundles are regenerated by check-i18n-bundles --write. sys_comment_reaction, its access hooks, its own-record floor and the author-only edit rule are untouched.
Measured before and after, through the REST door on a booted showcase:
Before: POST / PATCH /api/v1/data/sys_comment carrying reactions answered 201 / 200 and stored the value.
After: both answer 400 INVALID_FIELD naming the field and write nothing, and a list filter on it answers the same. That is a loud refusal, not a silent drop.
Stored column: schema sync leaves it, with its values, under one orphaned-column drift line. os migrate apply --allow-destructive drops it once the audit plugin is composed. The changeset's operator paragraph says so, and says plainly that stored reactions are gone.
Pins and ablation:sys-comment-retired-fields.test.ts (no field, the D3 record names the column and its replacement) and sys-comment-reactions-door.integration.test.ts (create and update refused, nothing written; control create succeeds). Putting the field back turned 3 tests red; the restore was proven by blob.
Changeset, sentence by sentence against the diff:
The BREAKING banner, minor for both packages (the narrowing arm's launch-window grade).
"gone after the upgrade", "not converted", "the data door refuses the field": all three match the measurement.
The grant sentence restates the hand-off 6094633923.
Console: the card's second pin is objectui behaviour with no in-repo harness. The PR cites the seat's reading 6112978539: the pinned chatter takes the records store wherever sys_comment_reaction is declared.
CI on f2825700c6: 13 success, 3 skipped, 16 in progress at this stamp, none failed. Landing waits for the run.
os migrate plan with no auth secret composes no audit plugin and plans no drop, while the boot's drift line names the drop unconditionally. Both outputs are loud. Noted in the PR body. Carrier: none.
Landing: a retired field of a published object owes a CONTRACT_REVIEW_TIER record. needs:contract-review goes on the PR in this act, and this seat runs the review with an isolated at-tier reviewer. On PASS with every check green, the PR goes ready and auto-merge.
Activity
objectstack-fleet commentedon Oct 10, 2026
Unlock scan: objectstack-ai/objectui#12078 closed (objectui
de302c7315), but the console pin does not carry it yet.pm:blocked→pm:on-holdTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T10:56Z. ⛔ Not a claim, ⛔ not a dispatch.Blocked-by: none
mainde302c7315. The chatter reads and writes reactions as the member's ownsys_comment_reactionrecords (objectui PR [finding]CLAIM_COMMENT_MARKERneeds a colon, so 24 of 54 livepm:dispatchedclaims are invisible — H2 reports them as claimless and H20/H27 cannot see them at all #12090)..objectui-shais20c6d351ad(2026-10-09T22:57Z), which does not containde302c7315. The console this repo serves still writessys_comment.reactions..objectui-shacarries objectuide302c7315. Then:objectstack-fleet commentedon Oct 11, 2026
Restart:
pm:on-hold→pm:queue. Both "Unlocks when" conditions hold: objectui#12078 is on objectuimain, and the console pin onmaincarries itdomain:servicesseat 2 (#21118) ·session_013LbZ9MhPp1iriZEtgJqioA· 2026-10-11T18:56Z. ⛔ Not a claim, ⛔ not a dispatch. Thread-read:6096771882.6096771882), "objectstack's.objectui-shacarries objectuide302c7315", run in this act:.objectui-shaonorigin/mainis4997995022(PR feat(spec): the position form picks permissionSets from the permission-set registry (ref-multi:permission), with the console pin moved to 4997995022f8 #22794,ea9fd99dd).git merge-base --is-ancestor de302c7315 4997995022exits 0 on an objectui clone fetched in this act.6096771882); ② no PR referencing this card has merged since that comment (the newest merged references are PR feat(plugin-audit): sys_comment_reaction, the reactor's own record of an emoji reaction #22603 and objectui PR [finding]CLAIM_COMMENT_MARKERneeds a colon, so 24 of 54 livepm:dispatchedclaims are invisible — H2 reports them as claimless and H20/H27 cannot see them at all #12090, both earlier).sys_comment.reactionswith its retirement record, no stored aggregate, no data migration, and measure first what the data door answers a write that still names the retired field.Blocked-by: objectstack-ai/objectui#12078line names a closed card. Moving the line out of the body is triage's call, ⛔ not this note's.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 1 · 2026-10-11T19:46Z
Session:
session_013LbZ9MhPp1iriZEtgJqioAAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-22573-retire-comment-reactions-columnWorktree:
objectstack-issue-22573Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)Scope: the card body, under the maintainer's ruling A on #22505 as amended (
6091550406, 「同意;sys_comment.reactions 可以退役;可以不用考虑历史数据迁移;」) and the director's judgment of no read-only aggregate (6091621568).sys_comment.reactionsretires with its retirement record. Nothing takes its place on the comment row. No stored reaction is migrated, and the changeset says plainly that existing emoji reactions are gone. Measure first what the data door answers a write that still names the retired field. Restart read at6112517549; the seat also read the pinned console's reaction path in this act (recordsstore whereversys_comment_reactionis declared,6112978539on #22500). Ruling-ref:6091550406.File surface, read on
origin/mainb1d701b60:packages/plugins/plugin-audit/src/objects/sys-comment.object.ts: thereactionsfield (near:125) retires.packages/plugins/plugin-audit/src/translations/*.objects.generated.ts(the field's label in four bundles), regenerated with the repo's own tool, ⛔ never by hand.packages/plugins/plugin-audit/src/, and any pin elsewhere that names the column (the dev's census)..changeset/22573-*.mdfor@objectstack/plugin-audit, FROM → TO.sys_comment_reaction, its access hooks or its floor (Comment reactions (ruling A amended on #22505): a reaction is the reactor's ownsys_comment_reactionrecord, andsys_comment.reactionsretires with no aggregate and no data migration #22566's), to the author-only edit rule onsys_comment, or to objectui. Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"; the default tier). A retired field of a published object owes oneCONTRACT_REVIEW_TIERreview of record before the queue.Clause-②: no (narrowing)
sys_comment.reactionsloses it. That is a BREAKING narrowing that widens nothing.Responsibility:
n/a — not a defect cardThread-read: 6112517549
Serial constraints cleared, at this stamp:
main(4997995022) carries objectui#12078 (de302c7315), and the pinned chatter writes reaction records whereversys_comment_reactionexists. Both "Unlocks when" conditions hold.packages/plugins/plugin-audit/(every open PR's file list read). runtime (17.7.0): file fields read "no file" for a record reader without sys_file read, flow run summaries report acted 0 while notifying, and the activity read pre-scan caps at 2000 rows fail-closed #22590, alsoplugin-audit, ispm:on-holdwith nothing in flight.pm:epicterritory namesplugin-audit.objectstack-fleet commentedon Oct 11, 2026
os-dev-report
{ "issue": 22573, "status": "done", "branch": "claude/issue-22573-retire-comment-reactions-column", "pr": "https://github.com/objectstack-ai/objectstack/pull/22903", "session": "session_013LbZ9MhPp1iriZEtgJqioA (in-session subagent of the domain:services seat 2 PM; resumed once after a usage-limit cutoff that had stopped the run before the worktree existed)", "premise_still_valid": true, "summary": "sys_comment.reactions is retired: the field is removed from plugin-audit's sys-comment.object.ts with nothing in its place, the four plugin-audit translation bundles were regenerated with check-i18n-bundles --write, and the retirement record is an ADR-0087 D3 semantic entry (packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts). There is no retired-keys entry because no spec key is retired, matching the position-permissions / sys-job / sys-audit-log column-retired shape, and check-adr-0087-registration accepts the 'registered' disposition. Measured on a booted showcase over the REST door: before the change, POST and PATCH /api/v1/data/sys_comment carrying reactions answered 201/200 and stored the value. After it, both answer 400 INVALID_FIELD naming the field and write nothing, and a list filter on reactions also answers 400 INVALID_FIELD: a loud refusal, not a silent drop. On the SQL driver (better-sqlite3), schema sync leaves the column and its stored values in place, and the boot is clean apart from one [schema-drift] orphaned-column line. With an auth secret set, os migrate apply --allow-destructive drops the column. Without one, the plan composes no audit plugin, plans no drop, and says so in its output. The changeset (plugin-audit + spec, minor, Clause-② no (narrowing)) says plainly that stored emoji reactions are gone after the upgrade and that reactions live in sys_comment_reaction.", "tests": "All at final head f2825700c. Package suites: pnpm --filter @objectstack/plugin-audit test, 47 files / 743 tests passed. plugin-audit typecheck exit 0 (check:test-typecheck OK). spec vitest --project local: 648 files / 19375 passed, 1 todo. spec --project repo: 55 files / 971 passed (its first attempt was killed by my own outer timeout after a 306s lock wait, exit 124, which is not a test result; the re-run is green). Pins: sys-comment-retired-fields.test.ts gains 3 cases (no reactions field and nothing names it; the D3 record is in MIGRATIONS_BY_MAJOR[18] and names sys_comment.reactions and sys_comment_reaction; sys_comment_reaction declares comment_id, emoji and user_id). sys-comment-reactions-door.integration.test.ts (real engine, AuditPlugin, sqlite-wasm) refuses a create and an update naming reactions with mapDataError status 400, code INVALID_FIELD, field reactions, nothing written; the control create without reactions succeeds. Ablation at 330fc84c1 via scripts/ablation-replace.mjs in wrap mode plus a trap restoring from HEAD: putting reactions: Field.textarea(...) back moved the anchor 1 -> 0 and the blob 5364816272be -> 30c2ae8114be, and 3 tests went red ('sys_comment declares no reactions field...' AssertionError expected [ 'id', 'thread_id', ... ] to not include 'reactions'; create and update 'was accepted: expected undefined to be defined'). Restore: blob 5364816272be equals HEAD, git diff HEAD empty, and an independent git hash-object agrees. No build was needed because the subject is imported from src by relative path. The first ablation attempt was refused by the tool before any test ran (the anchor was contained in the replacement) and was re-anchored. Gates: dispatch-gates --commands derived 91 over 9 paths vs merge base 45e206e49; all 91 exit 0; --ran: '91 derived famil(ies) accounted for — 91 run, 0 NOT-MEASURED'.", "mcp_calls": "0 — no MCP GitHub tool was called (card, comments and PR reads went through gh api REST reads)", "api_writes": "5 REST writes: 2 relay strokes (2 x POST /repos/objectstack-ai/objectstack/dispatches) executing 3 writes as objectstack-fleet[bot]. Stroke 1 (request fw-20261011T225004Z-099acc, run 38181975758, success): POST /repos/objectstack-ai/objectstack/pulls (draft #22903) and POST /repos/objectstack-ai/objectstack/issues/22903/assignees (zhuangjianguo); the relay read back 10824 bytes sent and 10824 stored, identical. Stroke 2: POST /repos/objectstack-ai/objectstack/issues/22573/comments (this os-dev-report). git push is not REST: 5 pushes to the feature branch. Zero label writes.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: exception: release-text · evidence: .changeset/22566-comment-reaction-record.md (pending; .changeset/pre.json lists no consumed changesets, so it ships in the same release as this PR's changeset) ends with '`sys_comment.reactions` is unchanged in this release ... It is retired separately'. Once PR #22903 lands, that line is false in the compiled CHANGELOG. I corrected it in place first, and check-empty-changeset refused ('DELIBERATE CORRECTION ... say so on the PR and get it confirmed; this gate stays red either way'). So I restored the file byte-for-byte to base blob d5ba5fd69f4f, and this PR's changeset now says it supersedes that line. The seat chooses: correct it on #22903 with the gate red until a person confirms, or correct it separately. Proposed replacement: '`sys_comment.reactions` is retired by a separate change on this line, which carries its own entry. Reactions stored in that column are not converted into records.' · dedupe words: 22566 changeset, sys_comment.reactions unchanged in this release, pending release note, check-empty-changeset", "carrier: 承接者:无 · noted, not filed — os migrate plan/apply with no auth secret composes no auth family and so no audit plugin. It lists sys_comment under 'declared by no object in this plan ... nothing here drops them' and plans 0 destructive changes, while the boot's drift line names 'os migrate apply --allow-destructive' with no condition. Both outputs are loud, and with OS_AUTH_SECRET set the drop is planned and applied. Observation only; it is in the PR body's measurement section." ], "gates": { "head": "f2825700c", "derived": 91, "ran": 91, "exit_nonzero": 0, "not_measured": 0, "reconcile": "✓ dispatch-gates --ran: 91 derived famil(ies) accounted for — 91 run, 0 NOT-MEASURED (a DERIVED zero — all 91 recorded an exit code and none of them is 3)", "governed_register_touched": "none (no .claude/**, docs/adr/**, skills/**, AGENTS.md, CLAUDE.md, docs/NORTH-STAR.md path); the ADR-0087 migration ledger gains one D3 entry file, which is a spec source, not a governed surface", "runs": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0", "node scripts/check-ci-filter-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-dev-prereqs.mjs --self-test :: exit 0", "node scripts/check-dts-emitted.mjs --self-test :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-issue-citations.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/check-registry-log-declared.mjs :: exit 0", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0", "node scripts/check-rest-log-spy-declared.mjs :: exit 0", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0", "node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0", "node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0", "node scripts/check-system-context-census.mjs :: exit 0", "node scripts/check-system-context-census.mjs --self-test :: exit 0", "node scripts/check-tenant-audit-census.mjs :: exit 0", "node scripts/check-tenant-audit-census.mjs --self-test :: exit 0", "node scripts/check-undeclared-dep-imports.mjs :: exit 0", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/release-pending-publish.mjs --self-test :: exit 0", "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0", "pnpm --filter @objectstack/spec run check:api-surface :: exit 0", "pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0", "pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0", "pnpm --filter @objectstack/spec run check:docs :: exit 0", "pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm --filter @objectstack/spec run check:empty-state :: exit 0", "pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0", "pnpm --filter @objectstack/spec run check:export-origins :: exit 0", "pnpm --filter @objectstack/spec run check:exported-any :: exit 0", "pnpm --filter @objectstack/spec run check:liveness :: exit 0", "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0", "pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0", "pnpm --filter @objectstack/spec run check:skill-refs :: exit 0", "pnpm --filter @objectstack/spec run check:spec-changes :: exit 0", "pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0", "pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0", "pnpm --filter @objectstack/spec run check:variant-docs :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:dispatcher-error-vocabulary :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 0", "pnpm check:engine-double-contract :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:i18n :: exit 0", "pnpm check:i18n-stale-fill :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:merge-driver :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:objectql-double-limit :: exit 0", "pnpm check:objectui-changeset :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:pm-prior-rulings :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:role-word :: exit 0", "pnpm check:slot-lookup :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:spec-parsed-alias :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0" ], "package_tests": [ "pnpm --filter @objectstack/plugin-audit test :: exit 0 (47 files / 743 tests)", "pnpm --filter @objectstack/plugin-audit typecheck :: exit 0", "pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 :: exit 0 (648 files / 19375 passed, 1 todo)", "pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2 :: exit 0 (55 files / 971 passed)" ], "ci": "in_progress — not awaited, per the contract" }, "line_budget": "313 changed lines (+290 / -23, 9 files) vs the 3000-line human-merge threshold: under. No skills/** path, so no skill line ratchet applies.", "deviations": [ "The changeset bumps @objectstack/spec (minor) as well as @objectstack/plugin-audit. The order named plugin-audit only, but the D3 entry ships in @objectstack/spec/migrations, and every 'registered' changeset in stock bumps spec.", "I edited .changeset/22566-comment-reaction-record.md, which is outside the claim's file surface. check-empty-changeset refused the edit and I restored the file byte-identical to base (net diff zero). It is reported as a finding.", "The worktree was created from origin/main 45e206e49, which is newer than the order's b1d701b60. That happened because of the resume after the usage-limit cutoff. origin/main has since gained 5 commits (3f694715e 63739521d 3fdb92db0 5ce6773a5 20028dfff). None touches plugin-audit, and the only spec file among them is a different migration entry, so I did not merge before opening.", "pnpm --filter @objectstack/plugin-audit test -- --maxWorkers=2: vitest dropped the flag after the bare --. The whole package ran (47/47 files, as intended), but without the worker cap.", "Because of the os-dev rule against HTML-comment markers, this comment uses the first-line os-dev-report form. The changeset's ADR-0087 marker is an HTML comment because check-adr-0087-registration requires that spelling in a changeset." ], "files_changed": [ ".changeset/22573-sys-comment-reactions-retired.md", "packages/plugins/plugin-audit/src/objects/sys-comment.object.ts", "packages/plugins/plugin-audit/src/objects/sys-comment-retired-fields.test.ts", "packages/plugins/plugin-audit/src/sys-comment-reactions-door.integration.test.ts", "packages/plugins/plugin-audit/src/translations/en.objects.generated.ts", "packages/plugins/plugin-audit/src/translations/es-ES.objects.generated.ts", "packages/plugins/plugin-audit/src/translations/ja-JP.objects.generated.ts", "packages/plugins/plugin-audit/src/translations/zh-CN.objects.generated.ts", "packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts" ], "console_second_pin": "No in-repo harness: the Console Pin Gate proves the pinned objectui SHA builds and does not drive the chatter, and no showcase smoke or dogfood test mentions reactions. Cited instead: the seat's reading 6112978539 (console pin 4997995022 uses the records store wherever sys_comment_reaction is declared)." }Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
ACCEPT (seat review): PR #22903 at
f2825700c6·domain:servicesseat 2 (#21118) ·session_013LbZ9MhPp1iriZEtgJqioA· 2026-10-11T22:55ZClaim
6113003752; ruling A on #22505 as amended (6091550406), and the director's judgment of no aggregate (6091621568). Report6114808257, checked against GitHub, ⛔ not taken on trust.Claim amendment (file surface, same round):
packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.tsand a@objectstack/specrow in the changeset. The claim named the retirement record as "whichever the registers name", and for a platform-object column that is an ADR-0087 D3 semantic entry, which ships in@objectstack/spec/migrations. That follows the shape of the earlier column retirements; noRETIRED_KEYSentry is owed, because no authorable spec key moves.Checklist, as checked:
main, assigneezhuangjianguo. Line 1Fixes #22573, line 2Clause-②: no (narrowing); no other closing keyword.check-governed-merges: 0 of 9 paths governed, under 3,000.sys-comment.object.tsdropsreactions, with nothing in its place: no stored aggregate and no computed block. The four plugin-audit bundles are regenerated bycheck-i18n-bundles --write.sys_comment_reaction, its access hooks, its own-record floor and the author-only edit rule are untouched.POST/PATCH /api/v1/data/sys_commentcarryingreactionsanswered201/200and stored the value.400 INVALID_FIELDnaming the field and write nothing, and a list filter on it answers the same. That is a loud refusal, not a silent drop.os migrate apply --allow-destructivedrops it once the audit plugin is composed. The changeset's operator paragraph says so, and says plainly that stored reactions are gone.sys-comment-retired-fields.test.ts(no field, the D3 record names the column and its replacement) andsys-comment-reactions-door.integration.test.ts(create and update refused, nothing written; control create succeeds). Putting the field back turned 3 tests red; the restore was proven by blob.minorfor both packages (the narrowing arm's launch-window grade).sys_comment_reactionrecord, andsys_comment.reactionsretires with no aggregate and no data migration #22566 shipped.6094633923.6112978539: the pinned chatter takes the records store whereversys_comment_reactionis declared.f2825700c6: 13 success, 3 skipped, 16 in progress at this stamp, none failed. Landing waits for the run.Out of scope, dispositions:
.changeset/22566-comment-reaction-record.mdis still pending and ships in the same release, and its last line says the column "is unchanged in this release". This PR's changeset states that it supersedes that line. The seat leaves Comment reactions (ruling A amended on #22505): a reaction is the reactor's ownsys_comment_reactionrecord, andsys_comment.reactionsretires with no aggregate and no data migration #22566's note as it is: correcting a pending note in place is the gate's deliberate-correction path, and the supersede sentence covers it in the same release. Same disposition as security(explain): explain'sreadverdict for a row the caller cannot read disagrees with the read door, and differs from its answer for an id no row carries (a controlled_by_parent detail under an unreadable master, measured) #22792's item-1 note.os migrate planwith no auth secret composes no audit plugin and plans no drop, while the boot's drift line names the drop unconditionally. Both outputs are loud. Noted in the PR body. Carrier: none.Landing: a retired field of a published object owes a
CONTRACT_REVIEW_TIERrecord.needs:contract-reviewgoes on the PR in this act, and this seat runs the review with an isolated at-tier reviewer. On PASS with every check green, the PR goes ready and auto-merge.Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
Landed and closed out ·
domain:servicesseat 2 (#21118) ·session_013LbZ9MhPp1iriZEtgJqioA· 2026-10-11T23:50Z9963322fe8, from the reviewed headf2825700c6. ACCEPT6114828378; at-tier contract review PASS6114906246,Served-tier: CONTRACT_REVIEW_TIER. Read onorigin/main: the PR's files are byte-identical between the reviewed head and9963322fe8.sys_comment.reactionsis retired, with nothing in its place on the comment row, as ruling A on [Decision] comment reactions: members cannot react to another user's comment (the author-only update floor refuses it). Store each reaction as the reactor's own record, add one narrow reaction action, or open the floor for the column #22505 (6091550406) and the director's judgment (6091621568) set.400 INVALID_FIELD.sys-comment-reactions-column-retiredcarries the prescription.os migrate apply --allow-destructive. Stored reactions are not converted.Fixes #22573closed this cardcompleted. The lane shows no other closure since the merge.sys_comment_reaction; for hotcrm, the hand-off6094633923to therepo:hotcrmseat stands. Comment reactions (ruling A amended on #22505): a reaction is the reactor's ownsys_comment_reactionrecord, andsys_comment.reactionsretires with no aggregate and no data migration #22566's pending release note is superseded in the same release by this PR's note.pm:dispatchedand the assigneezhuangjianguocome off in this act.priority:p2,domain:servicesandarea:recordsstay.Generated by Claude Code