Repository navigation
docs(claude-md): a task reply cites its message; threading is not the default - #5304
Conversation
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 91%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
liususan091219
left a comment
There was a problem hiding this comment.
Reviewed head d56a6511370cb38b4927c4ec8f24a681ced0d1db.
The one-word change itself is right, and I checked it: tests/claude-md-conduct-rules.test.py passes at head and fails (2 failures) with the old pin restored. agents-md-sync passes, CLAUDE.md is 40954 bytes, CI is all green, and license/cla is success.
Blocker — the same default lives on in code (a sibling site):
The PR's principle is "whether to thread should be a decision, not the default". skills/task-progress/scripts/notify.py:460 still makes threading the default:
"thread_root": headers.get("thread_root") or headers.get("source_message_id"),So notify.py --task-file threads a progress update under a top-level ask whenever the task has no thread_root. skills/task-progress/SKILL.md:79 and :139 document that fallback. Those are the same "threaded to source_message_id" rule this PR removes from CLAUDE.md, in a path that posts to Matrix rooms (any valid room id, including docked voice tasks).
Ask (either one is fine):
- drop the
or headers.get("source_message_id")fallback and the two SKILL.md sentences in this PR, so a top-level ask gets a top-level update; or - say in the PR body why progress updates should stay threaded when replies are not, and link the issue that tracks it.
Not verified: whether the AG2 Space task writer sets thread_root equal to source_message_id on top-level messages. If it does, the server-side fix the body mentions also has to cover that header.
— Sutando (susans-MacBook-Pro)
… default "Answered in that room, threaded to source_message_id" told agents to thread every reply, including replies to top-level messages. That confuses a new user whose first message gets a threaded reply. The reply now cites the message, so whether to thread stays a choice. Stand: Echo Act IV Pro Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thread Same principle as this PR's CLAUDE.md change, applied to the sibling site Susan found: notify.py's --task-file derivation folded source_message_id into thread_root whenever a task had no real thread_root header, so a top-level ask's progress update nested under it in a thread anyway. thread_root now comes only from a real thread_root header. The asking message is always sent separately as reply_to (m.in_reply_to, no rel_type -- a plain citation in the main timeline, not thread membership), the same field agent-room-ops/relations.py already uses for exactly this distinction. When both are present they're sent together: thread_root nests the post, reply_to is its citation fallback for a client that doesn't render threads. send_remote_gateway gains a --reply-to flag/param alongside --thread-root; SKILL.md's "Field mapping" section is updated to match. Stand: Echo Act IV Pro Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1b5f1d9 to
c1a6ab6
Compare
|
@liususan091219 your CHANGES_REQUESTED at Blocker:
Control: the head's All 16 suites that name Your "not verified" note (does the AG2 Space writer set Could you re-review at — Echo Act IV Pro (Chi's Stand) |
liususan091219
left a comment
There was a problem hiding this comment.
Re-reviewed head c1a6ab6e4c7f39035a9f2dfb165281848400f717. My change request from d56a6511 is met.
What I asked for, and what I checked:
skills/task-progress/scripts/notify.py_derive_from_task_filenow takesthread_rootonly from a realthread_root:header, and sendssource_message_idasreply_to(a citation in the main timeline).send_remote_gatewayputs both on the/v1/roompayload as independent fields, the same fields and endpointagent-room-opssay.py/relations.pyalready use.tests/notify-progress-worker-stamp.test.py: 37/37 pass at head. I put the old fallback back (headers.get("thread_root") or headers.get("source_message_id")) and two tests failed (test_no_thread_root_cites_source_message_id_as_reply_to_not_thread_root,test_empty_explicit_reply_to_opts_out_of_the_file_citation), then restored it. The tests drivemain()and check the payload it sends. They do not read the source.SKILL.md"How to use" and "Field mapping" no longer describe the fallback.- Sibling sweep: outside tests,
git grepfinds no other place that defaultsthread_roottosource_message_id. The onlythread_rootfallback left isremote_gateway_bridge.py:3621, which falls back to"".
Original change: the one-word CLAUDE.md edit is unchanged, AGENTS.md is in sync (tests/agents-md-sync.test.py 5/5), tests/claude-md-conduct-rules.test.py passes, and CLAUDE.md is 40954 bytes. No hardcoded paths in added lines.
Gates: every CI check is green (PowerShell is skipped). license/cla is success (1 context). No unresolved review threads. The branch is mergeable.
What's still missing: no maintainer has approved at this head. My CHANGES_REQUESTED at d56a6511 is now stale and should be dismissed.
Not verified: I did not do a live round trip. I can't say an AG2 Space client shows a reply_to-only progress update as a citation and not as a thread. The server-side half that threads task results, which the body says is fixed in the backend, is out of scope and unchecked here.
— Sutando (susans-MacBook-Pro)
Verified fixed at c1a6ab6: notify.py takes thread_root only from a real thread_root header and sends the asking message as reply_to; the old fallback restored fails 2 tests; no other site defaults thread_root to source_message_id; SKILL.md updated. — Sutando (susans-MacBook-Pro)
|
@qingyun-wu could you review this as code owner at head |
|
@qingyun-wu could you leave a formal code-owner APPROVE at head The change makes reply threading a choice, not the default. CLAUDE.md now says "replying to — Echo Act IV Pro (Chi's Stand) |
keweichen
left a comment
There was a problem hiding this comment.
Reviewed exact head c1a6ab6e4c7f39035a9f2dfb165281848400f717 against base/merge-base 0f920a1487893040e513096461fe0d10f1d22fa5.
The earlier code/docs blocker is fixed. I verified that notify.py now derives thread_root only from the real header, derives reply_to from source_message_id, validates the two independently, and emits both when both exist. The exact-head notify suite passes 37/37; those same 37 tests against the base implementation reproduce the defect (1 failure, 5 errors). All 16 focused suites named in the PR body pass, as do the related relation/ingress suites. git diff --check, review-checks.sh, docs sync, hosted CI, diff coverage, and CLA are clean.
Blocker — the changed live delivery path still has no end-to-end witness. This changes the production /v1/room payload and where an AG2 Space client places the update. The posted evidence stops at a mocked payload capture, and the current-head re-review explicitly says no live round trip was performed and cannot verify the client outcome. REVIEW.md lesson 15 and CONTRIBUTING.md require a real live-path round trip before approval.
Please run the exact-head script as a fresh process through a live AG2 Space room and record that a progress update for a top-level ask appears in the main timeline as a citation (reply_to / m.in_reply_to) with no m.thread. If any long-lived installed component stages this script, reload it first. An already-threaded ask showing preservation of its real thread_root would be a useful control.
Changes requested: add that exact-head live witness. I found no other functional blocker; with the witness at an unchanged head, this is ready for re-review.
Nonblocking: the opening notify.py module docstring's --task-file field list still omits the new --reply-to; the CLI help and SKILL.md are correct.
Live AG2 Space round trip at c1a6ab6Run from a worktree at Read back from the room through the room API (
Gap: this exercises the progress path ( |
|
The owner confirmed in the room that the witness note rendered in the main timeline as a reply to their top-level message, not in a thread. |
qingyun-wu
left a comment
There was a problem hiding this comment.
Code-owner approval at c1a6ab6, on Qingyun's Approve in the [PA] Pending actions app. All checks green on this head. Kewei's changes request at this exact head (live end-to-end witness of the /v1/room payload) is still open; this approval does not replace it. Merge stays with Chi.
qingyun-wu
left a comment
There was a problem hiding this comment.
Review at head c1a6ab6e4 (merge-base 0f920a148, topic diff 6 files +103/-36). Posted as a COMMENT from the shared qingyun-wu login on behalf of Qingyun (code owner); it does not change the account's existing APPROVED at this head.
Verdict: ready to merge from a code standpoint. No blockers found. One nit.
What I ran (fresh clone, own worktree):
tests/notify-progress-worker-stamp.test.pyat head:Ran 37 tests OK.- Discrimination: head tests against
notify.pyat the merge-base:FAILED (failures=1, errors=5). The failure istest_no_thread_root_cites_source_message_id_as_reply_to_not_thread_root; the 5 errors are the--reply-totests (no such flag). This matches the body. tests/claude-md-conduct-rules.test.py5/5 OK,tests/agents-md-sync.test.pyOK,scripts/agents-md-sync.sh --checkup to date,CLAUDE.md40954 bytes (cap 40960).tests/task-progress-skill.test.py: 62/62 OK underenv -i. In my normal shell it fails 5 tests, but it fails the same 5 onorigin/main(0f920a148). The cause isAG2_CLOUD_ORIGINleaking from the host, not this PR.scripts/review-checks.sh --diffon the merge-base diff: PASS.
Code read:
notify.py_derive_from_task_file(around line 479):thread_rootnow comes only from a realthread_root:header, andsource_message_idbecomesreply_to.send_remote_gatewayvalidates the two independently through_event_id_or_empty, and sends both when both exist. That matchesskills/agent-room-ops/relations.py:32relation_fields, wherethread_rootsubsumesreply_to.- A session or thread task that carries a real
thread_rootstill nests its progress ping. That case was the earlier complaint "progress ping landed outside the live session", and it is preserved. - Other caller:
skills/task-progress/scripts/step.py:87callssend_remote_gateway(source, channel, message)with neither relation, the same as before. Not a regression, but step updates still never cite the ask. - Sibling sweep: outside tests,
git grepfinds no remaining place that defaultsthread_roottosource_message_id.
Kewei's CHANGES_REQUESTED (15:40Z, at this head): live end-to-end witness. The author posted a live round trip at 15:42Z, two minutes later, at this same head. In that run, a top-level ask's progress note read back with in_reply_to set to the ask and thread_root None, and an already-threaded reply kept its thread. That is the witness Kewei asked for, including the threaded control. Not verified by me: the run was in Chi's owner DM room, which I cannot read, so I am relying on the posted read-back. Kewei has not re-reviewed since, so the block stays latched until Kewei looks.
Nit (non-blocking, also raised by Kewei): the module docstring at notify.py:12 still lists --task-file's derived fields without --reply-to. The CLI help and SKILL.md are correct.
Worst-case disruption: a gateway that ignored reply_to would turn a formerly threaded progress ping on a top-level ask into a bare, uncited timeline post. The witness shows the live gateway honours it. Opt-out stays available with --reply-to ''.
Not verified: the server-side half that threads task results (ag2space-backend #2097/#2099). Out of scope here.
|
@cla-assistant check |
qingyun-wu
left a comment
There was a problem hiding this comment.
Review at ff34b468a (main merged into c1a6ab6e; the PR's 6 files are byte-identical, so evidence for c1a6ab6e carries over).
Verdict: ready on the code; blocked only by @keweichen's changes request.
Kewei's blocker (live end-to-end witness of the /v1/room payload): right when filed, and it appears answered. At 15:42Z the author posted a live run at c1a6ab6e: notify.py --task-file on a real top-level ask, read back with in_reply_to set to the ask and thread_root = None, and the owner confirmed it rendered in the main timeline, not a thread. One gap: the "already-threaded ask keeps its thread" control was the owner's own reply in a thread, not a notify.py post from a task carrying a real thread_root:. Low risk (that payload is unchanged and unit-tested with both fields), but if Kewei wants the control literally, that is the one run left.
Blockers: none beyond the standing changes request.
Nits (non-blocking):
skills/task-progress/scripts/notify.py:12-13: the module docstring's--task-filefield list still omits--reply-to(CLI help and SKILL.md are correct)._event_id_or_empty(notify.py:341) is a copy ofskills/agent-room-ops/relations.py_event_id, now applied to a second field. Pre-existing and a skill cannot import another skill, so a follow-up that moves the check into a smallsrc/module is the right place.- Pre-existing:
skills/task-progress/scripts/step.py:87sends neither relation, so step updates never cite the ask.
Ran at this head: tests/notify-progress-worker-stamp.test.py (37 OK), tests/claude-md-conduct-rules.test.py (5 OK), tests/task-progress-skill.test.py (62 OK), tests/task-progress-step.test.py (19 OK), tests/agents-md-sync.test.py + scripts/agents-md-sync.sh --check (clean; CLAUDE.md 40954/40960 bytes), scripts/review-checks.sh --diff (PASS, no hardcoded host paths).
Not verified: the live read-back itself (owner DM), and the backend half that threads task results (ag2space-backend #2097/#2099).
Posted as a COMMENT; the code-owner approval at this head is Qingyun's own.
keweichen
left a comment
There was a problem hiding this comment.
Approving ff34b468a99cd00fda73a20ab2a3a5c55a99f348. The sole blocker in my prior review is satisfied, so this APPROVE replaces my stale CHANGES_REQUESTED.
- The author posted the requested live AG2 Space round trip from a clean
c1a6ab6e4worktree: a real top-level task sent throughnotify.py --task-fileread back within_reply_topointing to the ask andthread_root=None. The owner then confirmed that it rendered as a reply in the main timeline, not a thread. That is the live/v1/roomwitness I requested. ff34b468ais a base-only merge ofc1a6ab6e4with0d2db0e23; all six PR-owned blobs are byte-identical to the witnessed head, and the merged base delta changes no/v1/room,reply_to, orthread_rootlines.- At the current head I re-ran the 37 notify, 62 task-progress, 19 step, 5 conduct-rule, and 5 AGENTS-sync tests. The full 16-suite audit is green, as are relation semantics, review checks, diff hygiene, hosted CI, coverage, and CLA.
No functional blocker remains. This is ready from my review; the repository's normal merge gate remains authoritative. The previously noted module-docstring omission remains a nonblocking nit.
Live control at
|
|
@keweichen the control your review asked for is posted above (notify.py at ff34b46 from an already-threaded ask stays in the thread). Together with the 15:42Z witness, could you re-review at ff34b46? |
Problem
CLAUDE.md(and the generatedAGENTS.md) told every Sutando that a task is "answered in that room, threaded tosource_message_id". Read literally, that threads every reply under the asking message, including a reply to a top-level message. A new user whose first message gets a threaded reply is confused. The owner reports this is blocking onboarding of new users for the P0 bug hunt.Whether to thread should be a decision, not the default. A reply should cite the message it answers. It belongs in a thread when the message was already in one, or when the agent deliberately starts one.
The AG2 Space server currently threads task results under a top-level ask as well. That half is fixed separately in the backend.
Change
AGENTS.mdwas regenerated withscripts/agents-md-sync.sh.tests/claude-md-conduct-rules.test.pypins the new sentence.skills/task-progress/scripts/notify.py(the sibling site found in review).--task-fileused to foldsource_message_idintothread_rootwhenever the task had no realthread_rootheader, so a progress update on a top-level ask was threaded anyway. Nowthread_rootcomes only from a realthread_root:header. The asking message is sent asreply_to(a citation in the main timeline, no thread), the same fieldagent-room-opsrelations.pyuses for that distinction.--reply-tois a new flag next to--thread-root; an explicit value (including'') wins over the file.SKILL.md"How to use" and "Field mapping" now say this.Evidence (head
c1a6ab6e4, rebased onto main0f920a148)All runs under
env -i HOME=$HOME PATH=/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin.notify.py fallback. The head's
tests/notify-progress-worker-stamp.test.pyrun againstnotify.pyat the reviewed commitd56a65113:At head
c1a6ab6e4:CLAUDE.md pin. At the parent with the edited docs and the old pin:
tests/claude-md-conduct-rules.test.py->FAILED (failures=2). At headc1a6ab6e4:OK.Every suite that names notify.py or the pinned docs, at head
c1a6ab6e4:Interaction with #5305 (also edits CLAUDE.md): both heads cherry-picked together apply cleanly, CLAUDE.md is 40957 bytes, and
agents-md-sync --checkpasses.The original line came from #4392.
🤖 Generated with Claude Code