Repository navigation
feat(plugin-audit)!: sys_comment.reactions retires; a reaction lives only in sys_comment_reaction, and stored reactions are not migrated - #22903
Conversation
A reaction is its reactor's own sys_comment_reaction row; nothing about reactions is stored or computed on the comment row. The four generated translation bundles drop the field's label (check-i18n-bundles --write). Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA Co-authored-by: Claude <noreply@anthropic.com>
…rement, and its pins The semantic entry sys-comment-reactions-column-retired carries the prescription; the data door's INVALID_FIELD refusal carries the enforcement. Pins: sys_comment declares no reactions field and the record names it; a create or update naming the field is refused 400 INVALID_FIELD and writes nothing. Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA Co-authored-by: Claude <noreply@anthropic.com>
… and the operator sentence The sibling changeset that added sys_comment_reaction said the column was unchanged in this release; both are pending stock for the same release, so that bullet now points at the retirement instead. Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA Co-authored-by: Claude <noreply@anthropic.com>
…de its column line from this entry check-empty-changeset refuses a change to a changeset this PR did not add, and a deliberate correction there needs a person's confirmation. The stale line is reported to the seat instead, and this entry says plainly that it supersedes it. Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 141 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6d6329140920b579b33c71efa7efcc991e0a50e8 && git checkout 6d6329140920b579b33c71efa7efcc991e0a50e8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3f694715ee43565502bd70d6b3a64b28033531b4 f2825700c6a7ef2a18cf21ab5fa9cd167b6cee5b && git checkout -B drift-repro 3f694715ee43565502bd70d6b3a64b28033531b4 && git merge --no-ff f2825700c6a7ef2a18cf21ab5fa9cd167b6cee5b
node scripts/docs-audit/affected-docs.mjs --json 3f694715ee43565502bd70d6b3a64b28033531b4
|
Contract reviewServed-tier: Rendered by an isolated reviewer subagent of the seat in Inputs read at 2026-10-11T23:05Z: card #22573 (body and all 5 comments, the seat's ACCEPT ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #22573
Clause-②: no (narrowing)
sys_comment.reactionsretires under the maintainer's ruling A on #22505 as amended (record6091550406): 「同意;sys_comment.reactions 可以退役;可以不用考虑历史数据迁移;」. Per the director's judgment of the delegated point (6091621568), nothing takes its place on the comment row: no stored aggregate and no block computed on read. A reaction is its reactor's ownsys_comment_reactionrecord, which landed in PR #22603. No stored reaction is migrated.What changed
packages/plugins/plugin-audit/src/objects/sys-comment.object.ts: thereactionsfield is gone. The docblock records the retirement, its reason and what an existing database keeps. The file surface is the one the claim names; the producer is this declaration, nothing else.plugin-audit/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts) drop the field's label. They were regenerated withnode scripts/check-i18n-bundles.mjs --write, not edited by hand. Only the plugin-audit bundles moved.packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts, on the protocol-18 step. This is a platform-object column, not an authorable spec key. So nothing goes intoRETIRED_KEYS_BY_MAJOR(no spec schema ever declared the key) and there is no liveness-ledger row. That is the shapeposition-permissions-column-retired,sys-job-organization-column-retiredandsys-audit-log-organization-column-retiredalready use. The gates decided this.check-adr-0087-registration --base origin/mainprinted:✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. .changeset/22573-sys-comment-reactions-retired.md [BREAKING+bang+clause-②-narrowing] registered sys-comment-reactions-column-retired (new here: sys-comment-reactions-column-retired).check:spec-changes,check:upgrade-guide,check:api-surfaceandcheck:authorable-surfaceare all green, and no generated spec artifact moved..changeset/22573-sys-comment-reactions-retired.mdcovers@objectstack/plugin-auditand@objectstack/spec, bothminor. Theminorfollows the launch-window convention thatcheck-changeset-no-majorenforces, and the spec bump carries the new registry entry, which ships in@objectstack/spec/migrations. The changeset states that existing emoji reactions in the column are gone after the upgrade and that reactions now live insys_comment_reaction. It gives a FROM → TO table and an operator sentence built from the measurements below.Measured: what the data door answers a write that still names the field
Showcase booted with
pnpm dev -- --fresh -p PORT --database file:SCRATCH/react.db(SqlDriver(better-sqlite3)), signed in as the seeded admin, REST data door:45e206e49)POST /api/v1/data/sys_commentwithreactions201, map stored and returned400{"error":"Unknown field 'reactions' on object 'sys_comment'","code":"INVALID_FIELD","field":"reactions","object":"sys_comment"}, nothing writtenPATCH /api/v1/data/sys_comment/IDwithreactions200, map overwritten400 INVALID_FIELD, same bodyGET /api/v1/data/sys_comment?reactions=x400 INVALID_FIELDGET /api/v1/data/sys_comment/ID(row holding a stored map)reactionsreturned200, noreactionskeyPOSTwithoutreactions201201The after-reading is a loud refusal, not a silent drop. It is the engine's undeclared-field preflight, which runs before any hook or driver. The door's handling of unknown fields is unchanged.
Measured: what happens to the stored column (SQL driver)
The after-boot ran on the same sqlite file as the before-boot, which had a
reactionscolumn holding a stored map:PRAGMA table_info('sys_comment')after the boot. The column still holds the map written before the change.[schema-drift] sys_comment.reactions: column exists in the database but not in metadata (orphaned) — "os migrate apply --allow-destructive" to drop it., and no error.os migrate planwith an auth secret set lists✗ sys_comment.reactions [drop_column]as destructive, andos migrate apply --allow-destructive --yesdrops it (✓ Applied 1 change(s)., column absent on re-read). Without an auth secret the plan composes no auth family, the audit plugin included. It then listssys_commentunder "declared by no object in this plan" and plans no drop. The plan prints that coverage gap itself, and the changeset's operator sentence says to run it with the audit plugin composed.Census (re-done at
45e206e49)sys-comment.object.tsand labelled only in the four bundles.packages/qa/dogfood: 0 hits.examples/: 0 hits.content/docs: no page names the column; the hits areenableReactions, a UI prop, and the word "reactions" in prose.packages/spec/src/api/discovery.test.ts:688iscapabilities.comments.features.reactions, a discovery capability flag in a fixture. It is not about this column.packages/spec/scripts/liveness/key-mention.test.ts:177uses the English word ("the two reactions available to the seat") in a comment about a ratchet. It is not about this column.docs/adr/0052-audit-is-not-the-activity-feed.md:178listsreactionsamong the fieldssys_commentdeclared when that ADR was written. That is historical ADR text on a governed surface, and it is not edited here.Pins
plugin-audit/src/objects/sys-comment-retired-fields.test.ts, new block "reactions stays retired":sys_commentdeclares noreactionsfield, and no index, highlight or title names it.MIGRATIONS_BY_MAJOR[18].semanticholdssys-comment-reactions-column-retired, itssurfacestartssys_comment.reactions, and itsreplacementnamessys_comment_reaction.sys_comment_reactiondeclarescomment_id,emojianduser_id.plugin-audit/src/sys-comment-reactions-door.integration.test.tsruns on a real engine with the realAuditPluginand the sqlite-wasm driver. A create and an update namingreactionsare refused:mapDataErrorgives400withcodeINVALID_FIELDandfieldreactions. No row is written, and the update's other key is not applied either. The control is the same create withoutreactions, which succeeds.330fc84c1. I rannode scripts/ablation-replace.mjs(wrap mode, plus a shell trap that restores fromHEAD), puttingreactions: Field.textarea(...)back into the object. Anchor 1 → 0, blob5364816272be→30c2ae8114be. Three tests went red:× sys_comment declares no reactions field, and no index, highlight or title names it—AssertionError: sys_comment.reactions was retired — … expected [ 'id', 'thread_id', …(11) ] to not include 'reactions'× create: 400 INVALID_FIELD naming reactions …—a create carrying reactions was accepted: expected undefined to be defined× update: 400 INVALID_FIELD naming reactions …— same5364816272beequals the blob at HEAD,git diff HEADis empty, and an independentgit hash-objectagrees.4997995022(6112978539on plugin-security: a member cannot react to another user's comment — reactions are stored on the author's sys_comment row, and the created_by update floor refuses the write (403) #22500):RecordDetailView.tsxuses therecordsstore whenever the deployment declaressys_comment_reaction, which this framework does. I did not invent a harness for it.Tests and gates (all at
f2825700c, the final commit)pnpm --filter @objectstack/plugin-audit test: 47 files, 743 tests passed.pnpm --filter @objectstack/plugin-audit typecheck: exit 0, test layer included (check:test-typecheck: OK).@objectstack/spec:vitest run --project local648 files / 19375 passed (1 todo);--project repo55 files / 971 passed. Spec and plugin-audit were rebuilt before the runs.node scripts/pm/dispatch-gates.mjs --commands(9 paths vs merge base45e206e49) derived 91 commands. All 91 ran and exited 0.--ranreconciliation:✓ dispatch-gates --ran: 91 derived famil(ies) accounted for — 91 run, 0 NOT-MEASURED.origin/mainhas moved past the base:3f694715e,63739521d,3fdb92db0,5ce6773a5,20028dfff. None touchesplugin-audit. The only spec file among them is a different migration entry, so I did not merge; the merge ref CI builds covers the join.Acceptance notes
.changeset/22566-comment-reaction-record.md(from PR feat(plugin-audit): sys_comment_reaction, the reactor's own record of an emoji reaction #22603, not yet consumed:.changeset/pre.jsonlists no consumed changesets) ends with "sys_comment.reactionsis unchanged in this release … It is retired separately". Both changesets are pending for the same release. I first corrected that bullet in place.check-empty-changesetthen refused, as designed for somebody else's release note: "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 the base (blobd5ba5fd69f4f). This PR's own changeset now says it supersedes that line. Whether to correct the sibling note too, with the gate red until a person confirms, or in a separate docs-only change, is the seat's call. The one-line replacement would be: "sys_comment.reactionsis retired by a separate change on this line, which carries its own entry. Reactions stored in that column are not converted into records."sys_comment_reactiongrant (read, create, delete). That was handed to therepo:hotcrmseat in6094633923and is not part of this PR.sys_comment_reaction, its access hooks, its own-record floor, the author-only edit rule onsys_comment, or objectui.Generated by Claude Code