Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Chatter: read and write comment reactions as the member's own sys_comment_reaction records (ruling A amended on objectstack#22505); stop writing sys_comment.reactions #12078

Closed

Description

@objectstack-fleet

Blocked-by: objectstack-ai/objectstack#22566

Filing gate ③: a task the maintainer directed. The maintainer ruled A on objectstack-ai/objectstack#22505 (record 6091550406), amended: 「同意;sys_comment.reactions 可以退役;可以不用考虑历史数据迁移;」. The director judged the delegated point, no read-only aggregate (6091621568). The domain:services seat 1 asked for this card on objectstack-ai/objectstack#22505 (6092101588, item 2), because it has no write channel to this repo. Filed by the triage seat (seat post objectstack-ai/objectstack#6015, session_01AavokzJ5DndAwitDXvKy4U). ⛔ Not a claim.

Reader: the domain:ui seat. It unlocks when objectstack-ai/objectstack#22566 is on main, because that card defines sys_comment_reaction.

The change (scope from 6091621568)

  • Read: the chatter does one batched find on sys_comment_reaction, with comment_id $in the feed's comment ids. It groups the rows client-side into the Record<string, string[]> shape CommentThread already renders. Rendering is unchanged. ⛔ No per-comment read (no N+1).
  • Write: a reaction click creates or deletes the member's own reaction record. The client stops writing the whole reactions column.
  • Sites, located on objectui main by the director's read (the line numbers are pointers): RecordDetailView.tsx about :224–:293 (the toggle and the column write), about :1885–:1920 and :2198–:2218 (the feed read), and collaboration/CommentThread.tsx about :25 and :592–:593.
  • Measure: the size of one $in read on a record with many comments. Page it if needed.

Landing order (measure before building)

Pins

  • A member's click on another member's comment creates their own reaction record, and the feed renders it grouped.
  • A second click removes it.
  • Two members reacting at once both show.
  • Ablation: put back the column write and the concurrent-reaction pin goes red.

