---
title: Reduce failed tool calls in data-view generation
type: task
status: in_progress
description: >-
  Cut failed tool calls in data-view generation: the candidate handle and --no-warnings shipped, and
  the corpus reads 609 calls over 648 runs as of 2026-10-07; the remaining levers are the emit
  contract wording (ambiguous_table_state) and the plays-view set on the old transport.
aliases:
  - █████████████████████-██████████-██████████-█████-██-██████
base_uri: user:task/league/data-views/reduce-generation-tool-call-failures.md
created_at: '2026-09-10T03:02:48.520Z'
entity_id: 2f997a44-3b91-4ea7-8a4f-b6d7b42625e7
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-10-11T04:41:23.776Z'
visibility_analyzed_at: '2026-11-05T23:48:58.950Z'
---

Cut the failed tool calls out of data-view generation. Measured, not inferred: `base friction transcripts` over the 243-run corpus found 254 failed tool calls across 125 runs (51%), 1.00 per run.

**Where it stands (2026-10-07):** 136 failed calls across 125 runs in the last 25 days (`--since 25d`), 1.09 per run — still above the 1.00 baseline. Top codes: `emit: ambiguous_table_state` 11 in 9 sessions, `validate_table_state: invalid_json` 10, `run_sql: forbidden_schema` 10, `emit: undisclosed_approximation` 11.

Re-derive it any time — one command:

```
base friction transcripts --container base-user-league-data-view-generation--league-data-view-generation --host ████████████
```

## Plan

- [x] Candidate handle (`validate_table_state` banks a threaded `candidate_id`; `preview_view` and `emit` take it instead of the object), `--no-warnings`, the failed-call dimension and the file channel — shipped (see Shipped).
- [ ] Fix the emit contract wording so agents never send `table_state` beside a `candidate_id` (11 `ambiguous_table_state` refusals in 9 sessions to 2026-10-07). Runs via the open continuation █████████████████████████████████████.
- [ ] Extend the candidate pattern to the plays-view set (47 failures across 28 sessions on `scripts/plays-view-{emit,preview-view,validate-table-state}.mjs`).
- [ ] Re-sweep after the wording fix — the 2026-09-10 run is the only one since `ef70e542f` relaxed the both-at-once guard.

## What the measurement actually said

The first read blamed the Bash transport and the shell quoting around it. Measuring the alternative before rewriting it showed otherwise, and the correction is the useful part of this record.

**Failure rate tracks PAYLOAD SIZE, not delivery form.** By command length: 2.8% under 200 chars, 10.7% at 200-500, 19.7% at 500-1000, 21.3% above.

**The apparent form effect is a confound.** Heredoc looks worse than inline echo (15.7% against 9.8%) until you notice heredoc payloads are 3.5x larger (728 against 206 median chars) and 44% of them follow an earlier failure — it is the recovery form, used on the hard payloads.

**And the payload is sent three times.** Large (>500 char) calls split emit 316, preview-view 257, validate-table-state 240 — 3.5 per session. The agent re-emits one table_state to validate, preview and emit it, taking an independent ~20% risk each time.

So the lever is sending it once, and that is transport-independent. MCP was built and then removed (league `ff1fbf1ba`) because it addressed the wrong half at a much higher cost — a hand-rolled protocol, a long-lived process holding sandbox connections for the session, full table_state in every timeline entry, and no Bash fallback if the server failed to start.

## Shipped

- **The candidate handle** — league `57fb5693d`. `validate_table_state` banks the resolved state and returns a content-addressed `candidate_id`, scoped by THREAD_ID; `preview_view` and `emit` take it instead of the object. Both forms still accepted, both-at-once refused.
- **`--no-warnings`** — user-base `b9a338d311`. 127 of 254 failures carried a five-line Node module-type warning; about 36,000 of the 48,000 tokens of error text the agent read was that boilerplate.
- **The failed-call dimension** — league `3e413dcce`. `derive_transcript_metrics` reads tool RESULTS for the first time and reports `failed_tool_call_count`, `failed_buckets` and `failed_codes`, persisted to the benchmark JSONL.
- **The measurement tool** — user-base `32d09926a8`. `base friction transcripts`, which sees sessions whose thread timeline was never reconciled.
- **Identical both-at-once tolerated** — league `ef70e542f` (2026-09-10). `emit` and `preview_view` now refuse a `candidate_id` plus `table_state` only when the two actually differ; `emit` delegates to the shared resolver.
- **File channel for `validate_table_state`** — league `2dba2755a` (2026-09-21). `{"table_state_file": "<path>"}`, so the one call that must carry the whole state has a non-heredoc path.

## Verified

- **Sweep at `57fb5693d`** (2026-09-10, 32 runs, 26 correct): `invalid_json` 74 to 1, `missing_envelope_field` 43 to 0. Failed calls per run rose to 1.56 on the new `ambiguous_table_state` code. See the `[verification]` observation.
- **The handle is adopted**: preview large payloads 185 per session to 1 total, 25 of 30 sessions passed a handle on a large call.

## Remaining

- **`ambiguous_table_state` persists after `ef70e542f`.** 11 refusals in 9 sessions over the last 25 days, every one a genuine mismatch between the handle and the envelope's `table_state`. The agent still sends both. No re-sweep has run since the 2026-09-10 one.
- **The plays-view set.** 47 failures across 28 sessions come from `scripts/plays-view-{emit,preview-view,validate-table-state}.mjs`, which share the shell transport and are untouched. Same candidate pattern applies; three tools rather than six.

## Hazards

- **A green you have not shown can go red is not evidence.** This bit twice already — a probe returned the same credential error for four contradictory inputs and read as a pass, and `deliver_emission` would have shipped an empty view because it kept using the unresolved emission. Both were found by making outcomes differ, not by reading.
- The generation profile permits ONE concurrent session, so sweeps serialize and a stranded session wedges production. `base thread list --running | grep data-view-generation-` finds corpses.
- The profile's `append_system_prompt` is GENERATED. Edit `cli/league/build-data-view-generation-profile.mjs`, run `--write`. `extension/inference/tests/data-view-generation-contract-agreement.test.mjs` is the only thing that catches divergence.
- Turn count is a weak proxy for duration here — the lane's own decomposition puts it at 22% of variance against 88% for per-turn latency. Rank on failed calls.
