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
v18 upgrade drops stored comment reactions (9963322fe8 does not migrate sys_comment.reactions) — under the maintainer's rule an upgrade keeps data #23281
Filed by the repo:cloud#1 seat (seat post #6026, session session_01WVbr5J6u8BHh8EyFtcWciH). Source: the cloud upgrade rehearsal objectstack-ai/cloud#2867 6147243232 (item I9), measured on a tenant kernel at 01a3421f42. It affects cloud's v18 upgrade (objectstack-ai/cloud#2709), so it is filed priority:p1, target:v18.
What happens
Data is lost: a comment with reactions stored at v17 reads reactions: null at v18, and the column is orphaned ([schema-drift]).
This is deliberate: commit 9963322fe8 says "stored reactions are not migrated".
Why it is filed
The maintainer's rule for upgrades, 2026-10-13 (objectstack-ai/cloud#2843 6145481762): 「我没有要求清库,这个肯定不行啊,以后不可能每次升级都清库啊」. An upgrade keeps existing data. Reactions are user data that a v18 upgrade silently drops.
Ask
A conversion of stored sys_comment.reactions into v18's reaction storage, in the v18 upgrade (the ceremony or a stored-row replay).
Or bring "accepted loss" to the maintainer explicitly. The seat does not decide that it is acceptable.
Triage: first grade, bug · priority:p1 · target:v18 · domain:services, into pm:queue (finding removed). The v18 upgrade carries stored sys_comment.reactions into sys_comment_reaction rows: an upgrade keeps data
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-13T14:23Z. ⛔ Not a claim, ⛔ not a dispatch.
Not a decision: the maintainer's rule for upgrades (objectstack-ai/cloud#2843 6145481762) is that an upgrade keeps existing data. That supersedes the commit's "not migrated". The card's second option, bringing an accepted loss to the maintainer, is therefore not needed.
Why p1 target:v18: user data is lost silently on every upgraded install, and cloud's v18 upgrade (objectstack-ai/cloud#2709) runs into it.
Lane:domain:services, the lane that retired the column, which owns plugin-audit and the meaning of both stores. The converter's home (a gated os migrate step, or the ceremony's stored-row pass) is the seat's call, declared in the claim.
Direction:
Each stored reaction becomes one sys_comment_reaction row, with the same comment, user and emoji, and is never duplicated on a re-run (idempotent).
A stored value the converter cannot read is listed in its report, ⛔ never dropped silently.
The column is dropped only after the conversion is verified, as the ceremony does with its mirrors.
The conversion ships with its own changeset, which says what the upgrade now carries.
Pins: a v17-shaped row with reactions reads them back at v18; a re-run adds nothing; an unreadable value appears in the report.
Clause ②: no. A conversion of stored rows widens nothing.
Triage: lands in plugin-audit's upgrade conversion (home declared in the claim) (domain:services); rationale: an upgrade keeps data.
Claim: PM loop round 11 · 2026-10-13T16:42Z
Session: session_01FzQy26KznSa5j2gnf3AzsE
Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-23281-comment-reactions-upgrade-conversion
Worktree: objectstack-issue-23281
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface:
The conversion: stored sys_comment.reactions values become sys_comment_reaction rows through a gated os migrate data step, on the pattern of os migrate audit-metadata-bodies / value-shapes (the sys_migration ledger, the journal, resume). That is one new subcommand under packages/cli/src/commands/migrate/, its registration if the CLI needs one, and its tests.
packages/plugins/plugin-audit/: only where the conversion needs the reaction object's own shape, or a docblock that names the retirement.
The semantic entry's prose:packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts. Its text says stored reactions "are not converted into records", and its prescription drops the column before any conversion could run. It is updated together with the upgrade guide it feeds, regenerated by the repo's own tool.
{
"issue": 23281,
"status": "done",
"branch": "claude/issue-23281-comment-reactions-upgrade-conversion",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23318",
"session": "session_01FzQy26KznSa5j2gnf3AzsE — the dispatching seat's session (mode:subagent)",
"premise_still_valid": true,
"summary": "New gated data subcommand `os migrate comment-reactions` (packages/cli) reads sys_comment.reactions below the data door (the account-issuer driver read) and writes one sys_comment_reaction row per stored (comment, emoji, user) in the comment's organization with created_by = the reactor, skipping rows already held (natural key), listing every unreadable value with its comment id (not_json, not_a_map, not_a_user_list, not_a_user_id, emoji_not_storable, unknown_user) and recording a sys_migration row (sys-comment-reactions-to-records) verified only when a read-back finds everything readable converted and nothing unreadable. `os migrate apply --allow-destructive` refuses the sys_comment.reactions drop (exit 1, refused_comment_reactions_unconverted) until that verified row exists, a column-specific sibling of the sys_account.issuer pre-flight. The semantic entry's replacement/reason/acceptanceCriteria prose names the conversion and orders it before the drop (id and surface kept; upgrade guide regenerated with gen:upgrade-guide into the gitignored docs tree), plus the plugin-audit docblock, cli.mdx and one changeset (cli minor, spec patch). Hypotheses: H1-H3 hold; H4 measured (no notification/automation fires; system write keeps user_id; created_by carried so the reactor can un-react, measured DELETE 200); H5 partly falsified (audit-metadata-bodies has no ledger row and no journal; no journal used, idempotent re-run is the resume); H6 falsified as stated (files-to-references' refusal is internal to that command) and landed through the existing mechanism that does hold a drop inside apply, the account-issuer pre-flight, reading the ledger row rather than a live probe so a later un-react is neither blocking nor resurrected.",
"tests": "All at HEAD bf15e3062a (after merging origin/main 5c9bfb0d3f): build `turbo run build --filter=!@objectstack/docs --concurrency=2` 74 tasks green; `pnpm --filter @objectstack/cli typecheck` clean + `check:test-typecheck: OK` (3 files/28 errors/6 signatures, unchanged ledger); cli integration `vitest run --project integration` on comment-reactions.integration.test.ts + apply.account-issuer-retirement.integration.test.ts + schema-migrate.one-shot-family.integration.test.ts: 3 files, 99 passed (new cases 0.1-0.65 s each); cli unit tier `vitest run --project unit`: 286 files, 4288 passed; spec `pnpm test`: 669 files, 20276 passed, 1 todo; plugin-audit sys-comment-retired-fields + objects + sys-comment-reactions-door: 3 files, 22 passed; eslint --no-inline-config --format json on the 8 changed .ts files: 0 errors 0 warnings (no type-aware linting in eslint.config.mjs, so untouched files cannot move). Booted composition before the pins (pnpm dev:todo, SqlDriver better-sqlite3, branch f5db79977e; 17.x column + 4 v17-shaped values planted by hand): apply --allow-destructive before conversion exit 1 refused, column kept; dry run 4 scanned / 5 readable / 3 unreadable, 0 rows; --apply 5 rows (created_by + organization_id correct), ledger blocking 3; re-run 0 converted / 5 already; drop refused; operator resolves 2 values, --apply verified exit 0; drop exit 0, column gone, 5 rows kept; reboot: no schema-drift line, REST reads 5 rows, admin DELETE of its converted reaction 200. Ablations (fix committed first; scripts/ablation-replace.mjs under the verify lock with trap restore; subject imported from cli src by relative path, so no dist rebuild in the path): (1) idempotency guard `if (held.has(reactionKey(...)))` → `if (false && held.has(...))`, anchor 1→0, blob 1c075685b2e1→dd02d298f4c0: 5 failed/2 passed, re-run pin got alreadyConverted 0 and 5 DUPLICATE_RECORD failures; (2) parse-level `report.unreadable.push(...)` deleted, blob →75af39952334: 3 failed/4 passed, unreadable list missing not_json/not_a_user_id/not_a_user_list. Both restored: blob == HEAD 1c075685b2e1, git diff HEAD empty.",
"gates": "dispatch-gates --commands (no paths) at bf15e3062a: 118 families from 10 changed paths (identical set to the derivation at a1b2e9999f and a superset of the PM lead); all 118 run, each exit 0; `--ran` with exit codes: '118 derived famil(ies) accounted for — 118 run, 0 NOT-MEASURED (a DERIVED zero — all 118 recorded an exit code and none of them is 3)'. Includes check:upgrade-guide, check:spec-changes, check:doc-authoring, check:tenant-audit-census (225 sites certified), check:nul-bytes (no raw control bytes), check:adr-0087-registration (no declared-breaking changeset). CI convergence not awaited.",
"line_budget": "not applicable: no skills/** file and no governed line-ratchet ledger touched",
"files_changed": [
".changeset/23281-comment-reactions-upgrade-conversion.md",
"content/docs/deployment/cli.mdx",
"packages/cli/src/commands/migrate/apply.ts",
"packages/cli/src/commands/migrate/comment-reactions.ts",
"packages/cli/src/commands/migrate/comment-reactions.integration.test.ts",
"packages/cli/src/utils/comment-reactions-conversion.ts",
"packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts",
"packages/cli/test/json-stdout-purity.e2e.test.ts",
"packages/plugins/plugin-audit/src/objects/sys-comment.object.ts",
"packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts"
],
"deviations": [
"H6 landed in packages/cli/src/commands/migrate/apply.ts as a column-specific refusal beside the sys_account.issuer pre-flight (the existing mechanism that holds one destructive drop inside apply); the claim's file surface did not list apply.ts. Every other drift entry, flag and exit path of apply is unchanged, which I read as not a change to apply's general behaviour; if the seat reads it otherwise, the gate is one hunk plus pins 1/5/7.",
"File surface beyond the claim's list: packages/cli/src/utils/comment-reactions-conversion.ts (oclif's commands glob loads every module under commands/, so the module shared by the new command and apply.ts lives in utils/); the two derived-population family tables (schema-migrate.one-shot-family.integration.test.ts and json-stdout-purity.e2e.test.ts, which red on an unregistered bootSchemaStack caller); content/docs/deployment/cli.mdx (docs for the new command).",
"No migration journal (H5): the idempotent natural-key re-run is the resume, the audit-metadata-bodies posture.",
"Booted measurement used a database created by main with the 17.x column and values planted by hand (ALTER TABLE ADD COLUMN reactions TEXT + v17-shaped JSON), not a database booted by a 17.x release.",
"Labels: zero label writes — the dispatch names no label and skip-changeset does not apply (cli and spec publish).",
"The harness reminder asked for a commit trailer naming a model; AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) was used instead, as the pre-push hook requires.",
"test/json-stdout-purity.e2e.test.ts (nightly tier) gained the new member but was not run locally; declared to the nightly run."
],
"mcp_calls": "0",
"api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches each): stroke 1 executed POST /repos/objectstack-ai/objectstack/pulls (draft #23318) + POST /repos/objectstack-ai/objectstack/issues/23318/assignees (zhuangjianguo), read back byte-identical; stroke 2 executes this POST /repos/objectstack-ai/objectstack/issues/23281/comments. git push is not REST.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — os migrate apply --json emits its two sys_account.issuer pre-flight refusals (refused_account_issuer_collisions, refused_account_issuer_preflight_unreadable) with exit code 0 (emitJson(..., 0) then return in apply.ts); read from the code, not measured at the door, so it stays in the PR's Acceptance notes",
"carrier: 承接者:无 · noted, not filed — docs/qa/platform-checklist/areas/cli.json's migrate item still enumerates 13 os migrate subcommands; the tree registers more (organization-ownership, security-catalog-overlays, comment-reactions); a checklist-author sweep owns it"
]
}
Claim amendment · domain:services seat 2 · session_01FzQy26KznSa5j2gnf3AzsE · 2026-10-13T18:44Z, under claim 6150488591. ⛔ Not a new claim. Same branch, same PR #23318.
The seat's review of PR #23318 at bf15e3062a against the dev's report (6152601447) sends one patch round.
packages/cli/src/utils/comment-reactions-conversion.ts, where the module shared with apply.ts lives, because oclif loads every module under commands/.
The two derived-population family tables, content/docs/deployment/cli.mdx, and packages/cli/test/json-stdout-purity.e2e.test.ts.
Grows by one defect the round touches (same file, same refusal family): under --json, the two sys_account.issuer pre-flight refusals in apply.ts emit with exit code 0 and return (refused_account_issuer_preflight_unreadable, refused_account_issuer_collisions). The human path exits 1, and the new sibling exits 1. They are aligned to 1 in this PR, with a pin.
Grows by one stale list: the migrate item of docs/qa/platform-checklist/areas/cli.json lists 13 subcommands. It is brought to the subcommands the tree registers, comment-reactions included.
Stored shape, confirmed by the seat: the console's reader of the old column store (objectstack-ai/objectui packages/app-shell/src/views/RecordDetailView.tsx, storedReactions(): Record<string, string[]>) uses a JSON map of emoji to user-id lists, the shape the converter parses.
os-dev-report
Patch round 1 on PR #23318 (review at bf15e3062a, against report 6152601447). Head 42b3638b94.
{
"issue": 23281,
"status": "done",
"round": "patch round 1 on PR #23318 (review at bf15e3062a against report 6152601447; claim amendment 6152677078)",
"branch": "claude/issue-23281-comment-reactions-upgrade-conversion",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23318",
"head": "42b3638b94",
"session": "session_01FzQy26KznSa5j2gnf3AzsE — the dispatching seat's session (mode:subagent)",
"premise_still_valid": true,
"summary": "All four asks are done; nothing else was touched. (1) The changeset now follows the narrowing convention set by #23142: `minor` kept, a bang summary `feat(cli)!:`, a **BREAKING** (accept-set narrowing) banner, the line `Clause-②: no (narrowing)` with its reason, and the ADR-0087 disposition `not-required (already-registered sys-comment-reactions-column-retired)`. `pnpm check:adr-0087-registration` accepts it. The changeset also states the issuer exit-code fix. (2) The defect was measured at the real door before any change, with `node packages/cli/bin/run.js migrate apply --allow-destructive --yes` on legacy-shaped SQLite databases and an auth secret set. Collisions (two rows sharing `(provider_id, account_id)`): `--json` gave `refused_account_issuer_collisions` with exit 0, while the human face exited 1. Unreadable (200001 rows, past the probe's 200000-row cap): `--json` gave `refused_account_issuer_preflight_unreadable` with exit 0, while the human face exited 1. Both `emitJson` calls now exit 1. The new pin file `apply.account-issuer-json-refusal.test.ts` (unit tier) asserts exit 1 and `message` for each refusal, and that nothing was written. It has two controls: the human face exits 1, and a clean pre-flight reaches the writes with exit 0. (3) In `docs/qa/platform-checklist/areas/cli.json`, the `cli.migrate-plan-apply-json` item now lists the 16 subcommands that `os migrate --help` registers. organization-ownership, security-catalog-overlays and comment-reactions were added, the source count changed from 13 to 16, and revision 4 has a history entry. (4) Re-verified at 42b3638b94 (see tests/gates).",
"tests": "At 42b3638b94. `pnpm --filter @objectstack/cli typecheck`: clean, plus check:test-typecheck: OK (ledger unchanged). cli integration on comment-reactions.integration.test.ts, apply.account-issuer-retirement.integration.test.ts and schema-migrate.one-shot-family.integration.test.ts: 3 files, 99 passed. cli unit tier: 287 files, 4292 passed; the new pin file alone gives 4 passed, with first-case wall 4.9 s for oclif's command-table load and the others 0.03–0.05 s. Ablation for item 2 (fix committed first as 87300afdff; trap restore; both sites mutated in apply.ts with scripts/ablation-replace.mjs). Unreadable site: `}, 1,` changed to `}, 0,` with --hold, anchor 1→0, blob 4fe54313fc9f→421f3eeb1114. Collisions site: same change in WRAP mode under the verify lock, anchor 1→0, blob →3b37bf6b7d56. Result: 2 failed / 2 passed. Both new pins were red with `AssertionError: expected +0 to be 1`; both controls stayed green. Restored: `ok restored: blob == HEAD (4fe54313fc9f) and git diff HEAD is empty`, and git status --porcelain was empty.",
"gates": "Ran `dispatch-gates --commands` (no paths) at 42b3638b94: 12 changed paths, 119 families (last round's 118 plus `pnpm check:platform-checklist`, now derived because cli.json is touched). All 119 were run. 118 exited 0. `pnpm check:platform-checklist` exited 1 with 8 failures, none of them in cli.json: ABSENT SYMBOL anchors in areas/access-security.json (metadata-protocol protocol.ts anonymousFormIntakeOrgScopeRefusal, anonymousFormIntakeReopenRefusal, envWideRawViewRows), areas/search.json (protocol.ts countable) and areas/attachments-storage.json (attachment-access-hooks.ts canEdit; anchor floor 28→27). Control: the same command at 87300afdff, with cli.json put back from HEAD by a patch round-trip, gave the identical 8 failure lines. The cited symbols are also absent at origin/main 404fc477e5. By the maintainer's decision recorded in lint.yml, this gate is not in per-PR CI; its daily watchdog on main owns it. `--ran` with exit codes: '119 derived famil(ies) accounted for — 119 run, 0 NOT-MEASURED (a DERIVED zero — all 119 recorded an exit code and none of them is 3)'. Changeset gates: `✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. … [BREAKING+bang+clause-②-narrowing] not-required (already-registered)`; `✓ This diff introduces no major bump`; `✓ No empty-frontmatter changeset introduced by this diff`.",
"line_budget": "not applicable: no skills/** file and no governed line-ratchet ledger touched",
"files_changed": [
".changeset/23281-comment-reactions-upgrade-conversion.md",
"docs/qa/platform-checklist/areas/cli.json",
"packages/cli/src/commands/migrate/apply.ts",
"packages/cli/src/commands/migrate/apply.account-issuer-json-refusal.test.ts"
],
"deviations": [
"The issuer pins are unit-tier and replace the boot, the occupancy probe, the process exit and the pre-flight probe. The command's own parse, drift handling and refusal branches run for real. The real-door reading for both refusals is the measurement quoted in summary (2). A real-database pin for the unreadable refusal would need 200001 rows per run.",
"The ADR-0087 disposition is an HTML comment in the changeset, because that is the only marker spelling the registration gate reads. The changeset is a repo file and does not render on GitHub.",
"origin/main was not merged this round. It moved one commit (404fc477e5), which touches none of this PR's files.",
"check:platform-checklist is red for pre-existing reasons outside this diff (see gates)."
],
"mcp_calls": "0",
"api_writes": "1 relay stroke this round: POST /repos/objectstack-ai/objectstack/dispatches, which executes POST /repos/objectstack-ai/objectstack/issues/23281/comments (this report). 2 git pushes (87300afdff; af8fdf9903 + 42b3638b94). No PR body or title write, no label write, no MCP write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the platform-checklist watchdog on main (承接者:无 beyond it) · noted, not filed — check:platform-checklist is red at origin/main 404fc477e5 on 8 symbol anchors in access-security.json, search.json and attachments-storage.json whose symbols are gone from the code"
]
}
Seat ACCEPT: PR #23318 at 42b3638b94 · Fixes #23281
domain:services seat 2 · session_01FzQy26KznSa5j2gnf3AzsE · 2026-10-13T19:40Z. Under claim 6150488591 (its Clause-② line edited in place) and amendment 6152677078. Declared to domain:cli (6150536077 on #22648) and domain:spec (6150545862 on #6017), with no objection. The dev's reports are 6152601447 and 6153407686 (patch round). Checked on GitHub and on the branch, not from the reports.
Checklist
PR form: draft, base main, assignee zhuangjianguo. Title feat(cli)!: …. Line 1 Fixes #23281, line 2 Clause-②: no (narrowing) — …, which the claim, the changeset and the PR all agree on. The seat wrote the title, line 2 and the patch-round section through the relay.
Scope: 12 files, +1521 / -21. check-governed-merges --pr 23318: NOT governed (0 of 12 paths), 1542 lines, under the threshold. The surface is the claim's plus the amendment's declared deviations:
apply.ts (column-specific pre-flight and the issuer exit-code fix);
the utils/ conversion module;
two family tables, cli.mdx, the e2e member and the QA checklist item.
os migrate comment-reactions reads sys_comment.reactions below the data door. A preview is the default, and --apply writes.
Records: each stored (comment, emoji, user) becomes one sys_comment_reaction row in the comment's organization, with created_by = the reactor, under the bookkeeping write context (isSystem + skipAutomations).
Re-runs: idempotent on the natural key.
Unreadable values: each is listed with its comment id under six named reasons, and nothing is written for it.
Ledger: the run is recorded in sys_migration (sys-comment-reactions-to-records), verified only when the read-back finds everything converted.
The drop:os migrate apply --allow-destructive refuses the sys_comment.reactions drop (exit 1, refused_comment_reactions_unconverted) until that verified row exists.
The semantic entry names the conversion and orders it before the drop (prose only; id and surface kept).
The defect the round touched: under --json, the two sys_account.issuer refusals exited 0 while the human face exited 1. That was measured at the real command, both now exit 1, and they are pinned with two controls.
Stored shape: confirmed against objectui's reader of the old column store (RecordDetailView.tsx, storedReactions(): Record<string, string[]>), the shape the converter parses.
Tests and ablations:
a booted composition measured before and after (refusal → preview → apply → re-run adds 0 → verified → drop → reactor un-reacts, DELETE 200);
three ablations, each red then restored with blob proof: the idempotency guard (5 red), the unreadable report (3 red), and the issuer exit code (2 red);
cli integration 99 passed and cli unit 4292 passed.
Gates: 119 derived and 119 run at 42b3638b94. 118 exit 0. check:platform-checklist exits 1 on 8 anchors outside this diff, with the identical lines on main without this PR's edit.
Contract review: PASS 6153659174 at 42b3638b946bdbbf42e5d5d9b1b87c7c7fc99f3e (CONTRACT_REVIEW_TIER, Local-runs: none), so no waiting label is owed.
CI at 42b3638b94: 48 check-runs, the latest run per name all success or skipped (41 / 7). None red. Mergeable.
Changeset:
@objectstack/climinor with a bang summary, a BREAKING (accept-set narrowing) banner and ADR-0087 not-required (already-registered sys-comment-reactions-column-retired); check:adr-0087-registration is green.
@objectstack/specpatch (prose).
The changeset prose I checked sentence by sentence against the diff: the upgrade order (backup, preview, --apply, correct or clear each unreadable value, then the drop) and the refusal's message match the code.
Writes:mcp_calls 0. The commit trailers carry no model identifier.
The review's two escalations, answered:
A database written by a 17.x release itself has not been read through the command. The booted run planted 17.x-shaped values by hand. The seat accepts the reading as it stands, because the shape is confirmed from the console's own reader, and an unexpected shape is reported and blocks the drop rather than losing data. The release-written reading belongs to cloud's upgrade rehearsal (objectstack-ai/cloud#2867, item I9, which measured the loss). The landing note points it there.
check:platform-checklist is red on main. The carrier is the gate's own channel: .github/workflows/platform-checklist-watchdog.yml runs it on main daily and files or refreshes one platform-checklist-watchdog issue while it is red. check:platform-checklist is red on main #23139 was the latest. The seat files nothing beside it.
Out-of-scope findings: none filed beyond the above.
Landing:pr_ready + automerge_enable through the relay, now. This seat follows it to MERGED.
Landed · PR #23318 merged through the merge queue at 2026-10-13T20:09Z as 6673c8018f, and that commit is on origin/main (read 2026-10-13T20:10Z). domain:services seat 2 · session_01FzQy26KznSa5j2gnf3AzsE. It ran under claim 6150488591 and amendment 6152677078, with contract review PASS 6153659174 and ACCEPT 6153697508.
On main:
os migrate comment-reactions carries the reactions a 17.x deployment stored in sys_comment.reactions into sys_comment_reaction records. It is idempotent, reports unreadable values and records a verified run in sys_migration.
os migrate apply --allow-destructive drops the column only after that verified run.
The protocol-18 semantic entry orders the conversion before the drop.
Under --json, the two sys_account.issuer refusals now exit 1.
Queue path:ready_for_review, then auto_merge_enabled, then added_to_merge_queue at 2026-10-13T19:43Z. The PR's only closing keyword was Fixes #23281. No other card closed in the window.
Board: this card closed completed by that keyword. This write clears pm:dispatched and the assignee; priority:p1, target:v18, domain:services and area:records stay.
For cloud's upgrade (objectstack-ai/cloud#2709): the rehearsal that measured the loss (objectstack-ai/cloud#2867, item I9) can re-read it once its framework pin carries this commit. A database written by a 17.x release itself is that rehearsal's reading; the PR's booted run planted 17.x-shaped values. The order is backup, os migrate comment-reactions, then --apply, then the destructive apply.
Publication:.changeset/23281-comment-reactions-upgrade-conversion.md (@objectstack/climinor, BREAKING narrowing; @objectstack/specpatch) ships at the next version release. A merge to main does not publish.
Activity
objectstack-fleet commentedon Oct 13, 2026
Triage: first grade,
bug·priority:p1·target:v18·domain:services, intopm:queue(findingremoved). The v18 upgrade carries storedsys_comment.reactionsintosys_comment_reactionrows: an upgrade keeps dataTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T14:23Z. ⛔ Not a claim, ⛔ not a dispatch.9963322fe8(feat(plugin-audit)!: sys_comment.reactions retires; a reaction lives only in sys_comment_reaction, and stored reactions are not migrated #22903, card Retire sys_comment.reactions (ruling A amended on #22505): no aggregate, no data migration, after the console reads reaction records #22573) retiredsys_comment.reactionsand says "stored reactions are not migrated". The cloud rehearsal measured the loss on a tenant kernel (objectstack-ai/cloud#28676147243232, item I9):reactions: nullat v18, with the column left as[schema-drift].6145481762) is that an upgrade keeps existing data. That supersedes the commit's "not migrated". The card's second option, bringing an accepted loss to the maintainer, is therefore not needed.target:v18: user data is lost silently on every upgraded install, and cloud's v18 upgrade (objectstack-ai/cloud#2709) runs into it.domain:services, the lane that retired the column, which ownsplugin-auditand the meaning of both stores. The converter's home (a gatedos migratestep, or the ceremony's stored-row pass) is the seat's call, declared in the claim.sys_comment_reactionrow, with the same comment, user and emoji, and is never duplicated on a re-run (idempotent).plugin-audit's upgrade conversion (home declared in the claim) (domain:services); rationale: an upgrade keeps data.objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round 11 · 2026-10-13T16:42Z
Session:
session_01FzQy26KznSa5j2gnf3AzsEAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23281-comment-reactions-upgrade-conversionWorktree:
objectstack-issue-23281Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface:
sys_comment.reactionsvalues becomesys_comment_reactionrows through a gatedos migratedata step, on the pattern ofos migrate audit-metadata-bodies/value-shapes(thesys_migrationledger, the journal,resume). That is one new subcommand underpackages/cli/src/commands/migrate/, its registration if the CLI needs one, and its tests.packages/plugins/plugin-audit/: only where the conversion needs the reaction object's own shape, or a docblock that names the retirement.packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts. Its text says stored reactions "are not converted into records", and its prescription drops the column before any conversion could run. It is updated together with the upgrade guide it feeds, regenerated by the repo's own tool..changeset/23281-*.md.packages/cliisdomain:cli(declared on [PM seat] domain:cli · seat 2 — 🟢 sunhaolin · session_01Q9PZ5Pzqcus4woVvTZwWNU · p1+p2, batch 3 (maintainer) · landed 13 · in flight 3 (#23562 #23555 #23573) #22648) and the semantic entry isdomain:spec(declared on [PM seat] domain:spec — ⏳ vacant #6017).sys_comment_reaction's schema, access hooks or floor. ⛔ No change toorganization-ownership.ts(C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937 / PR feat(cli): os migrate organization-ownership --apply — the ADR-0131 D10 ceremony (C7b, PR 2 of 3) #23034) or to the general behaviour ofos migrate apply(stop on breach; explain in the report).Container & model:
M (one gated data step, idempotent, with a report of unreadable values and a guide update),mode:subagent,model: default — dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate … The tier stays the PM's per-card judgment call"Clause-②: no (narrowing) — converting stored rows into an existing object widens no accept set, and a gated data subcommand follows the
os migrate audit-metadata-bodiesprecedent ([security] Family closeout: stored metadata bodies (stored datasource credential material included) are still reachable outside the redacting doors at several further positions — detail withheld pending maintainer #21120, graded no). The narrowing:os migrate apply --allow-destructiverefuses thesys_comment.reactionsdrop until a verified conversion is recorded (amendment below). The semantic entry changes in prose only.Responsibility: the v18 retirement of
sys_comment.reactions(9963322fe8, the platform's own change) left stored reactions unconverted | none: no upgrade step reads the orphaned column, and the guide prescribes dropping it | every install upgraded from 17.x that holds reactions; measured on a cloud tenant kernel (objectstack-ai/cloud#28676147243232, item I9)Thread-read: 6147772435
Serial constraints cleared:
packages/cli/src/commands/migrate/organization-ownership.tsand its test, and this claim touches neither.plugin-audit,packages/cli/src/commands/migrate/or the semantic entry (5 open PRs read at this claim).pm:dispatchedclaim declares these files.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{ "issue": 23281, "status": "done", "branch": "claude/issue-23281-comment-reactions-upgrade-conversion", "pr": "https://github.com/objectstack-ai/objectstack/pull/23318", "session": "session_01FzQy26KznSa5j2gnf3AzsE — the dispatching seat's session (mode:subagent)", "premise_still_valid": true, "summary": "New gated data subcommand `os migrate comment-reactions` (packages/cli) reads sys_comment.reactions below the data door (the account-issuer driver read) and writes one sys_comment_reaction row per stored (comment, emoji, user) in the comment's organization with created_by = the reactor, skipping rows already held (natural key), listing every unreadable value with its comment id (not_json, not_a_map, not_a_user_list, not_a_user_id, emoji_not_storable, unknown_user) and recording a sys_migration row (sys-comment-reactions-to-records) verified only when a read-back finds everything readable converted and nothing unreadable. `os migrate apply --allow-destructive` refuses the sys_comment.reactions drop (exit 1, refused_comment_reactions_unconverted) until that verified row exists, a column-specific sibling of the sys_account.issuer pre-flight. The semantic entry's replacement/reason/acceptanceCriteria prose names the conversion and orders it before the drop (id and surface kept; upgrade guide regenerated with gen:upgrade-guide into the gitignored docs tree), plus the plugin-audit docblock, cli.mdx and one changeset (cli minor, spec patch). Hypotheses: H1-H3 hold; H4 measured (no notification/automation fires; system write keeps user_id; created_by carried so the reactor can un-react, measured DELETE 200); H5 partly falsified (audit-metadata-bodies has no ledger row and no journal; no journal used, idempotent re-run is the resume); H6 falsified as stated (files-to-references' refusal is internal to that command) and landed through the existing mechanism that does hold a drop inside apply, the account-issuer pre-flight, reading the ledger row rather than a live probe so a later un-react is neither blocking nor resurrected.", "tests": "All at HEAD bf15e3062a (after merging origin/main 5c9bfb0d3f): build `turbo run build --filter=!@objectstack/docs --concurrency=2` 74 tasks green; `pnpm --filter @objectstack/cli typecheck` clean + `check:test-typecheck: OK` (3 files/28 errors/6 signatures, unchanged ledger); cli integration `vitest run --project integration` on comment-reactions.integration.test.ts + apply.account-issuer-retirement.integration.test.ts + schema-migrate.one-shot-family.integration.test.ts: 3 files, 99 passed (new cases 0.1-0.65 s each); cli unit tier `vitest run --project unit`: 286 files, 4288 passed; spec `pnpm test`: 669 files, 20276 passed, 1 todo; plugin-audit sys-comment-retired-fields + objects + sys-comment-reactions-door: 3 files, 22 passed; eslint --no-inline-config --format json on the 8 changed .ts files: 0 errors 0 warnings (no type-aware linting in eslint.config.mjs, so untouched files cannot move). Booted composition before the pins (pnpm dev:todo, SqlDriver better-sqlite3, branch f5db79977e; 17.x column + 4 v17-shaped values planted by hand): apply --allow-destructive before conversion exit 1 refused, column kept; dry run 4 scanned / 5 readable / 3 unreadable, 0 rows; --apply 5 rows (created_by + organization_id correct), ledger blocking 3; re-run 0 converted / 5 already; drop refused; operator resolves 2 values, --apply verified exit 0; drop exit 0, column gone, 5 rows kept; reboot: no schema-drift line, REST reads 5 rows, admin DELETE of its converted reaction 200. Ablations (fix committed first; scripts/ablation-replace.mjs under the verify lock with trap restore; subject imported from cli src by relative path, so no dist rebuild in the path): (1) idempotency guard `if (held.has(reactionKey(...)))` → `if (false && held.has(...))`, anchor 1→0, blob 1c075685b2e1→dd02d298f4c0: 5 failed/2 passed, re-run pin got alreadyConverted 0 and 5 DUPLICATE_RECORD failures; (2) parse-level `report.unreadable.push(...)` deleted, blob →75af39952334: 3 failed/4 passed, unreadable list missing not_json/not_a_user_id/not_a_user_list. Both restored: blob == HEAD 1c075685b2e1, git diff HEAD empty.", "gates": "dispatch-gates --commands (no paths) at bf15e3062a: 118 families from 10 changed paths (identical set to the derivation at a1b2e9999f and a superset of the PM lead); all 118 run, each exit 0; `--ran` with exit codes: '118 derived famil(ies) accounted for — 118 run, 0 NOT-MEASURED (a DERIVED zero — all 118 recorded an exit code and none of them is 3)'. Includes check:upgrade-guide, check:spec-changes, check:doc-authoring, check:tenant-audit-census (225 sites certified), check:nul-bytes (no raw control bytes), check:adr-0087-registration (no declared-breaking changeset). CI convergence not awaited.", "line_budget": "not applicable: no skills/** file and no governed line-ratchet ledger touched", "files_changed": [ ".changeset/23281-comment-reactions-upgrade-conversion.md", "content/docs/deployment/cli.mdx", "packages/cli/src/commands/migrate/apply.ts", "packages/cli/src/commands/migrate/comment-reactions.ts", "packages/cli/src/commands/migrate/comment-reactions.integration.test.ts", "packages/cli/src/utils/comment-reactions-conversion.ts", "packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts", "packages/cli/test/json-stdout-purity.e2e.test.ts", "packages/plugins/plugin-audit/src/objects/sys-comment.object.ts", "packages/spec/src/migrations/entries/semantic/18.sys-comment-reactions-column-retired.ts" ], "deviations": [ "H6 landed in packages/cli/src/commands/migrate/apply.ts as a column-specific refusal beside the sys_account.issuer pre-flight (the existing mechanism that holds one destructive drop inside apply); the claim's file surface did not list apply.ts. Every other drift entry, flag and exit path of apply is unchanged, which I read as not a change to apply's general behaviour; if the seat reads it otherwise, the gate is one hunk plus pins 1/5/7.", "File surface beyond the claim's list: packages/cli/src/utils/comment-reactions-conversion.ts (oclif's commands glob loads every module under commands/, so the module shared by the new command and apply.ts lives in utils/); the two derived-population family tables (schema-migrate.one-shot-family.integration.test.ts and json-stdout-purity.e2e.test.ts, which red on an unregistered bootSchemaStack caller); content/docs/deployment/cli.mdx (docs for the new command).", "No migration journal (H5): the idempotent natural-key re-run is the resume, the audit-metadata-bodies posture.", "Booted measurement used a database created by main with the 17.x column and values planted by hand (ALTER TABLE ADD COLUMN reactions TEXT + v17-shaped JSON), not a database booted by a 17.x release.", "Labels: zero label writes — the dispatch names no label and skip-changeset does not apply (cli and spec publish).", "The harness reminder asked for a commit trailer naming a model; AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) was used instead, as the pre-push hook requires.", "test/json-stdout-purity.e2e.test.ts (nightly tier) gained the new member but was not run locally; declared to the nightly run." ], "mcp_calls": "0", "api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches each): stroke 1 executed POST /repos/objectstack-ai/objectstack/pulls (draft #23318) + POST /repos/objectstack-ai/objectstack/issues/23318/assignees (zhuangjianguo), read back byte-identical; stroke 2 executes this POST /repos/objectstack-ai/objectstack/issues/23281/comments. git push is not REST.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed — os migrate apply --json emits its two sys_account.issuer pre-flight refusals (refused_account_issuer_collisions, refused_account_issuer_preflight_unreadable) with exit code 0 (emitJson(..., 0) then return in apply.ts); read from the code, not measured at the door, so it stays in the PR's Acceptance notes", "carrier: 承接者:无 · noted, not filed — docs/qa/platform-checklist/areas/cli.json's migrate item still enumerates 13 os migrate subcommands; the tree registers more (organization-ownership, security-catalog-overlays, comment-reactions); a checklist-author sweep owns it" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Claim amendment ·
domain:servicesseat 2 ·session_01FzQy26KznSa5j2gnf3AzsE· 2026-10-13T18:44Z, under claim6150488591. ⛔ Not a new claim. Same branch, same PR #23318.The seat's review of PR #23318 at
bf15e3062aagainst the dev's report (6152601447) sends one patch round.no (narrowing), and the claim's line is edited in place in this act.os migrate apply --allow-destructivenow refuses thesys_comment.reactionsdrop until a verified conversion is recorded: the same input that was accepted is now refused. That narrows a published accept set (execution-duties.md: no lane move; one contract-review-tier review before the queue). The changeset and the PR follow the lane's narrowing convention (precedent service-settings: the settings REST family (registerSettingsRoutes) answers an anonymous caller from its own authorization logic instead of the 401 floor; ADR-0056 D2 and ADR-0138 D2 require 401 — the fourth door blocking ADR-0138 criterion 2 #23142):minorwith a BREAKING banner, a bang summary and the ADR-0087 disposition the registration gate accepts.packages/cli/src/commands/migrate/apply.ts: one column-specific pre-flight, a sibling of thesys_account.issuerone (Adopt better-auth's account-issuer rollback: drop sys_account.issuer, retire the backfill, raise the family to 1.7.3 #17440). It fires only when the intended drift dropssys_comment.reactions, and every other drift entry, flag and exit path is unchanged. The seat reads this as not a change toapply's general behaviour.packages/cli/src/utils/comment-reactions-conversion.ts, where the module shared withapply.tslives, because oclif loads every module undercommands/.content/docs/deployment/cli.mdx, andpackages/cli/test/json-stdout-purity.e2e.test.ts.--json, the twosys_account.issuerpre-flight refusals inapply.tsemit with exit code0and return (refused_account_issuer_preflight_unreadable,refused_account_issuer_collisions). The human path exits1, and the new sibling exits1. They are aligned to1in this PR, with a pin.migrateitem ofdocs/qa/platform-checklist/areas/cli.jsonlists 13 subcommands. It is brought to the subcommands the tree registers,comment-reactionsincluded.packages/app-shell/src/views/RecordDetailView.tsx,storedReactions(): Record<string, string[]>) uses a JSON map of emoji to user-id lists, the shape the converter parses.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
Patch round 1 on PR #23318 (review at
bf15e3062a, against report6152601447). Head42b3638b94.{ "issue": 23281, "status": "done", "round": "patch round 1 on PR #23318 (review at bf15e3062a against report 6152601447; claim amendment 6152677078)", "branch": "claude/issue-23281-comment-reactions-upgrade-conversion", "pr": "https://github.com/objectstack-ai/objectstack/pull/23318", "head": "42b3638b94", "session": "session_01FzQy26KznSa5j2gnf3AzsE — the dispatching seat's session (mode:subagent)", "premise_still_valid": true, "summary": "All four asks are done; nothing else was touched. (1) The changeset now follows the narrowing convention set by #23142: `minor` kept, a bang summary `feat(cli)!:`, a **BREAKING** (accept-set narrowing) banner, the line `Clause-②: no (narrowing)` with its reason, and the ADR-0087 disposition `not-required (already-registered sys-comment-reactions-column-retired)`. `pnpm check:adr-0087-registration` accepts it. The changeset also states the issuer exit-code fix. (2) The defect was measured at the real door before any change, with `node packages/cli/bin/run.js migrate apply --allow-destructive --yes` on legacy-shaped SQLite databases and an auth secret set. Collisions (two rows sharing `(provider_id, account_id)`): `--json` gave `refused_account_issuer_collisions` with exit 0, while the human face exited 1. Unreadable (200001 rows, past the probe's 200000-row cap): `--json` gave `refused_account_issuer_preflight_unreadable` with exit 0, while the human face exited 1. Both `emitJson` calls now exit 1. The new pin file `apply.account-issuer-json-refusal.test.ts` (unit tier) asserts exit 1 and `message` for each refusal, and that nothing was written. It has two controls: the human face exits 1, and a clean pre-flight reaches the writes with exit 0. (3) In `docs/qa/platform-checklist/areas/cli.json`, the `cli.migrate-plan-apply-json` item now lists the 16 subcommands that `os migrate --help` registers. organization-ownership, security-catalog-overlays and comment-reactions were added, the source count changed from 13 to 16, and revision 4 has a history entry. (4) Re-verified at 42b3638b94 (see tests/gates).", "tests": "At 42b3638b94. `pnpm --filter @objectstack/cli typecheck`: clean, plus check:test-typecheck: OK (ledger unchanged). cli integration on comment-reactions.integration.test.ts, apply.account-issuer-retirement.integration.test.ts and schema-migrate.one-shot-family.integration.test.ts: 3 files, 99 passed. cli unit tier: 287 files, 4292 passed; the new pin file alone gives 4 passed, with first-case wall 4.9 s for oclif's command-table load and the others 0.03–0.05 s. Ablation for item 2 (fix committed first as 87300afdff; trap restore; both sites mutated in apply.ts with scripts/ablation-replace.mjs). Unreadable site: `}, 1,` changed to `}, 0,` with --hold, anchor 1→0, blob 4fe54313fc9f→421f3eeb1114. Collisions site: same change in WRAP mode under the verify lock, anchor 1→0, blob →3b37bf6b7d56. Result: 2 failed / 2 passed. Both new pins were red with `AssertionError: expected +0 to be 1`; both controls stayed green. Restored: `ok restored: blob == HEAD (4fe54313fc9f) and git diff HEAD is empty`, and git status --porcelain was empty.", "gates": "Ran `dispatch-gates --commands` (no paths) at 42b3638b94: 12 changed paths, 119 families (last round's 118 plus `pnpm check:platform-checklist`, now derived because cli.json is touched). All 119 were run. 118 exited 0. `pnpm check:platform-checklist` exited 1 with 8 failures, none of them in cli.json: ABSENT SYMBOL anchors in areas/access-security.json (metadata-protocol protocol.ts anonymousFormIntakeOrgScopeRefusal, anonymousFormIntakeReopenRefusal, envWideRawViewRows), areas/search.json (protocol.ts countable) and areas/attachments-storage.json (attachment-access-hooks.ts canEdit; anchor floor 28→27). Control: the same command at 87300afdff, with cli.json put back from HEAD by a patch round-trip, gave the identical 8 failure lines. The cited symbols are also absent at origin/main 404fc477e5. By the maintainer's decision recorded in lint.yml, this gate is not in per-PR CI; its daily watchdog on main owns it. `--ran` with exit codes: '119 derived famil(ies) accounted for — 119 run, 0 NOT-MEASURED (a DERIVED zero — all 119 recorded an exit code and none of them is 3)'. Changeset gates: `✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. … [BREAKING+bang+clause-②-narrowing] not-required (already-registered)`; `✓ This diff introduces no major bump`; `✓ No empty-frontmatter changeset introduced by this diff`.", "line_budget": "not applicable: no skills/** file and no governed line-ratchet ledger touched", "files_changed": [ ".changeset/23281-comment-reactions-upgrade-conversion.md", "docs/qa/platform-checklist/areas/cli.json", "packages/cli/src/commands/migrate/apply.ts", "packages/cli/src/commands/migrate/apply.account-issuer-json-refusal.test.ts" ], "deviations": [ "The issuer pins are unit-tier and replace the boot, the occupancy probe, the process exit and the pre-flight probe. The command's own parse, drift handling and refusal branches run for real. The real-door reading for both refusals is the measurement quoted in summary (2). A real-database pin for the unreadable refusal would need 200001 rows per run.", "The ADR-0087 disposition is an HTML comment in the changeset, because that is the only marker spelling the registration gate reads. The changeset is a repo file and does not render on GitHub.", "origin/main was not merged this round. It moved one commit (404fc477e5), which touches none of this PR's files.", "check:platform-checklist is red for pre-existing reasons outside this diff (see gates)." ], "mcp_calls": "0", "api_writes": "1 relay stroke this round: POST /repos/objectstack-ai/objectstack/dispatches, which executes POST /repos/objectstack-ai/objectstack/issues/23281/comments (this report). 2 git pushes (87300afdff; af8fdf9903 + 42b3638b94). No PR body or title write, no label write, no MCP write.", "open_questions": [], "out_of_scope_findings": [ "carrier: the platform-checklist watchdog on main (承接者:无 beyond it) · noted, not filed — check:platform-checklist is red at origin/main 404fc477e5 on 8 symbol anchors in access-security.json, search.json and attachments-storage.json whose symbols are gone from the code" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Seat ACCEPT: PR #23318 at
42b3638b94·Fixes #23281domain:servicesseat 2 ·session_01FzQy26KznSa5j2gnf3AzsE· 2026-10-13T19:40Z. Under claim6150488591(itsClause-②line edited in place) and amendment6152677078. Declared todomain:cli(6150536077on #22648) anddomain:spec(6150545862on #6017), with no objection. The dev's reports are6152601447and6153407686(patch round). Checked on GitHub and on the branch, not from the reports.Checklist
main, assigneezhuangjianguo. Titlefeat(cli)!: …. Line 1Fixes #23281, line 2Clause-②: no (narrowing) — …, which the claim, the changeset and the PR all agree on. The seat wrote the title, line 2 and the patch-round section through the relay.check-governed-merges --pr 23318: NOT governed (0 of 12 paths), 1542 lines, under the threshold. The surface is the claim's plus the amendment's declared deviations:apply.ts(column-specific pre-flight and the issuer exit-code fix);utils/conversion module;cli.mdx, the e2e member and the QA checklist item.os migrate comment-reactionsreadssys_comment.reactionsbelow the data door. A preview is the default, and--applywrites.sys_comment_reactionrow in the comment's organization, withcreated_by= the reactor, under the bookkeeping write context (isSystem+skipAutomations).sys_migration(sys-comment-reactions-to-records), verified only when the read-back finds everything converted.os migrate apply --allow-destructiverefuses thesys_comment.reactionsdrop (exit 1,refused_comment_reactions_unconverted) until that verified row exists.--json, the twosys_account.issuerrefusals exited0while the human face exited1. That was measured at the real command, both now exit1, and they are pinned with two controls.RecordDetailView.tsx,storedReactions(): Record<string, string[]>), the shape the converter parses.42b3638b94. 118 exit 0.check:platform-checklistexits 1 on 8 anchors outside this diff, with the identical lines onmainwithout this PR's edit.6153659174at42b3638b946bdbbf42e5d5d9b1b87c7c7fc99f3e(CONTRACT_REVIEW_TIER,Local-runs: none), so no waiting label is owed.42b3638b94: 48 check-runs, the latest run per name allsuccessorskipped(41 / 7). None red. Mergeable.@objectstack/climinorwith a bang summary, a BREAKING (accept-set narrowing) banner and ADR-0087not-required (already-registered sys-comment-reactions-column-retired);check:adr-0087-registrationis green.@objectstack/specpatch(prose).--apply, correct or clear each unreadable value, then the drop) and the refusal'smessagematch the code.mcp_calls0. The commit trailers carry no model identifier.The review's two escalations, answered:
check:platform-checklistis red onmain. The carrier is the gate's own channel:.github/workflows/platform-checklist-watchdog.ymlruns it onmaindaily and files or refreshes oneplatform-checklist-watchdogissue while it is red. check:platform-checklist is red on main #23139 was the latest. The seat files nothing beside it.Out-of-scope findings: none filed beyond the above.
Landing:
pr_ready+automerge_enablethrough the relay, now. This seat follows it to MERGED.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Landed · PR #23318 merged through the merge queue at 2026-10-13T20:09Z as
6673c8018f, and that commit is onorigin/main(read 2026-10-13T20:10Z).domain:servicesseat 2 ·session_01FzQy26KznSa5j2gnf3AzsE. It ran under claim6150488591and amendment6152677078, with contract review PASS6153659174and ACCEPT6153697508.main:os migrate comment-reactionscarries the reactions a 17.x deployment stored insys_comment.reactionsintosys_comment_reactionrecords. It is idempotent, reports unreadable values and records a verified run insys_migration.os migrate apply --allow-destructivedrops the column only after that verified run.--json, the twosys_account.issuerrefusals now exit1.ready_for_review, thenauto_merge_enabled, thenadded_to_merge_queueat 2026-10-13T19:43Z. The PR's only closing keyword wasFixes #23281. No other card closed in the window.completedby that keyword. This write clearspm:dispatchedand the assignee;priority:p1,target:v18,domain:servicesandarea:recordsstay.os migrate comment-reactions, then--apply, then the destructive apply..changeset/23281-comment-reactions-upgrade-conversion.md(@objectstack/climinor, BREAKING narrowing;@objectstack/specpatch) ships at the next version release. A merge tomaindoes not publish.Generated by Claude Code
feat(cli)!: os migrate apply refuses an organization_id drop ahead of…
fix(cli,dogfood): S5 re-review 6180007547 fixes 2 and 3 on the combin…