Found by a binary-ablation study (v0.0.236 vs main, 3 runs per binary on an image task under a text-only deepseek root).
Evidence
Routing works exactly as designed: all 3 NEW runs seated codex/gpt-5.6-luna with source=vision-ask and the trace event vision ask: re-routed an automatic seat to a vision-capable model. But:
- Code:
vision.stageImagePath is called only from readline.zig (Ctrl-V/drag-drop), vision.zig (@[path] in a user message), and commands_model.zig (/image, /paste). Agent.pending_image is consumed only in mainloop.zig on ctx.root. subagent_run.zig never sets pending_image and never calls vision.imageMessage — a worker brief naming an image path carries text only.
- Runs: every re-routed worker answered via
bash, decoding the PNG with code; none ingested pixels. Answers were right only because a 64×64 solid swatch is trivially decodable in shell. The vision-capable seat received no image.
- Cost side effect: on this task the reroute was a net metered regression (~3.5×, $0.0058 vs $0.0016 avg) — moving the worker off the root's provider split the prompt cache. Paying for a vision seat that gets no vision is the worst of both.
Fix
When the #380 detection identifies a concrete local image path in a subagent task (the detection contract already exists in vision_ask.zig), stage the image into the worker's first message via the existing vision.stageImagePath/imageMessage machinery — same size caps and media-type checks as the paste path (#258/#349). Then the reroute buys what it pays for, and the honesty flag becomes the fallback rather than the ceiling. The workflow-phase path should get the same treatment or an explicit documented exclusion.
Acceptance: the ablation's T2 rerun shows the worker answering WITHOUT shell-decoding (no bash pixel probing; tool list shows the answer written directly), on a non-trivially-decodable image (e.g. a photo where shell decoding is impractical).
🤖 Generated with Claude Code
https://claude.ai/code/session_01QbsV84fdmf39Bh2RF8LdPs
Found by a binary-ablation study (v0.0.236 vs main, 3 runs per binary on an image task under a text-only deepseek root).
Evidence
Routing works exactly as designed: all 3 NEW runs seated
codex/gpt-5.6-lunawithsource=vision-askand the trace eventvision ask: re-routed an automatic seat to a vision-capable model. But:vision.stageImagePathis called only from readline.zig (Ctrl-V/drag-drop), vision.zig (@[path]in a user message), and commands_model.zig (/image,/paste).Agent.pending_imageis consumed only in mainloop.zig onctx.root.subagent_run.zignever setspending_imageand never callsvision.imageMessage— a worker brief naming an image path carries text only.bash, decoding the PNG with code; none ingested pixels. Answers were right only because a 64×64 solid swatch is trivially decodable in shell. The vision-capable seat received no image.Fix
When the #380 detection identifies a concrete local image path in a subagent task (the detection contract already exists in vision_ask.zig), stage the image into the worker's first message via the existing
vision.stageImagePath/imageMessagemachinery — same size caps and media-type checks as the paste path (#258/#349). Then the reroute buys what it pays for, and the honesty flag becomes the fallback rather than the ceiling. The workflow-phase path should get the same treatment or an explicit documented exclusion.Acceptance: the ablation's T2 rerun shows the worker answering WITHOUT shell-decoding (no bash pixel probing; tool list shows the answer written directly), on a non-trivially-decodable image (e.g. a photo where shell decoding is impractical).
🤖 Generated with Claude Code
https://claude.ai/code/session_01QbsV84fdmf39Bh2RF8LdPs