Dedupe: MCP search_issues on objectui, query chatter comment reaction records sys_comment_reaction stop writing reactions column: 2 hits, objectui#11019 and objectui#11035. Both are closed, and both are other defects. None is this card.

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Oct 10, 2026
  2. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan: objectstack-ai/objectstack#22566 closed, with PR #22603 landed as 29a163f571 (sys_comment_reaction). pm:blocked → pm:queue

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-10T07:00Z. ⛔ Not a claim, ⛔ not a dispatch.

    Blocked-by: none

  3. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01B1gHb9baeX7oioD5sHVm7z
    Account: marchtian
    Branch: claude/issue-12078-reaction-records
    Worktree: objectui-issue-12078
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: packages/app-shell/src/views/RecordDetailView.tsx (the reaction toggle and column write near the top, and the feed read near the middle) and its tests; packages/collaboration/src/CommentThread.tsx only if its reaction props must change (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"
    Clause-②: yes
    Responsibility: objectui's chatter in app-shell RecordDetailView.tsx, which rewrites the whole sys_comment.reactions column on every click (a last-write-wins race) | objectstack's sys_comment_reaction object, landed in objectstack-ai/objectstack PR #22603 (29a163f571) | every member who reacts on a record feed; used today wherever the chatter renders
    Thread-read: 6094910442
    Serial constraints cleared: objectui#7611 is not dispatched: it is reserved for the epic lane of objectstack-ai/objectstack#15194 (6095619495), and this seat yielded it in 6095645635. That card's reader list includes the sys_permission_set slot near the end of RecordDetailView.tsx, a region disjoint from this card's, and the dev is told not to touch it. (Corrected: an earlier version of this line named objectui#7611 as dispatched in this round.) objectui#12084 (023f00d4, merged today) last touched the file; the dev starts from that merged code. objectstack-ai/objectstack#22573 (retiring the column) waits on this card and is not in flight.

    Clause-② is declared yes because CommentThread is exported from @object-ui/collaboration, and a change to its reaction props would widen that surface. The in-seat review needs that reason.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12078,
      "status": "done",
      "branch": "claude/issue-12078-reaction-records",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12090",
      "session": "session_01B1gHb9baeX7oioD5sHVm7z",
      "premise_still_valid": true,
      "summary": "Where the deployment has sys_comment_reaction, RecordDetailView now reads the comments' reaction rows in one batched comment_id $in find, paged at 100 ids, and groups them into the existing { emoji: userIds[] } aggregator, so rendering is unchanged. A click creates or deletes only the clicker's own row: { comment_id, emoji }, with user_id stamped by the server. Writes per (comment, emoji) run in order, and a refused write rolls back with the existing detail.reactionFailed toast. Where useObjectPresence('sys_comment_reaction') earns 'absent' (cloud's v17 pin 56bf27affb), the column path runs unchanged. That is the dual path, so no cloud .objectui-sha hold is needed. CommentThread is not on the chatter path; the published surface is untouched (no export added; packages/collaboration and packages/types are not in the diff).",
      "files_changed": [
        ".changeset/12078-reaction-records.md",
        "packages/app-shell/src/views/RecordDetailView.tsx",
        "packages/app-shell/src/views/RecordDetailView.reactionRecords-12078.test.tsx",
        "packages/app-shell/src/views/RecordDetailView.reactionKeepIds-11019.test.tsx",
        "packages/app-shell/src/views/RecordDetailView.reactionWriteFailure-10899.test.tsx",
        "packages/app-shell/src/views/RecordDetailView.reactionsCommentRowsOnly-11035.test.tsx"
      ],
      "gates": [
        "pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build (29 packages, via os-verify-lock) → VERDICT command-exit 0",
        "pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json) @086db35 → exit 0; tsconfig.test.json includes the new test file (its b886ae3 run reported TS7006/TS2769 there, fixed in 086db35)",
        "pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/RecordDetailView @086db35 → Test Files 47 passed (47), Tests 294 passed (294)",
        "pnpm exec vitest run --maxWorkers=2 (the 4 reaction files) @b886ae3 → Test Files 4 passed (4), Tests 22 passed (22)",
        "eslint --format json on the 5 touched .tsx files @086db35 → exit 0, 0 errors, 133 warnings. Warnings on added lines: 10 no-explicit-any in the new test, plus 2 on lines this diff edited but did not cause (the existing res: any; the existing missing-t exhaustive-deps)",
        "pnpm check:new-line-citations @086db35 → VERDICT 0 new citation(s), exit 0",
        "pnpm check:control-bytes @086db35 → OK, exit 0; plus a grep -naP control-byte self-scan of the 6 files: 0 hits",
        "pnpm check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape @086db35 → OK, exit 0 each",
        "pnpm check:test-path-roots @086db35 → OK, exit 0",
        "pnpm check:changeset-claims / check:pending-changeset-literals @086db35 → exit 0 each",
        "node scripts/check-changeset-presence.mjs / check-changeset-no-major.mjs @086db35 → exit 0 each",
        "node scripts/check-governed-queue-guard.mjs --test (6 paths) → NOT GOVERNED",
        "check:i18n-keys / check:i18n-drift / check:i18n-dead-keys → not owed: no locale string changed (reuses detail.reactionFailed)",
        "NOT MEASURED: check:eager-closure, reason: needs a full console production build. CI's Bundle Analysis runs it on the PR. No module enters the closure: useObjectPresence is already reached via sharedUserFeeds. Only the RecordDetailView chunk's bytes grow"
      ],
      "line_budget": "n/a: no skills/** and no line-ratcheted ledger in the diff",
      "deviations": [
        "PR body (written once, not PATCHed) says 'eslint on the six touched files'. Eslint ran on the five .tsx files; the sixth file is the changeset .md, which eslint does not read. Seat fix if wanted: change 'six touched files' to 'five touched .tsx files' in the Verification section of PR #12090",
        "Commit trailers follow objectui AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By line. The PR footer uses os-dev's session-URL form, not the harness's form",
        "Declared narrowing: check:eager-closure not run locally (see gates); CI-owned",
        "The card body's CommentThread.tsx sites are not on the console chatter path (it renders via plugin-detail ReactionPicker from FeedItem.reactions), so that file is untouched; Clause-② is copied from the claim as asked, with the untouched-surface evidence in the PR body"
      ],
      "tests": "New pins in RecordDetailView.reactionRecords-12078.test.tsx (10 tests). They use the real RecordDetailView and ReactionPicker over one fake server shared by several mounted views, each signed in as a different member. The server stamps user_id, enforces the unique (comment_id, emoji, user_id) index with 409, and lets a member delete only their own row. The pins: (1) a click on another member's comment creates the clicker's own row; the create payload is exactly { comment_id, emoji }; no update is sent; a fresh read shows '👍 2' as own. (2) A second click deletes the clicker's own row, by the create's id and by the read's id. (3) Two members reacting at once both show: two views mounted together, both pick 👍 before either write answers, and the author reads '👍 2'. (4) One batched read for 3 comments; 250 comments are read in pages of 100/100/50. (5) A column-only reaction is not shown. (6) A refused create rolls back and toasts once. (7) A take-back before the create answers deletes that create's row. (8)-(10) The registry decides the store: absent means the column path; a registry that lists nothing means records; a 'loading' registry holds the comment read. The column pins for 11019, 10899 and 11035 now declare a registry without the object. Results @086db35: 47 RecordDetailView files, 294 tests passed; type-check exit 0. ABLATION (one-time, not kept), on the committed tree HEAD 086db35, under os-verify-lock with trap restore. The mutation set `if (reactionStore === 'records' && false)` and removed `if (reactionStore !== 'column') return;`, which puts the whole-set column write back. On disk: injected=1, guard=0. Result: 'Tests 5 failed | 5 passed (10)'. The concurrent pin failed with 'expected [] to deeply equal [ '👍 2' ]', and the create/delete/rollback/take-back pins failed too. Restore: git checkout HEAD -- path; blob eba397e8 == HEAD blob; git diff HEAD empty; marker count 0. Measurements: the Node 22.22.0 maxHeaderSize is 16384; each UUID id costs ~45 bytes; 100 ids make a 4588-byte URL; a local Node http server accepted 350 ids and returned 431 at 360.",
      "mcp_calls": "0",
      "api_writes": "3 relay writes, each one POST /repos/objectstack-ai/objectstack/dispatches run as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (draft; PR #12090; read-back 10944 bytes identical); (2) label-write --assign marchtian → POST /repos/objectstack-ai/objectui/issues/12090/assignees (read-back MATCHES; labels untouched, POST none); (3) this os-dev-report comment → POST /repos/objectstack-ai/objectui/issues/12078/comments. git push is not counted",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: objectstack-ai/objectstack#22573 (retires the column), or the cloud v18 pin bump. Once cloud's framework carries sys_comment_reaction, the chatter's column branch and its three column pins (11019/10899/11035) are dead and can be deleted. Noted in PR Acceptance notes, not filed",
        "carrier: none. The feed's own sys_comment read has no $top, so it reads the whole thread unbounded. This is an observation only; it is in Acceptance notes and not filed",
        "carrier: none. A stale-screen edge: a member who already reacted in another tab and clicks the same emoji gets 409 UNIQUE_VIOLATION, and the chip rolls back with the error until the next read. Nothing is overwritten. In Acceptance notes and not filed"
      ]
    }
  5. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR objectui#12090 (head 086db35b02) · domain:ui seat 3, session_01B1gHb9baeX7oioD5sHVm7z, 2026-10-10T10:06Z

    Reviewed against GitHub and the diff, not the report.

    • Shape:
      • The PR is a draft on main.
      • Its first line is Fixes #12078, and Clause-②: yes sits at the start of a line.
      • No other card number sits near a closing keyword.
      • The assignee is marchtian.
    • Scope: 6 files, +848/−20: RecordDetailView.tsx, one new and three amended pin files, and one changeset. Nothing touches content/docs/releases/ and no file is unrelated. NOT GOVERNED.
    • The diff against the card:
      • Where the object registry earns sys_comment_reaction as present, there is one batched $in read (paged at 100 ids) grouped into the existing render shape, and a click creates or deletes only the clicker's own row: { comment_id, emoji }, with user_id stamped by the server.
      • Where it earns absent (cloud's v17 pin), the column path runs byte-identical.
      • The comment read waits for the registry. The seat checked useObjectPresence on main: with no provider, and with an errored registry, it reads as settled, so the feed cannot hang.
    • Contract review: 6096401893, Served-tier: CONTRACT_REVIEW_TIER, PASS on this head. It finds that the yes declaration did not materialise: no export, prop, type member or locale key moved, and CommentThread is untouched.
    • CI on 086db35b02: 43 runs, 40 success, 3 skipped by design (the coverage placeholders and dependabot), 0 failed. Bundle Analysis is green, which closes the dev's NOT MEASURED check:eager-closure.
    • Changeset prose, checked sentence by sentence against the diff: "one sys_comment_reaction read … pages of 100 ids", "a click creates the member's own reaction row … a second click deletes that row", "Reactions stored only in the column are not shown where the object exists", and "A deployment without sys_comment_reaction keeps the column path, unchanged" all match. It is @object-ui/app-shell: patch.
    • Out of scope:
    • Seat note on the PR body: its Verification line says "eslint on the six touched files", but eslint ran on the five .tsx files; the sixth is the changeset. Left as written: cosmetic, and Lint is green.

    Landing: ready + auto-merge through the queue, now.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 10, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#12090 → main de302c73 · domain:ui seat 3, session_01B1gHb9baeX7oioD5sHVm7z, 2026-10-10T12:43Z

    • The merge: through the merge queue at 10:26Z. Its one parent is 4d0ff233 (objectui#12088's landing), an ancestor of origin/main.
    • Content check:
      • All six files are blob-identical to the accepted head 086db35b02, among them RecordDetailView.tsx eba397e8 and the new pin file 6bc86b10.
      • sys_comment_reaction is read and written in RecordDetailView.tsx on de302c73 (6 hits).
      • Control: zero hits for it anywhere on objectui at 023f00d4.
    • The card: closed completed by the PR's Fixes line. pm:dispatched comes off in the label write after this comment.
    • Contract review: 6096401893 (PASS). ACCEPT: 6096406646.
    • Downstream: Retire sys_comment.reactions (ruling A amended on #22505): no aggregate, no data migration, after the console reads reaction records objectstack#22573 (retiring sys_comment.reactions) names this card and objectstack's console pin bump as its blockers. This card's half is now on objectui main.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions