agent-compat: bidirectional inbox eval (console, issue #108)#250
Conversation
Two interactive_simulation cases exercise the human<->agent console from both sides: TC-INBOX-PROMPTED (agent told to read the console + reply) and TC-INBOX-UNPROMPTED (only the push-time 📬 nudge signals unread input). The console note is pre-seeded out-of-band (seed_inbox.py -> POST /inbox, gated by SEED_INBOX=1) since it doesn't arrive through the agent-generator conversation. It asks for two things a default login screen never has (a maintenance banner + a 'Beam me in' button label), so a generic-but-nice login form can't pass by luck. Each case gates on a deterministic pull+grep for the console-only string first, with the tc-judge rubric secondary; run at CONCURRENCY=1 (shared viewer -> a scoped pull can cross-read under CONCURRENCY>1, which the LLM judge passes). judge_board.py gains --rubric-name so cases can share one rubric across scopes.
Cross-agent reproduction + nudge confirmationGemini CLI (
So finding #1 (unprompted The nudge does fire — verified directly against a local viewer (seed an unread console message, push as a fresh agent): The failure is reception, not emission: the agent sees the Possible product follow-ups (out of scope for this eval PR): make the nudge imperative in the skill contract (e.g. |
…structured transient check) - seed_inbox.py: catch (URLError, TimeoutError, ValueError) instead of Exception; count only real scopes in the summary line (doc keys were inflating it) - judge_board.py: retry loop catches (APIError, httpx.HTTPError, JSONDecodeError, TimeoutError, ConnectionError) and classifies transient via APIError.code (429/5xx) instead of substring-matching the error message; justify the function-scoped genai import (optional-dep gating) Verified: --help imports clean on host python (no genai); real judge call passes; 404 (non-transient) fails fast in 2.1s with no backoff; connection-refused seed prints per-event FAIL + correct scope count.
Coding-standards review pass (dev-coding-standards)Audited the PR's Python (
Verified at the surface, not just compiled: Conscious deviations from the standards (documented, not oversights):
|
…l findings) (#252) * fix(cli): make the console nudge actionable + accept positional reply text (inbox eval findings) The inbox eval (PR #250) showed both Claude and Gemini ignore the count-only 📬 nudge when nothing in their task mentions the console: they read the line, finish the literal task, and the human's message is never applied (0/3 runs). It also showed both agents' first reply attempt is positional (`reply <scope> "text"`), which failed and cost a --help round-trip (3/3 runs). - inbox-nudge: when the unread header fires, fetch the unread events (?all=1 — peek, URL-gated) and print the message content inline plus an explicit act-before-finishing instruction. Feedback inside tool output the agent is already reading is the one channel that reliably lands (same pattern that fixed --type selection). Content fetch is best-effort; the imperative nudge prints regardless. Ack semantics unchanged (only a real `termchart inbox` read advances the receipt). - reply: bare words are now the message (joined); --message still wins; unknown flags still rejected; the missing-message error names both forms. - termchart front-door skill: document the 📬 contract (apply + mark read + reply before finishing); plugin 0.16.3. Verified end-to-end: eval TC-INBOX-UNPROMPTED flips FAIL(3/3)→PASS with the agent visibly acting on the inline nudge, and TC-INBOX-PROMPTED passes with zero reply --help detours (was 100%). Unit: 10/10 on the touched files. * test: update push nudge assertion to the new actionable wording --------- Co-authored-by: Ivan Cheung <ivanmkc@google.com>
What
Adds an agent-generator eval for the bidirectional console (issue #108) to the
agent-compatharness, exercising the human↔agent inbox from both sides, plus a deep-dive on how Claude actually behaves.Two
interactive_simulationcases (case-settermchart-inbox):📬nudge the CLI prints on push. Tests the core UX claim: does the agent notice unprompted human input on its own?The console note is pre-seeded out-of-band (
seed_inbox.py→POST /inbox, gated bySEED_INBOX=1) because it doesn't arrive through the agent-generator conversation. It asks for two things a default login screen never has (a maintenance banner + aBeam me inbutton label) so a good-but-generic login form can't pass by coincidence.Each case gates on a deterministic pull+grep for the console-only string first, with the
tc-judgerubric secondary.judge_board.pygains--rubric-nameso both cases share one rubric.Findings (Claude Code,
claude -p)termchart inboxtermchart reply📬nudge (headline). In every unprompted run it ran onlypush+list, saw📬 1 console message …in stderr, and finished — never reading the inbox. The "notice unprompted human input" claim doesn't hold once the agent has satisfied the literal task; the passive stderr nudge reads as informational.termchart replypositional-arg friction. Claude's first reply attempt was alwaystermchart reply <scope> "text"(positional), which fails; it recovered via--help→--message. Reply should accept a positional message, or the error should name the required--messageflag.tc-judge(LLM) +CONCURRENCY>1on a shared viewer let a scoped pull cross-read another case's board, passing a real failure. Fixed by login-alien asks, a deterministic pull+grep gate, and running atCONCURRENCY=1.Run it
Draft — this is an eval/harness addition (no product code changes). The product takeaways (findings 1 & 2) are candidates for follow-up.