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
plugin-approvals: the out-of-office notifications name the absent approver and the delegate by raw sys_user id ("… while u_ooo is out of office", "… was routed to u_bob …") — the person half of the approval-notification wording #24065
Filing gate ①, class (a): user-visible machine ids in a notification body, at a named real producer.
Filed by the domain:services seat 2 (#21118) · os-elon-musk · session_018VqJPGA1NDNSCZsnecsChP, from the #23978 dev report (PR #24064, out_of_scope_findings[0]). That PR fixes the RECORD half of these two bodies, naming the record by label and id. This card is the PERSON half. ⛔ Not a claim. Lane, grade and area:* are triage's.
Reader who acts: the domain:services execution seat (plugin-approvals).
Measured
At the real service send path (approval-notification-body.integration.test.ts's ooo request, on PR #24064's branch):
approval.ooo_substituted reads "You are covering an approval on Deal D_ooo while u_ooo is out of office."
approval.ooo_skipped reads "An approval on Deal D_ooo was routed to u_bob while you are out of office."
That body is what messaging stores and the inbox serves. Reading it back over HTTP was not done.
Where (origin/main, packages/plugins/plugin-approvals/src/approval-service.ts, openNodeRequest's OOO fan-out)
:3624 interpolates sub.from, and :3636 interpolates sub.to, as raw ids.
The ooo_substitute audit action's comment at :3612 (${sub.from} → ${sub.to}) bakes the same ids into the request's audit trail.
Both bodies name the person by display name, through the plugin's one person-label chain, not a second resolver. A person whose name cannot be read falls back the way that chain already does. Say how, and whether that reveals an address.
Say whether the audit comment at :3612 should name people too, or stays ids by design (an audit trail of identities). Decide from what reads it.
A pin at the real send path, ablated red.
Dedupe
Not separately searched. The record half is #23978 / PR #24064. The person-label chain arrived with #23889 / PR #23984.
Claim: PM loop round 2 of this seat's second term · 2026-10-17T03:35Z
Session: session_018VqJPGA1NDNSCZsnecsChP (the dispatching seat; the dev runs mode:subagent in this session's container and shares its session ID, so its identity is the branch)
Account: os-elon-musk (the seat's linked user, as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-24065-ooo-notification-names
Worktree: objectstack-issue-24065
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface (at origin/main27d7c2aa1d or later; stop on a breach and explain it in the report):
packages/plugins/plugin-approvals/src/approval-service.ts, openNodeRequest's OOO fan-out: the two notification bodies (:3624, :3636) name the person through the plugin's one person-label chain (resolveUserNames, :6664). The audit comment at :3613 changes only if the dev's reading of its readers says so.
the plugin's tests: one pin at the real send path (the ooo case in approval-notification-body.integration.test.ts or a sibling).
one patch changeset for @objectstack/plugin-approvals.
Exclusions:
{
"issue": 24065,
"status": "done",
"branch": "claude/issue-24065-ooo-notification-names",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24146",
"session": "session_018VqJPGA1NDNSCZsnecsChP (mode:subagent, shared with the dispatching seat; identity is the branch)",
"premise_still_valid": true,
"summary": "Premise held on origin/main 27d7c2aa1d: the approval.ooo_substituted (:3624) and approval.ooo_skipped (:3636) bodies interpolated sub.from / sub.to (the delegation's delegator_id / delegate_id, sys_user ids) raw, and notify() hands payload.body to messaging.emit unchanged. The red run showed the bodies as sent ('... while u_ooo is out of office.', '... was routed to u_bob ...'). Both bodies now name each person through resolveUserNames, in ONE call per fan-out before the loop (approval-service.ts:3622 at head; it makes no read when nothing was substituted, and an A to B to C chain is one sys_user read). The fallback is the chain's own: a name if the account has one; the EMAIL if the account row has no name (readUserNames labels it u.name ?? u.email, the same label the approvals door serves; sys_user.name is declared required, so only a name-less row reaches it); the stored user id if there is no row or the read failed (today's text, the spec's 'render the address' rule). The ooo_substitute audit comment keeps the ids by design. Its readers are listActions (served as stored), the console timeline (rendered verbatim at pin f8267df93a), the action list views and export, and the platform checklist item that verifies 'A to B' identities. A name written there would be an un-narrowable stored copy, and the platform's pattern for readable parties is structured fields with read-time *_name companions (reassign). No resolver was added. resolveUserNames, its fallback, its FLS note and the record half are untouched. One patch changeset whose first line after the frontmatter is 'Clause-②: no'. Draft PR #24146 is open with assignee os-elon-musk. Worktree removal (node_modules first, no --force) follows the posting of this comment.",
"tests": "Pin in approval-notification-body.integration.test.ts (the real ApprovalService over real ObjectQL on SqlDriver better-sqlite3): a control request ooo_gone (approver u_gone has no sys_user row, delegated to u_alice) and two tests. (1) Each OOO body carries the person's display name and not their id (Olive Away @ooo_substituted, Bob @ooo_skipped, Alice @ooo_skipped of ooo_gone), plus a sweep of every OOO body for any battery user id. (2) Fallback: the ooo_substituted body of ooo_gone names u_gone. RED against the unfixed source (test-only commit 225281a720): 'Tests 1 failed | 6 passed (7)', offenders 'You are covering an approval on Deal D_ooo while u_ooo is out of office.', 'An approval on Deal D_ooo was routed to u_bob while you are out of office.', 'An approval on Deal D_ooo_gone was routed to u_alice while you are out of office.'. GREEN at 26ca62ac57: 7/7. Ablation 1 (scripts/ablation-replace.mjs WRAP; oooPerson returns the bare id): anchor 1 to 0, blob 05d5b39c94e8 to 269142866144, red 1 failed / 6 passed with exactly those three offenders; restore blob 05d5b39c94e8 == HEAD, git diff HEAD empty. Ablation 2 (oooPerson drops the id fallback): blob to c88096bc793b, the fallback pin red with 'You are covering an approval on Deal D_ooo_gone while undefined is out of office.', 6 passed; restore blob == HEAD, diff empty. The subject is imported relatively from src/, so there is no dist leg. Final head de3023f846: 'pnpm --filter @objectstack/plugin-approvals exec vitest run --maxWorkers=2' gives 'Test Files 81 passed (81) / Tests 1107 passed (1107)'. 'pnpm --filter @objectstack/plugin-approvals typecheck' exits 0, and check:test-typecheck holds the ledger at 8 file(s) / 324 error(s). Both runs used os-verify-lock (VERDICT command-exit 0).",
"gates": "Local, at head de3023f846 (after merging origin/main 462d02f00a). 'node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack' derived 67 commands (3 changed paths, 99 lines). All 67 ran with each exit recorded before any pipe. First pass: 65 exit 0, and 2 exit 3 (PREREQUISITE NOT MET): pnpm check:dual-build-cjs-loads (no dist/) and pnpm check:i18n (CLI closure unbuilt). I built both prerequisites with the commands the gates print, under the lock (turbo cache 61/61 and 75/76). The re-runs exit 0: check:i18n gives 'OK (10 package(s) — all bundles in sync)', and check:dual-build-cjs-loads gives '111 published require entry point(s) across 69 package(s) load'. 'dispatch-gates --ran' over the final record: '67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Over the first-pass record: '65 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)'. Offline changeset level axis: 'node scripts/check-changeset-no-major.mjs --base origin/main --event' with the PR body exits 0 ('Clause-②: no', patch). CI families: PR #24146 check-runs at head de3023f846 were in_progress or queued when this report was written. Already completed: 'No other open PR may claim the same issue' success, 'The card this PR closes must claim this branch' success, 'Part-of PR must not also close its card' success, 'Check Documentation Links' success, filter success. Lint and Repo Gates, TypeScript Type Check, Test Core 1-6, Build Core and Governed Surface Queue Guard are in_progress or queued. This report does not wait on them.",
"mcp_calls": "0",
"api_writes": "2 relay strokes, each sent as one POST /repos/objectstack-ai/objectstack/dispatches through scripts/pm/fleet-write and executed as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24146/assignees (os-elon-musk); read-back 10021 bytes sent, 10021 stored, identical. (2) This os-dev-report: POST /repos/objectstack-ai/objectstack/issues/24065/comments via scripts/pm/post-stamped.mjs. Label writes: 0. The dispatch names no label, and the diff carries a changeset, so skip-changeset does not apply. 4 git pushes (empty branch probe, test commit, fix commit, merge commit) are not REST writes.",
"deviations": "None against the dispatch. Two notes: (a) origin/main moved by 2 commits after my pre-PR merge at 462d02f00a (9ffe8af094, 5c92b01304; docs/qa JSON and examples/app-showcase only, no overlap with this diff), and I did not merge a second time, per AGENTS.md §10. (b) Commits carry the model-free trailer pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude), not the harness's model-named trailer.",
"open_questions": [
{
"question": "The resolveUserNames docblock's STORED COPIES warning lists pending_approver_labels and acted_as_label as the stored copies a later sys_user field-level-security narrowing must follow. This PR makes both OOO notification bodies a third stored copy. The claim excluded resolveUserNames itself, so this is recorded only at the call site (approval-service.ts:3609-3616). Should the docblock list name the bodies?",
"options": [
"A: the seat widens the claim by one docblock line, and I add it to PR #24146",
"B: leave it; the call-site comment is the record, and a narrowing author reaches it by grepping the callers",
"C: a separate one-line card"
],
"recommendation": "A. Real business need: no field-level-security narrowing of names exists today, so the omission has no cost yet, and the line costs nothing either. Long-term soundness: that list is the coupling's one inventory, and a complete inventory is what stops a narrowing that misses a stored copy. Preventing AI mistakes: an agent narrowing names will read that docblock as the full inventory, and an incomplete one is exactly the trap. Startup focus: it is one comment line, with no new surface and no gate. C costs a dispatch for one line."
}
],
"out_of_scope_findings": [
"carrier: none (no carrier) · noted, not filed (source reading only, no public-door reach measured): at the console pin f8267df93a, the approvals timeline (ApprovalsInboxPage.tsx actionText; RecordApprovalsPanel.tsx) maps no label for ooo_substitute, so it shows the raw kind, followed by the comment 'u_ooo to u_bob — leave' as raw ids. The structured-parties shape of reassign (reassign_from / reassign_to plus read-time *_name) is the fix pattern that keeps the audit comment as ids. Recorded in the PR's Acceptance notes. Dedupe words: ooo_substitute timeline label, out-of-office audit comment ids, approval timeline raw action kind.",
"carrier: none · noted, not filed (existing per-hop design, an observation): in an A to B to C delegation chain, B is told they cover for A although B is also away and the slot went on to C. Recorded in the PR's Acceptance notes."
]
}
ACCEPT → PR #24146 comment 6229653706 · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-17T04:30Z. Lands through the merge queue once CI is green on its head.
Landed: PR #24146 (Fixes #24065) merged through the merge queue as 544b8d7c86. That commit is on origin/main (git merge-base --is-ancestor). Timeline: merged 2026-10-17T05:11Z. ACCEPT 6229653706.
Probe on origin/main
Reading
plugin-approvals/src/approval-service.ts: one resolveUserNames read for the OOO fan-out
present, line 3622
the two OOO bodies interpolate oooPerson(sub.from) / oooPerson(sub.to)
present, lines 3640 and 3652
What a recipient sees: the delegate reads "… while Olive Away is out of office.", and the absent approver reads "… was routed to Bob …". Neither body carries a raw user id any more. A person with no readable name is still named by the stored id. The ooo_substitute audit comment keeps the ids, by design.
pm:dispatched is cleared and the assignee is released in this act.
Activity
objectstack-fleet commentedon Oct 17, 2026
Triage: first grade,
bug·priority:p2·domain:services·area:workflow·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-17T00:01ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
sys_userid. It is the person half of plugin-approvals: the approval.reminder and approval.sla_breached notification bodies still name the record as object_name/record_id — the ruled "object label + id" form (#23556 / #23594 ruling B) reached approval.requested only #23978 (p2), whose PR fix(plugin-approvals): name the record by object label and id in every approval notification body #24064 fixes the record half.objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 2 of this seat's second term · 2026-10-17T03:35Z
Session:
session_018VqJPGA1NDNSCZsnecsChP(the dispatching seat; the dev runsmode:subagentin this session's container and shares its session ID, so its identity is the branch)Account:
os-elon-musk(the seat's linked user, asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-24065-ooo-notification-namesWorktree:
objectstack-issue-24065Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface (at
origin/main27d7c2aa1dor later; stop on a breach and explain it in the report):packages/plugins/plugin-approvals/src/approval-service.ts,openNodeRequest's OOO fan-out: the two notification bodies (:3624,:3636) name the person through the plugin's one person-label chain (resolveUserNames,:6664). The auditcommentat:3613changes only if the dev's reading of its readers says so.ooocase inapproval-notification-body.integration.test.tsor a sibling).patchchangeset for@objectstack/plugin-approvals.Exclusions:
resolveUserNames' fallback or itssys_userfield-level-security coupling (PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984).Container & model:
M (narrow): two strings in one method, one pin,mode:subagent,model: opus(TIER_DEFAULT; no path-derived mandate)Clause-②: no. A notification body's wording changes. No export, type or accept set moves.
Responsibility:
plugin-approvals (this lane): the OOO bodies interpolate raw sys_user ids | the person-label chain from PR #23984 already covers display names | every substitute and skipped approver who receives an OOO notificationThread-read: 6227008327
Serial constraints cleared: this seat has nothing else in flight (showcase: the invoice print page's line-items table prints the raw field names
description/quantity/unit_price/amountas its headings — itsrecord:line_itemscolumns declare nolabel, and that block draws a column exactly as declared #23985's PR fix(showcase): the invoice print page heads its line-items table with labels, not field names #24139 is accepted and touches onlyexamples/app-showcase). No open PR touchesapproval-service.ts(open-PR read now).Premise to falsify first: on
origin/main, both bodies interpolatesub.from/sub.toas raw ids, and the real send path stores that text.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 24065, "status": "done", "branch": "claude/issue-24065-ooo-notification-names", "pr": "https://github.com/objectstack-ai/objectstack/pull/24146", "session": "session_018VqJPGA1NDNSCZsnecsChP (mode:subagent, shared with the dispatching seat; identity is the branch)", "premise_still_valid": true, "summary": "Premise held on origin/main 27d7c2aa1d: the approval.ooo_substituted (:3624) and approval.ooo_skipped (:3636) bodies interpolated sub.from / sub.to (the delegation's delegator_id / delegate_id, sys_user ids) raw, and notify() hands payload.body to messaging.emit unchanged. The red run showed the bodies as sent ('... while u_ooo is out of office.', '... was routed to u_bob ...'). Both bodies now name each person through resolveUserNames, in ONE call per fan-out before the loop (approval-service.ts:3622 at head; it makes no read when nothing was substituted, and an A to B to C chain is one sys_user read). The fallback is the chain's own: a name if the account has one; the EMAIL if the account row has no name (readUserNames labels it u.name ?? u.email, the same label the approvals door serves; sys_user.name is declared required, so only a name-less row reaches it); the stored user id if there is no row or the read failed (today's text, the spec's 'render the address' rule). The ooo_substitute audit comment keeps the ids by design. Its readers are listActions (served as stored), the console timeline (rendered verbatim at pin f8267df93a), the action list views and export, and the platform checklist item that verifies 'A to B' identities. A name written there would be an un-narrowable stored copy, and the platform's pattern for readable parties is structured fields with read-time *_name companions (reassign). No resolver was added. resolveUserNames, its fallback, its FLS note and the record half are untouched. One patch changeset whose first line after the frontmatter is 'Clause-②: no'. Draft PR #24146 is open with assignee os-elon-musk. Worktree removal (node_modules first, no --force) follows the posting of this comment.", "tests": "Pin in approval-notification-body.integration.test.ts (the real ApprovalService over real ObjectQL on SqlDriver better-sqlite3): a control request ooo_gone (approver u_gone has no sys_user row, delegated to u_alice) and two tests. (1) Each OOO body carries the person's display name and not their id (Olive Away @ooo_substituted, Bob @ooo_skipped, Alice @ooo_skipped of ooo_gone), plus a sweep of every OOO body for any battery user id. (2) Fallback: the ooo_substituted body of ooo_gone names u_gone. RED against the unfixed source (test-only commit 225281a720): 'Tests 1 failed | 6 passed (7)', offenders 'You are covering an approval on Deal D_ooo while u_ooo is out of office.', 'An approval on Deal D_ooo was routed to u_bob while you are out of office.', 'An approval on Deal D_ooo_gone was routed to u_alice while you are out of office.'. GREEN at 26ca62ac57: 7/7. Ablation 1 (scripts/ablation-replace.mjs WRAP; oooPerson returns the bare id): anchor 1 to 0, blob 05d5b39c94e8 to 269142866144, red 1 failed / 6 passed with exactly those three offenders; restore blob 05d5b39c94e8 == HEAD, git diff HEAD empty. Ablation 2 (oooPerson drops the id fallback): blob to c88096bc793b, the fallback pin red with 'You are covering an approval on Deal D_ooo_gone while undefined is out of office.', 6 passed; restore blob == HEAD, diff empty. The subject is imported relatively from src/, so there is no dist leg. Final head de3023f846: 'pnpm --filter @objectstack/plugin-approvals exec vitest run --maxWorkers=2' gives 'Test Files 81 passed (81) / Tests 1107 passed (1107)'. 'pnpm --filter @objectstack/plugin-approvals typecheck' exits 0, and check:test-typecheck holds the ledger at 8 file(s) / 324 error(s). Both runs used os-verify-lock (VERDICT command-exit 0).", "gates": "Local, at head de3023f846 (after merging origin/main 462d02f00a). 'node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack' derived 67 commands (3 changed paths, 99 lines). All 67 ran with each exit recorded before any pipe. First pass: 65 exit 0, and 2 exit 3 (PREREQUISITE NOT MET): pnpm check:dual-build-cjs-loads (no dist/) and pnpm check:i18n (CLI closure unbuilt). I built both prerequisites with the commands the gates print, under the lock (turbo cache 61/61 and 75/76). The re-runs exit 0: check:i18n gives 'OK (10 package(s) — all bundles in sync)', and check:dual-build-cjs-loads gives '111 published require entry point(s) across 69 package(s) load'. 'dispatch-gates --ran' over the final record: '67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Over the first-pass record: '65 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)'. Offline changeset level axis: 'node scripts/check-changeset-no-major.mjs --base origin/main --event' with the PR body exits 0 ('Clause-②: no', patch). CI families: PR #24146 check-runs at head de3023f846 were in_progress or queued when this report was written. Already completed: 'No other open PR may claim the same issue' success, 'The card this PR closes must claim this branch' success, 'Part-of PR must not also close its card' success, 'Check Documentation Links' success, filter success. Lint and Repo Gates, TypeScript Type Check, Test Core 1-6, Build Core and Governed Surface Queue Guard are in_progress or queued. This report does not wait on them.", "mcp_calls": "0", "api_writes": "2 relay strokes, each sent as one POST /repos/objectstack-ai/objectstack/dispatches through scripts/pm/fleet-write and executed as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24146/assignees (os-elon-musk); read-back 10021 bytes sent, 10021 stored, identical. (2) This os-dev-report: POST /repos/objectstack-ai/objectstack/issues/24065/comments via scripts/pm/post-stamped.mjs. Label writes: 0. The dispatch names no label, and the diff carries a changeset, so skip-changeset does not apply. 4 git pushes (empty branch probe, test commit, fix commit, merge commit) are not REST writes.", "deviations": "None against the dispatch. Two notes: (a) origin/main moved by 2 commits after my pre-PR merge at 462d02f00a (9ffe8af094, 5c92b01304; docs/qa JSON and examples/app-showcase only, no overlap with this diff), and I did not merge a second time, per AGENTS.md §10. (b) Commits carry the model-free trailer pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude), not the harness's model-named trailer.", "open_questions": [ { "question": "The resolveUserNames docblock's STORED COPIES warning lists pending_approver_labels and acted_as_label as the stored copies a later sys_user field-level-security narrowing must follow. This PR makes both OOO notification bodies a third stored copy. The claim excluded resolveUserNames itself, so this is recorded only at the call site (approval-service.ts:3609-3616). Should the docblock list name the bodies?", "options": [ "A: the seat widens the claim by one docblock line, and I add it to PR #24146", "B: leave it; the call-site comment is the record, and a narrowing author reaches it by grepping the callers", "C: a separate one-line card" ], "recommendation": "A. Real business need: no field-level-security narrowing of names exists today, so the omission has no cost yet, and the line costs nothing either. Long-term soundness: that list is the coupling's one inventory, and a complete inventory is what stops a narrowing that misses a stored copy. Preventing AI mistakes: an agent narrowing names will read that docblock as the full inventory, and an incomplete one is exactly the trap. Startup focus: it is one comment line, with no new surface and no gate. C costs a dispatch for one line." } ], "out_of_scope_findings": [ "carrier: none (no carrier) · noted, not filed (source reading only, no public-door reach measured): at the console pin f8267df93a, the approvals timeline (ApprovalsInboxPage.tsx actionText; RecordApprovalsPanel.tsx) maps no label for ooo_substitute, so it shows the raw kind, followed by the comment 'u_ooo to u_bob — leave' as raw ids. The structured-parties shape of reassign (reassign_from / reassign_to plus read-time *_name) is the fix pattern that keeps the audit comment as ids. Recorded in the PR's Acceptance notes. Dedupe words: ooo_substitute timeline label, out-of-office audit comment ids, approval timeline raw action kind.", "carrier: none · noted, not filed (existing per-hop design, an observation): in an A to B to C delegation chain, B is told they cover for A although B is also away and the slot went on to C. Recorded in the PR's Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
ACCEPT → PR #24146 comment
6229653706·domain:servicesseat 2 ·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-17T04:30Z. Lands through the merge queue once CI is green on its head.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed ·
domain:servicesseat 2 (#21118) ·os-elon-musk·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-17T05:12Z.Landed: PR #24146 (
Fixes #24065) merged through the merge queue as544b8d7c86. That commit is onorigin/main(git merge-base --is-ancestor). Timeline:merged2026-10-17T05:11Z. ACCEPT6229653706.origin/mainplugin-approvals/src/approval-service.ts: oneresolveUserNamesread for the OOO fan-outoooPerson(sub.from)/oooPerson(sub.to)What a recipient sees: the delegate reads "… while Olive Away is out of office.", and the absent approver reads "… was routed to Bob …". Neither body carries a raw user id any more. A person with no readable name is still named by the stored id. The
ooo_substituteaudit comment keeps the ids, by design.pm:dispatchedis cleared and the assignee is released in this act.Generated by Claude Code