mirror of
https://github.com/block/buzz.git
synced 2026-10-28 21:11:48 +00:00
## Why Terminal task correctness alone does not guard Buzz-native collaboration behavior such as exact reply routing, non-waking narrative names, batched agent synthesis, thread isolation, and ambiguous identity targeting. ## What - Add five medium/hard Harbor tasks for multiline delivery, narrative agent names, interleaved agent reports, cross-thread requests, and ambiguous user mentions - Add signed scripted-event fixtures, duplicate-display-name profiles, and public-only evidence export support - Add positive and adversarial verifier fixtures covering dropped inputs, extra posts, incorrect routing, and unintended mentions ## Risk Assessment Low — changes are limited to benchmark tooling and datasets; production behavior is exercised but not modified. ## References - Grounded in behavior reported in `buzz-community` and public issues #5787, #5176, #5839, #4942, #4072, #4303, and #6257 - Live Claude Sonnet 4.6 smoke run: 2/5 passed; the three failures exposed top-level instead of threaded delivery, empty narrative output, and silent ambiguous-identity completion - Live GPT-5.6 Luna run (`buzz-native-solo-luna.yaml`): 3/5 passed. Passed `ambiguous-user-mention`, `cross-thread-requests`, and `interleaved-agent-reports`. Failed `multiline-message` and `narrative-agent-names` because both omitted the event-level user mention despite otherwise-correct content and threading. <img width="1176" height="805" alt="Screenshot 2026-08-21 at 11 11 17 AM" src="https://github.com/user-attachments/assets/fc259ec9-892c-4436-b022-1bf85415617d" /> Signed-off-by: Salman Mohammed <smohammed@squareup.com>
164 lines
5.3 KiB
Python
Executable File
164 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Deterministic verifier for aggregation of batched agent reports."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
REPORTERS = ("Ledger Scout", "Risk Sentinel", "Ops Forecaster")
|
|
LABELS = {"ledger-report", "risk-report", "operations-report"}
|
|
|
|
|
|
def _zero() -> dict[str, float]:
|
|
return {
|
|
"reward": 0.0,
|
|
"reports_delivered": 0.0,
|
|
"inputs_complete": 0.0,
|
|
"answer_correct": 0.0,
|
|
"single_human_callback": 0.0,
|
|
"reporters_not_rementioned": 0.0,
|
|
"reply_to_thread": 0.0,
|
|
"evidence_complete": 0.0,
|
|
}
|
|
|
|
|
|
def score_evidence(evidence: object) -> tuple[dict[str, float], dict[str, Any]]:
|
|
if not isinstance(evidence, dict):
|
|
return _zero(), {"error": "evidence root is not an object"}
|
|
root = evidence.get("task_event_id")
|
|
identities = (
|
|
evidence.get("identities")
|
|
if isinstance(evidence.get("identities"), dict)
|
|
else {}
|
|
)
|
|
user = identities.get("Nora Isabel Grant", {})
|
|
agents = [
|
|
row
|
|
for row in identities.values()
|
|
if isinstance(row, dict) and row.get("role") == "orchestrator"
|
|
]
|
|
agent_pubkey = agents[0].get("pubkey") if len(agents) == 1 else None
|
|
directory = {
|
|
row.get("name"): row
|
|
for row in evidence.get("directory", [])
|
|
if isinstance(row, dict)
|
|
}
|
|
reporter_pubkeys = {
|
|
directory[name].get("pubkey") for name in REPORTERS if name in directory
|
|
}
|
|
scripts = [
|
|
row for row in evidence.get("scripted_events", []) if isinstance(row, dict)
|
|
]
|
|
script_ids = {row.get("event_id") for row in scripts if row.get("label") in LABELS}
|
|
messages = [row for row in evidence.get("messages", []) if isinstance(row, dict)]
|
|
delivered_ids = {row.get("id") for row in messages}
|
|
reports_delivered = float(
|
|
len(script_ids) == 3 and None not in script_ids and script_ids <= delivered_ids
|
|
)
|
|
candidates = [
|
|
row for row in messages if agent_pubkey and row.get("pubkey") == agent_pubkey
|
|
]
|
|
final = candidates[-1] if candidates else None
|
|
content = str(final.get("content", "")) if final else ""
|
|
mentioned = set(final.get("mentioned_pubkeys", [])) if final else set()
|
|
inputs_complete = float(
|
|
all(
|
|
re.search(pattern, content, re.IGNORECASE)
|
|
for pattern in (
|
|
r"north\D+17",
|
|
r"south\D+23",
|
|
r"reserve\D+11",
|
|
r"multiplier\D+3",
|
|
)
|
|
)
|
|
)
|
|
answer_correct = float(
|
|
bool(
|
|
re.search(
|
|
r"(?:final|result|forecast|=)\D*87(?:\D|$)",
|
|
content,
|
|
re.IGNORECASE,
|
|
)
|
|
)
|
|
)
|
|
human_callbacks = [
|
|
row
|
|
for row in candidates
|
|
if user.get("pubkey") in row.get("mentioned_pubkeys", [])
|
|
]
|
|
single_human_callback = float(
|
|
len(human_callbacks) == 1 and final in human_callbacks
|
|
)
|
|
reporters_not_rementioned = float(
|
|
len(reporter_pubkeys) == 3
|
|
and all(
|
|
set(row.get("mentioned_pubkeys", [])).isdisjoint(reporter_pubkeys)
|
|
and not any(f"@{name}" in str(row.get("content", "")) for name in REPORTERS)
|
|
for row in candidates
|
|
)
|
|
)
|
|
reply_to_thread = float(
|
|
final is not None and final.get("reply_to_event_id") == root
|
|
)
|
|
evidence_complete = float(
|
|
evidence.get("schema_version") == 1
|
|
and evidence.get("task_name") == "interleaved-agent-reports"
|
|
and evidence.get("truncated") is False
|
|
and len(agents) == 1
|
|
and len(directory) == 3
|
|
and final is not None
|
|
and len(candidates) == 1
|
|
)
|
|
values = (
|
|
reports_delivered,
|
|
inputs_complete,
|
|
answer_correct,
|
|
single_human_callback,
|
|
reporters_not_rementioned,
|
|
reply_to_thread,
|
|
evidence_complete,
|
|
)
|
|
metrics = {
|
|
"reward": float(all(value == 1.0 for value in values)),
|
|
"reports_delivered": reports_delivered,
|
|
"inputs_complete": inputs_complete,
|
|
"answer_correct": answer_correct,
|
|
"single_human_callback": single_human_callback,
|
|
"reporters_not_rementioned": reporters_not_rementioned,
|
|
"reply_to_thread": reply_to_thread,
|
|
"evidence_complete": evidence_complete,
|
|
}
|
|
return metrics, {
|
|
"scripted_event_ids": sorted(str(value) for value in script_ids),
|
|
"selected_message_id": final.get("id") if final else None,
|
|
"content": content,
|
|
"mentioned_pubkeys": sorted(mentioned),
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--evidence", type=Path, required=True)
|
|
parser.add_argument("--reward", type=Path, required=True)
|
|
parser.add_argument("--details", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
try:
|
|
metrics, details = score_evidence(
|
|
json.loads(args.evidence.read_text(encoding="utf-8"))
|
|
)
|
|
except (OSError, json.JSONDecodeError) as error:
|
|
metrics, details = _zero(), {"error": str(error)}
|
|
args.reward.write_text(json.dumps(metrics, sort_keys=True) + "\n", encoding="utf-8")
|
|
args.details.write_text(
|
|
json.dumps(details, indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|