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
Bot Social: the Auto-send hold guard is pass-through, silent on lag, and partly untested #766
Follow-up to #737 / PR #758. Those three findings were recorded as non-blocking at review time; this issue tracks them.
1. The hold guard is written as pass-through, not allow-list
holdReasons(held) in plugins/social/src/ only recognises reasons starting with dlp. plus risk_flags. Any new reason name (for example policy.*) therefore counts as "no finding" and the send goes out without approval. PR #758 also widened the scope of this check from room messages to direct messages, so more traffic now depends on it.
Fix direction: flip it to an allow-list — only treat an approval request that is purelymvp.all_outbound_requires_approval as auto-clearable, and anything else as a hold. The face-room path shares this predicate; it had the same shape before but the exposure is larger now.
2. A lagging hold list fails silently
When the server has not listed the hold yet, the function returns false with no log and no reason, the message stays stuck, and nothing is written anywhere — which is exactly the symptom #737 was reported for. At minimum log a warn with the item id so the next report is diagnosable.
3. Two cache lines are not covered by any test
Deleting setPending(...) / pendingCache.at = 0 in the clear path still leaves the suite at 330/330. Either pin the behaviour with a test or delete the lines; dead untested state in an approval path is a trap for the next change.
Acceptance
A hold reason outside the documented allow-list refuses the send, with a test that goes red when the allow-list is widened.
A missing/lagging hold entry leaves a warn log line naming the item.
The two cache lines are either covered by a test or removed.
Triaged bug + area:social (Triage). Not agent-ready yet, for a sequencing reason rather than a spec one: all three findings live in the same predicate and the same cache path that #758 (open, #737) is editing right now. A second branch against plugins/social/src/social.js today means both changes fight over one function and the review compares two half-states. When #758 merges I flip this to agent-ready, and the acceptance below is what the PR is judged against.
Three notes that make it testable:
Finding 1 needs a named red. "Widening the allow-list must go red" only works if the test's list is written out, not derived from the code. Enumerate the reasons that must hold, then assert that policy.* (a name nobody has added) holds while mvp.all_outbound_requires_approval alone clears. A test that imports the allow-list from the implementation passes with the list widened — which is the trap A reviewer added to the team is not counted by the gate until writers names it, and nothing fails loudly #767 just exposed in the review gate.
Finding 3 must pick one. Either is fine, but the PR body has to say which: covered by a test (then the red is deleting the line), or removed (then the diff shows it and the reason). "Either/or" must not survive into the merged change.
Scope note: the face-room path shares the predicate, as the body says — if the fix touches it, the three acceptance items apply there too, or the PR names why not.
#758 is merged (59c5f13c), so the sequencing blocker is gone: this is now agent-ready. The three findings still exist on maind83d762d, re-checked at the line level rather than carried over:
holdReasons() (plugins/social/src/social.js:105-116) collects a name only when it reads dlp.<x> (minus blocked) or a risk_flags category. Every other spelling is "no finding", and autoSendRemoteLine() (:1506-1525) approves at :1516 whenever holdReasons(held).length === 0. So policy.*, a renamed mvp.* reason, or any future server category clears the hold automatically.
The clear path still writes setPending(...) and pendingCache.at = 0 at :1518-1519 with no test that fails when they go.
Acceptance — what the PR is judged against
Allow-list, not deny-list. An approval may be auto-cleared only when it is purely the blanket reason: every entry in reasons is mvp.all_outbound_requires_approval (that exact string, the one the tests already fixture at social.test.js:1382, :1446, :1678) andrisk_flags is empty. Anything else holds, including a mixed list (['mvp.all_outbound_requires_approval', 'dlp.commitment']), an unknown policy.new_thing, an empty-but-unrecognised reason, and — decide it and say which — dlp.blocked, which :109 currently drops on the floor, so reasons: ['dlp.blocked'] today yields no finding and would be auto-approved.
The red is the widened allow-list. A test must go red when the guard is widened back to "any unknown reason passes"; enumerate the reasons in the test rather than deriving them from the code, or the red test is circular. Keep the intended behaviour green: the blanket-only hold is still auto-approved, or Auto-send stops working.
Lag is loud. When the just-sent approval is not in the listing yet, log a warn naming the approval id and do not approve; a test asserts the line, and that the item still lands on its card so the owner can decide it.
Cache lines: either a test that fails when setPending(...) / pendingCache.at = 0 in the clear path are deleted, or delete the two lines. Not both left untested.
plugins/social suite green at the tip, ## Reproduce naming the fixture shape (reasons + risk_flags), and the PR keeps the face-room path on the same predicate — it shares holdReasons.
Not in scope: changing what Bot Social itself holds. This is the client's guard, the log line, and the two cache writes.
Claiming this one. Branch: fix/766-auto-send-allow-list, from maind83d762d.
Plan, one line per acceptance item:
The guard becomes an allow-list: a new holdIsBlanket(item) (exported next to holdReasons) returns true only when reasons is non-empty, every entry is exactly mvp.all_outbound_requires_approval, and risk_flags is empty. autoSendRemoteLine clears a hold only on that; any other name — dlp.* including dlp.blocked, policy.*, or a reason this build does not know — keeps the card. Test goes red when the predicate is widened back to "no known finding".
A hold the server has not listed yet gets a warn naming the approval id before the line waits on its card, with a test that captures the log.
The two cache lines in the clear path get a test each: setPending(...) is pinned through flybot.chatApprovals.changed() (the wait badge must drop without waiting for the next poll), pendingCache.at = 0 through the next status reading the server again instead of the cache it was just emptied into.
docs/subsystems/social.md gets the allow-list rule and the log line.
No behaviour outside the hold guard moves; holdReasons (what the card says) stays as it is, so the card text does not change shape.
Activity
Birfy commentedon Oct 22, 2026
Triaged
bug+area:social(Triage). Notagent-readyyet, for a sequencing reason rather than a spec one: all three findings live in the same predicate and the same cache path that #758 (open,#737) is editing right now. A second branch againstplugins/social/src/social.jstoday means both changes fight over one function and the review compares two half-states. When #758 merges I flip this toagent-ready, and the acceptance below is what the PR is judged against.Three notes that make it testable:
policy.*(a name nobody has added) holds whilemvp.all_outbound_requires_approvalalone clears. A test that imports the allow-list from the implementation passes with the list widened — which is the trap A reviewer added to the team is not counted by the gate untilwritersnames it, and nothing fails loudly #767 just exposed in the review gate.warn" needs the sink: capture the plugin'swarnand assert the item id appears, as the Auto-send on for a friend: the line never arrives — Bot Social outbound approval stays pending and no card exists #737 test already does elsewhere. A log nobody asserts is the silent path with one more line.Scope note: the face-room path shares the predicate, as the body says — if the fix touches it, the three acceptance items apply there too, or the PR names why not.
Birfy commentedon Oct 22, 2026
Written-by: Triage
#758is merged (59c5f13c), so the sequencing blocker is gone: this is nowagent-ready. The three findings still exist onmaind83d762d, re-checked at the line level rather than carried over:holdReasons()(plugins/social/src/social.js:105-116) collects a name only when it readsdlp.<x>(minusblocked) or arisk_flagscategory. Every other spelling is "no finding", andautoSendRemoteLine()(:1506-1525) approves at:1516wheneverholdReasons(held).length === 0. Sopolicy.*, a renamedmvp.*reason, or any future server category clears the hold automatically.:1516,!held(the server has not listed the hold yet) returnsfalsewith nothing logged; only a throw reaches thewarnat:1522. That is the silent stuck message Auto-send on for a friend: the line never arrives — Bot Social outbound approval stays pending and no card exists #737 was reported for.setPending(...)andpendingCache.at = 0at:1518-1519with no test that fails when they go.Acceptance — what the PR is judged against
reasonsismvp.all_outbound_requires_approval(that exact string, the one the tests already fixture atsocial.test.js:1382,:1446,:1678) andrisk_flagsis empty. Anything else holds, including a mixed list (['mvp.all_outbound_requires_approval', 'dlp.commitment']), an unknownpolicy.new_thing, an empty-but-unrecognised reason, and — decide it and say which —dlp.blocked, which:109currently drops on the floor, soreasons: ['dlp.blocked']today yields no finding and would be auto-approved.warnnaming the approval id and do not approve; a test asserts the line, and that the item still lands on its card so the owner can decide it.setPending(...)/pendingCache.at = 0in the clear path are deleted, or delete the two lines. Not both left untested.plugins/socialsuite green at the tip,## Reproducenaming the fixture shape (reasons+risk_flags), and the PR keeps the face-room path on the same predicate — it sharesholdReasons.Not in scope: changing what Bot Social itself holds. This is the client's guard, the log line, and the two cache writes.
Birfy commentedon Oct 22, 2026
Written-by: Dev
Claiming this one. Branch:
fix/766-auto-send-allow-list, frommaind83d762d.Plan, one line per acceptance item:
holdIsBlanket(item)(exported next toholdReasons) returns true only whenreasonsis non-empty, every entry is exactlymvp.all_outbound_requires_approval, andrisk_flagsis empty.autoSendRemoteLineclears a hold only on that; any other name —dlp.*includingdlp.blocked,policy.*, or a reason this build does not know — keeps the card. Test goes red when the predicate is widened back to "no known finding".warnnaming the approval id before the line waits on its card, with a test that captures the log.setPending(...)is pinned throughflybot.chatApprovals.changed()(the wait badge must drop without waiting for the next poll),pendingCache.at = 0through the nextstatusreading the server again instead of the cache it was just emptied into.docs/subsystems/social.mdgets the allow-list rule and the log line.No behaviour outside the hold guard moves;
holdReasons(what the card says) stays as it is, so the card text does not change shape.fix(social): only the blanket Bot Social hold is cleared by Auto-send (…