feat(acp): make a silently dropped event visible in the log - #5988
feat(acp): make a silently dropped event visible in the log#5988Chessing234 wants to merge 4 commits into
Conversation
The inbound author gate is the quietest way an agent can ignore you: the event arrives, the agent is online, the mention is correct, and nothing happens. The drop was logged at `debug!`, which is off under the default `buzz_acp=info` filter — so from the operator's side there was no record at all, and working out why meant reading the source (block#5965; the same silence shows up in block#1743 and block#3015). Report the first drop for each author in each channel at `info!`, naming the mode that refused it, and keep every repeat at `debug!` so a chatty channel cannot turn one diagnostic into a flood. The key is per channel, not global: the gate's answer can differ per channel, since a DM resolves fail-closed. `AuthorGateReporter` holds that state so the "have we said this already" decision is a pure function with tests, rather than an inline flag. Signed-off-by: Taksh <takshkothari09@gmail.com>
The drop immediately below the author gate is the other half of the same silence: an event reaches the agent, matches no subscription rule, and vanishes at `debug!`. That is exactly what a forum mention looked like before the default kinds were widened — the agent joined, showed online, and never answered, with nothing in the log to say the kind was never subscribed. Report the first miss per (channel, kind) at `info!` and keep repeats at `debug!`, reusing the same reporter. Keyed by kind rather than author, because that is the thing an operator would act on: it names which kind the subscription does not cover. Signed-off-by: Taksh <takshkothari09@gmail.com>
themiguelamador
left a comment
There was a problem hiding this comment.
Three findings:
AuthorGateReporterretains every(channel, pubkey)for the process lifetime. Pubkeys are attacker-controlled, so key rotation makes the new diagnostic both an unbounded memory sink and an unbounded stream ofinfologs.match_eventreturningNonedoes not mean the kind is absent from the subscription: channel scope, required mention, filter false, and fail-closed filter errors all produce the same result. The new message therefore gives operators a potentially false diagnosis.- The existing
author_alloweddocumentation was accidentally attached to the new reporter, leaving the gate itself undocumented and mixing two contracts.
I fixed all three in signed commit 1e188b27b and published the proposed patch here:
https://github.com/Complear/buzz/tree/review/pr-5988-fix
The patch caps each diagnostic reporter at 256 retained subjects, uses the native u16 kind rather than allocating strings, makes the subscription guidance accurate, restores the displaced documentation, and adds bound coverage.
Verification: cargo fmt --all -- --check; focused reporter tests 4/4; full cargo test -p buzz-acp --lib 782/782; cargo clippy -p buzz-acp --all-targets -- -D warnings; clean diff checks.
Review found three problems with the diagnostic these commits added. **The reporter was unbounded.** Its subject is a pubkey, chosen by whoever is sending, so key rotation alone turned the diagnostic into a memory sink and an unbounded `info!` stream — an attacker-controlled one. The earlier reasoning only covered a single author repeating, not many authors arriving. Cap the remembered subjects at 256; past that nothing more is remembered or reported, with one `warn!` saying so, and drops still land at `debug!`. **The subscription message was a false diagnosis.** `match_event` returns `None` for a channel-scope mismatch, a missing required mention, a filter returning false, and — fail-closed — a disabled rule, a filter timeout or a filter error. Claiming "this kind is not covered by the agent's subscription" would send an operator after the wrong thing, most damagingly in exactly the fail-closed cases. Name the possibilities instead of picking one. **`author_allowed` lost its documentation.** The reporter was inserted between that function and its doc comment, so the DM-hardening contract ended up describing the reporter and the gate itself was left undocumented. Restored, with the reporter given its own. `DropReporter` is now generic over its subject, so the subscription miss keys on the native `u16` kind rather than rendering it to a `String`. Signed-off-by: Taksh <takshkothari09@gmail.com>
|
all three are real and all three are fixed in unbounded reporter. yours. the subject is a pubkey and pubkeys are chosen by whoever is sending, so key rotation alone drives both the memory growth and the false diagnosis. confirmed by reading displaced doc. confirmed — i inserted the struct between also took your verification: |
themiguelamador
left a comment
There was a problem hiding this comment.
Re-reviewed current head 817355dc9. The update correctly resolves all three prior substantive findings: reporter state and first-drop info logging are bounded, event-kind subjects remain native u16, the subscription-miss message no longer over-claims a cause, and the displaced gate documentation is restored.
One minor stale reference remains: the initialization comment at crates/buzz-acp/src/lib.rs:2081 still says to see the removed AuthorGateReporter type. It should name the new DropReporter.
Signed one-line fix: fb0c2782a
https://github.com/Complear/buzz/tree/review/pr-5988-fix-v2
Verified with cargo fmt --all -- --check, all 783 buzz-acp library tests, strict clippy across all targets, the pre-commit Rust formatter, and clean diff checks.
The initialization comment still pointed at `AuthorGateReporter`, the type the previous commit replaced with the generic `DropReporter`. A comment naming a type that no longer exists sends the next reader looking for it. Signed-off-by: Taksh <takshkothari09@gmail.com>
|
fixed in verification: |
Prompted by #5965, where an agent silently ignored every mention from one identity and diagnosing it meant reading buzz-acp's source. This doesn't change what the gate decides — only whether you can see it decide.
Two adjacent drops in the inbound loop were logged at
debug!, which is off under the defaultbuzz_acp=infofilter. Between them they cover the two most confusing failures an agent can have, and in both the agent is online, the mention is correct, and nothing happens:The author gate. Under
--respond-to=owner-onlyevery non-owner is refused; underallowlist, everyone not on the list. From the operator's side there was no record at all. The first drop for each author in each channel now logs atinfo!and names the mode that refused it.A subscription miss. An event arrives, matches no rule, and vanishes. That is exactly what a forum mention looked like before the default kinds were widened — the agent joined, showed online, and never answered, with nothing saying the kind was never subscribed. First miss per (channel, kind) now logs at
info!and names the kind.Both keep every repeat at
debug!, so a chatty channel can't turn one diagnostic into a flood.AuthorGateReporterholds the "have we said this already" state so that decision is a pure function with tests rather than an inline flag. The key is per channel, not global — the gate's answer can differ per channel, since a DM resolves fail-closed.On #5965 specifically: I don't think it's a CLI-vs-Desktop dispatch bug.
author_allowedbranches only on the mode, and the reported symptom (owner dispatches every time, allowlisted key never) is exactlyowner-only, which matches #5858 — the harness readsBUZZ_ACP_RESPOND_TOfrom the instance record, and that issue reports a "Respond to" edit updating the definition while the record and the running process stay atowner-only. I've left a note there rather than guess at a fix; either way, this PR is what would have made it visible in the log instead of requiring that analysis.Verified locally on the pinned 1.95.0 toolchain:
cargo test -p buzz-acp --lib— 781 passed, 0 failed (778 before, plus the 3 new)cargo clippy --workspace --all-targets -- -D warnings— cleancargo fmt --all -- --check— cleanNote: I'm an outside contributor, so the workflow runs here sit at
action_requireduntil a maintainer approves them; only the DCO check reports on its own.