opencode 1.17.15 — grep tool wire result format (captured 2026-07-13, Arm 0)

When the serve emits a `grep` tool_call, opencode executes it and returns the
result as a standard OpenAI role:"tool" message (matching tool_call_id) whose
`content` string is:

    Found <N> matches
    <absolute-path-1>:
      Line <n>: <matching line text>
      Line <m>: <matching line text>
    <absolute-path-2>:
      Line <k>: <matching line text>

Example (pattern "def mean", 1 match):

    Found 1 matches
    /abs/.../capture-repo/metrics.py:
      Line 1: def mean(values):

Normalizer notes (grep->read):
- Absolute paths, one per matching file, each terminated by ":".
- Matches under each file are indented two spaces: "  Line <lineno>: <text>".
- Header line "Found <N> matches" (N may be 0).
- grep INPUT schema (advertised): {pattern (required, regex), path?, include?}.
- Contrast glob: glob output is bare newline-joined absolute paths, no header,
  no line numbers. grep adds the header + per-file grouping + line hits.
- Captured via fake-SSE probe grep_probe.py (emits one grep tool_call, dumps
  opencode's follow-up). Full envelope: grep-followup-request-full.json (also
  shows opencode's system prompt + all 16 advertised tool schemas + tool
  results riding the wire as role:"tool" content strings).

---
glob brace-alternation (captured 2026-07-14 via tool_probe.py): opencode 1.17.15
glob SUPPORTS brace expansion. Pattern `**/*{metrics,calc}*` returned calc.py,
metrics.py, test_metrics.py (all files whose basename contains any listed stem).
So a multi-stem explain question can search all candidate stems in ONE glob
round: `**/*{stem1,stem2,...}*`, then filter .py / not test_ / one-or-refuse.

---
AMENDMENT (2026-08-13, #121 design pre-flight — binary extraction of
opencode 1.17.15's Service.grep, verified via `opencode debug rg search`,
which invokes the same code path):
- REFUTED: "N may be 0". An empty result is the literal string
  `No files found` — never "Found 0 matches".
- The client caps at 100 matches and computes the header count FROM the
  capped array, so header-count-vs-listed-lines arithmetic can never
  detect a client-side cut. The real truncation signals: a
  ` (more matches available)` header suffix, a trailing
  `(Results truncated. Consider using a more specific path or pattern.)`
  footer line, and `metadata.truncated`.
- File groups are separated by BLANK LINES (the single-file capture
  above never showed one).
- The grep TRAVERSES HIDDEN DIRS (unlike the client's glob, which cannot
  list them) — .llm-orc, .claude/worktrees etc. appear in results.
- The first-100 cut is NONDETERMINISTIC (parallel traversal, no sort
  before the limit): identical invocations return different sequences.
- Measurement instrument for grep-shaped design work:
  `opencode debug rg search <pattern> --glob '*.py' --limit 100`.
