The Tax Agent's Workers Counted March Twice and Skipped Late June. Make Every Delegation Return the Range It Actually Covered.
Splitting a year of transactions across four workers looks like clean delegation until the merged total is off and every worker reports success. The fix is a coverage contract the supervisor checks before it adds anything up.
A supervisor agent is asked to total a household's deductible expenses for the tax year. It splits the job four ways, hands each worker a quarter, collects four tidy summaries, and adds them up. Every worker reports success. The total is wrong, because March was counted by two workers and the last three days of June were counted by none.
Nothing in that run crashed. No tool returned an error. The failure lives entirely in the space between what the supervisor meant to delegate and what each worker actually did, and a plain summary hides that space completely.
🔍 How the gap and the overlap got in
Here is the brief the supervisor wrote, more or less:
Worker A: review transactions January through March
Worker B: review transactions March through June
Worker C: review transactions July through September
Worker D: review transactions October through December
The typo in Worker B's brief is the kind of thing a language model produces constantly: it meant "April through June" and wrote a range that reads naturally. Worker B took it literally, which is the correct behaviour for a worker. March got counted twice.
The June gap is subtler. Worker B fetched data by downloading monthly statements, and the bank's June statement closed on the 27th. The worker processed every statement it was given, wrote "Reviewed April–June, 214 transactions, $3,880 deductible," and stopped. Its summary repeated the brief's range back. It did not report the range of the data it had actually touched.
So the supervisor received four summaries that, read together, claimed full-year coverage. Two independent faults, one in the delegation and one in the data, both invisible in the merge.
Why "ask the workers to be careful" doesn't help
The tempting fix is a better prompt: tell workers to double-check their dates, tell the supervisor to write clearer briefs. Both help a little and neither closes the hole, for one structural reason: each worker can only see its own slice. Worker B has no way of knowing Worker A already covered March. Worker C has no way of knowing anyone stopped at June 27th. Overlaps and gaps are properties of the union of the work, and only the supervisor sees the union.
That means the check has to live at the merge, and it has to operate on facts the workers report about their data, not on the workers' restatement of their instructions.
🧩 The coverage contract
Treat every delegation of partitioned work as a contract with three parts.
1. The supervisor assigns machine-readable scope, not prose
Ranges go out as structured, half-open intervals generated by code, not written by the model:
{ "worker": "B", "field": "posted_date", "from": "2025-04-01", "to_exclusive": "2025-07-01" }
Half-open intervals (from inclusive, to_exclusive exclusive) make adjacent ranges meet without overlap by construction. "Through March" stops being ambiguous because nobody writes it. The model can still decide how to split the work; a small function turns that decision into intervals and asserts they tile the whole period before anything is sent.
2. Workers return observed coverage, derived from data
Each worker's result includes what it actually saw, computed from the records it processed:
{
"worker": "B",
"assigned": { "from": "2025-04-01", "to_exclusive": "2025-07-01" },
"observed": { "min_date": "2025-04-01", "max_date": "2025-06-27", "record_count": 214 },
"sources": ["stmt-2025-04", "stmt-2025-05", "stmt-2025-06"],
"total_deductible": 3880.00
}
The observed block must come from the data, ideally computed by the tool layer rather than written by the model. A model asked to summarise its range will tend to echo the brief. A min() and max() over the processed rows cannot.
3. The supervisor reconciles before it adds
Before summing anything, the supervisor runs a check over all returned contracts:
def reconcile(results, period_start, period_end_exclusive):
problems = []
spans = sorted((r["assigned"]["from"], r["assigned"]["to_exclusive"], r["worker"]) for r in results)
cursor = period_start
for start, end, w in spans:
if start > cursor:
problems.append(f"gap in assignment {cursor}..{start}")
if start < cursor:
problems.append(f"overlap in assignment at {start} ({w})")
cursor = max(cursor, end)
if cursor < period_end_exclusive:
problems.append(f"gap in assignment {cursor}..{period_end_exclusive}")
for r in results:
if r["observed"]["max_date"] < day_before(r["assigned"]["to_exclusive"]):
problems.append(f"{r['worker']} data ends {r['observed']['max_date']}, assigned through {r['assigned']['to_exclusive']}")
if r["observed"]["min_date"] > r["assigned"]["from"]:
problems.append(f"{r['worker']} data starts late at {r['observed']['min_date']}")
return problems
The first loop catches the March overlap, even if the intervals were hand-edited later. The second catches the June gap: Worker B's observed data ends on the 27th while its assignment runs to the 30th.
A short observed range is not always a failure. Nobody may have spent money on June 28th. So the supervisor's response to a mismatch is not to fail the run but to ask a narrower question: re-dispatch a worker for just 2025-06-28 to 2025-07-01, against a different source such as the transaction feed rather than the statement. If that comes back empty, the gap is confirmed as genuinely empty and recorded as such.
⚠️ What the merged output should say
The final answer carries its coverage alongside its number:
Deductible total: $X. Coverage: 2025-01-01 to 2025-12-31, four partitions, no overlaps. One gap (2025-06-28 to 2025-06-30) re-checked against the transaction feed: zero transactions.
That line costs a few tokens and changes what a reader can trust. A total without coverage is a claim. A total with reconciled coverage is a claim plus its evidence.
Where else this shape appears
The tax example is tidy because dates sort. The same contract works anywhere a supervisor partitions work:
- Research: workers split a list of sources; each returns the IDs it actually read, and the supervisor diffs that against the assigned list.
- Support triage: workers split a ticket backlog by queue; each returns the ticket IDs it closed or deferred, and the union must equal the backlog.
- Code review: workers split a diff by file; each returns the file paths and line ranges it inspected.
The dimension changes (dates, IDs, paths), but the three parts stay the same: structured scope out, observed coverage back, reconciliation before merge.
The takeaway
Partitioning is the easy half of multi-agent work. The hard half is proving the pieces add back up to the whole, and no individual worker can prove it, however careful it is. Put the proof at the merge, make workers report what the data showed rather than what the brief said, and treat any mismatch as a narrower question to send back out instead of a rounding error to absorb.