Skip to content

comments: the @mention picker offers a member who cannot read the thread, and the author is never told that person will not be notified #27703

Open

Description

@objectstack-fleet

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

Filing gate: ② a silent drop of what the author asked for. Split out of #27520 row 1, as triage 6495077487 directed: 「It becomes its own p2 card: a silent drop of the author's intent.」 Observed by the 18.3.0 RC walkthrough at c4c515ad. Mechanism read at origin/main a75ddba1. ⛔ Not a claim. Triage sets the lane.

Business reason

Who: anyone who @mentions a colleague in a record's Discussion. What happens: the picker offers a member who cannot read that thread. The comment saves with the mention, and the server withholds that person's notification. Nobody tells the author, so they believe the person was told when they were not. The rendered mention also looks like plain typed text.

Mechanism (read, not reproduced here)

Wanted (the route is triage's call)

Either leave non-readers out of the picker, or tell the author after save which mentions will not be notified. Either way the author is no longer misled. The first needs a read the console does not make today (who can read this thread). The second needs the comment write to say which recipients were withheld. Both cross the server boundary, so this may split into a domain:server half and a domain:ui half. Rendering picked mentions as chips is the presentation half of the same row.

Dedupe

I read the 266 most recent issues by title for mention, cannot_read_thread and picker. The only hit is #27503 (a mention by email matched case-sensitively), which is a different defect. New.

Links back: #27520 (row 1)


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Oct 29, 2026
  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 业务对象与记录 | none | P②

    Triage: p2 · domain:ui · area:records · audit · 2026-10-30T04:00Z

    ⛔ Not a claim. This audit was owed: a filer's pm:queue with no triage audit.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

    Claim: PM loop round 1
    Session: session_01Utp6CaJoinvGQSbHeDaWBe
    Account: os-bill
    Branch: claude/project-thread-jaslxp (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27703
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: the mention/discussion region of ui/packages/app-shell/src/views/RecordDetailView.tsx, ui/packages/react/src/context/DiscussionContext.tsx, ui/packages/plugin-detail/src/RichTextCommentInput.tsx (chips, if taken), tests beside, root .changeset/, i18n for a new sentence (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on 178b5238: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: ui code produces the defect | no platform path covers it | every Studio / console author reaches it today
    Thread-read: 6514642798
    Premise: holds
    Serial constraints cleared: PRs #27636 and #27592 (record page, named by triage) merged 2026-10-29T20:03Z

    Scope of this claim: measure first. Per triage (os-zhuang, 2026-10-30T04:00Z), if the fix needs a new served key (withheld recipients in the comment-save answer, or a who-can-read read), the thread stops before code and the seat returns the card with pm:retriage as a Seam. Only the mention/discussion region of RecordDetailView.tsx is in scope: seat 4's PR #27793 (apiHandler region) and seat 2's record page v4 PRs (#27920, #27545) are off limits, as are seat 4's #27912, #27907 and #27893 files.

    Dispatched 2026-10-30T07:12Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CY77T7uJxshsVS2zgc

    os-dev-report

    Reported 2026-10-30T07:16Z for #27703. No PR: the triage STOP case holds. Nothing the client already reads says who can read the thread or which mentions were withheld, so the fix needs a new served key. Seam below.

    {
      "issue": 27703,
      "status": "needs_decision",
      "branch": "claude/project-thread-jaslxp",
      "pr": null,
      "session": "session_01Utp6CY77T7uJxshsVS2zgc",
      "premise_still_valid": true,
      "summary": "Measured on origin/main 46943657, reading only. (1) The picker list is RecordDetailView.tsx L2492-2541: dataSource.find of sys_user, top 50, select id name email image, plus the viewer. It carries no record or thread readability. (2) The saves are handleAddComment / handleAddReply (L2925-3014, dataSource.create of sys_comment) and handleEditComment (L3040ff, dataSource.update). Each answers the stored row. sys_comment (sys-comment.object.ts) has no admitted or withheld field. (3) The gate runs inside the sys_comment afterInsert writer (audit-writers.ts writeCommentMentions, registered L2641). admitMentionRecipients returns the admitted ids only. A withheld recipient is only logged at warn, and MentionWithheldReason is documented as never shown to the author. Nothing is stamped on the row or put in the create answer, and the notification lands in the recipient's own inbox, which the author cannot read. (4) The only read on main that answers readability for ANOTHER user is POST /api/v1/security/explain. Its other-user arm (security-plugin-explain-access.ts L136-166) needs manage_users, organization administration of that user, or a delegated adminScope, and refuses an ordinary commenter with PermissionDeniedError. The record page asks it only for the viewer (useRecordEditable, queueClaim). The ui cannot derive readability itself: OWD, sharing, RLS, the tenant wall and the comment object's CRUD are server-side, and the gate's own docblock says nothing outside the evaluator derives it. So both routes the card names cross the server boundary. Per the ruling: stop before code, no PR.",
      "tests": "No code, so no tests. Readings at origin/main 46943657: RecordDetailView.tsx L783 (state), L2492-2541 (directory read), L2925-3074 (create, reply, edit writes); DiscussionContext.tsx L28-66 (DiscussionMentionSuggestion is id, label, avatarUrl; no readability field); mention-recipient-gate.ts (whole file, L84-92 reason type, L172-180 verdicts); audit-writers.ts L2540-2641 (afterInsert only, admitted ids only); sys-comment.object.ts L141-259 (field list); security-plugin-explain-access.ts L136-166 (other-user refusal). Not measured: a live comment save against a running server. Reason: the reading leaves no client path to measure.",
      "gates": "n/a. No diff, so dispatch-gates --commands derives nothing and check-sdui-manifest has nothing to read.",
      "dogfood_battery": "n/a. No PR.",
      "line_budget": "0. No diff.",
      "files_changed": [],
      "deviations": [
        "No worktree and no pnpm install: no edit was made.",
        "Read the card and code before the seat's start (reading only), then re-checked the newest Claim (6517141465, Branch claude/project-thread-jaslxp) after start."
      ],
      "mcp_calls": "0",
      "api_writes": "1. This report comment, through post-stamped (POST issues/27703/comments via the relay).",
      "open_questions": [
        {
          "question": "Seam (protocol addition, triage asked for p1): which served answer tells the author a mention was withheld?",
          "options": [
            "A. The sys_comment create (and update) answer carries a response-only list of the mentioned user ids the gate withheld, with no reason. Server half (domain:server, plugin-audit): writeCommentMentions already holds admitted vs mentioned; it hands the withheld ids back on the write's answer for the AUTHOR's own request only. Spec half: the key is declared where the data write answer is typed (packages/spec), as a response-only key, not a stored sys_comment field, because a stored field would tell every reader of the comment who cannot read it. ui half that follows (domain:ui, this card): handleAddComment, handleAddReply and handleEditComment read the key, map ids to names through mentionSuggestions, and show one warning naming who will not be notified (new en/zh sentence plus packs), pinned with an ablation. Cost: server S-M, ui S.",
            "B. A picker-time read: the server answers which candidates can read this record and the sys_comment object, and the picker leaves the others out. Cost: a new endpoint, one grant resolution per candidate per open, and it lets any commenter probe whether a named colleague can read a record. The thread arm (cannot_read_thread) can only be judged after the comment exists, so B still needs A to be exact.",
            "C. Both: B for the picker, A as the exact answer at save."
          ],
          "recommendation": "A, because it reuses the gate's own verdict (no second derivation of readability), is exact for both reasons, and only tells the author about people they named themselves. B can follow if the picker should also shrink."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: not measured live, read from code: the record page Edit rewrites mentions through dataSource.update (RecordDetailView.tsx handleEditComment), but the mention writer is registered on sys_comment afterInsert only (audit-writers.ts L2641, the handler returns unless ctx.event is afterInsert). So a mention added by editing a comment notifies nobody, even a reader, and the author is not told. Same silent drop of the author's intent as this card. Dedupe words: mention added on edit not notified, sys_comment afterUpdate mentions, edit comment mention notification. carrier: the seat, to fold into the server card that takes this Seam",
        "carrier: #27703 · noted, not filed: rendering picked mentions as chips (plugin-detail RichTextCommentInput.tsx) is ui-only, but this run makes no PR; it belongs with the ui half after the Seam lands"
      ]
    }

    Dedupe words: mention withheld author not told, mention picker non-reader, comment save withheld recipients.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

    Seam → pm:retriage · 2026-10-30T07:17Z

    Per the triage, a new served key returns the card to triage. The card thread measured on origin/main 46943657 (report 6517192427) and stopped before code. No PR exists and the branch is untouched.

    Measured: the client holds nothing that answers "who will not be notified".

    • Picker candidates: they come only from the sys_user directory read (RecordDetailView.tsx ≈L2492–2541), which carries no readability.
    • Comment write answer: a comment create/update answers with the stored row, and sys_comment has no withheld field.
    • Mention gate: plugin-audit runs the gate in the afterInsert writer. It returns only the admitted recipients and logs the withheld ones at warn.
    • Readability check: the only read that answers "can another user read this" is the security/explain other-user branch. It needs manage_users, an org admin or a delegated adminScope, so an ordinary commenter is refused. The client can't infer readability itself.

    Seam (the thread recommends A; the seat agrees):

    • A. The sys_comment create/update answer carries a response-only field, for the author's own request only: the ids of the mentioned users whose notification was withheld, with no reason.
      • Landing: plugin-audit writeCommentMentions plus the spec's data-write answer type. Metadata-protocol added.
      • ⛔ Not a stored field: that would show every reader who can't read the thread.
      • The ui half follows as a domain:ui card. The three write callbacks read it, map ids to names through mentionSuggestions, and say "X and Y will not be notified", with an ablation. Mention chips (RichTextCommentInput.tsx) ride with that ui half.
    • B. The server filters the picker to readers. Not recommended alone: it is costly, it can probe colleagues' permissions, and the thread arm can only be judged after the comment is stored.

    Out of scope, from reading the code (not measured live): the mention writer hangs on afterInsert only, so an @ newly added by editing a comment notifies no one. Fold it into the server card that takes the Seam.

    Labels: pm:dispatched → pm:retriage, and the seat's assignee is cleared. Triage sets the lane and priority (「协议新增定 p1」).


    Generated by Claude Code

  6. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    and removed on Oct 30, 2026
  7. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 业务对象与记录 | none | P②

    Triage: retriage answer · Seam A, p1 · needs-user-decision on one point · 2026-10-30T07:54Z

    ⛔ Not a claim.

    • The measurement holds: the client holds nothing that answers "who will not be notified", so this is a Seam.
    • Triage's grade is A. A response-only member on the sys_comment create/update answer goes to the author's own request only: the ids of the mentioned users whose notification was withheld, with no reason. ⛔ It is not a stored field.
      • The ends are packages/spec (the data-write answer type) and plugin-audit (writeCommentMentions). Card domain domain:spec, as a Seam.
      • The edit-time mention (afterInsert only) folds into the server end.
      • The ui half follows as its own domain:ui card once the key is on main.
      • It is a new served key, so 「协议新增定 p1」 and Clause-②: yes.
    • The point for the maintainer: A tells an author that a named colleague cannot read this thread. It is one bit per user the author named, with no reason, and only on the author's own write. That is a disclosure about another user's access, so it sits on the security-boundary floor, and the maintainer confirms it before dispatch. Triage's lean: accept, because the author already named the user and the alternative is a silent false belief.
    • pm:retriage removed.

    Generated by Claude Code

  8. added
    priority:p1High: required for production / M2
    and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:specneeds-user-decisionpriority:p1High: required for production / M2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions