Problem
Nothing records, as a typed field, what KIND of work item a dispatched worker handled. The only carrier is agents[].label, which is free prose the main loop writes:
"Rework cyclo.site#434"
"Fix red PR st0x.deploy#300"
"Mutation pass on PR 160 diff"
"Rework rain.erc4626.words#188"
So grouping comparable work — every rework item, every step-3b red triage — means pattern-matching that prose. That is the exact shape the org has ruled against: a heuristic join over text, where the fix is to capture the fact typed at its source.
Why it blocks measurement
Run cost is dominated by WHICH work the queue served, not by how efficiently it was done. Measured across four producer runs:
| run |
usd |
wall |
worker mean |
work drawn |
| 20260816T170156Z |
18.32 |
31m |
2.42 |
5 step-3b red PRs (triage) |
| 20260817T000148Z |
20.30 |
68m |
2.49 |
5 rework items (CI round-trips) |
| 20260817T050103Z |
10.84 |
28m |
1.24 |
— |
| 20260817T110404Z |
25.50 |
46m |
3.08 |
— |
A 2.4× spread in run cost, with the two runs whose composition is known differing in kind rather than in efficiency. Any before/after comparison over run totals is therefore measuring the queue's draw, not the change under test — which is why the backgrounding change (#327) could not be evaluated: a ~9% effect inside one worker is invisible against that.
The fix is to compare like with like — rework workers against rework workers — and that needs the kind as data, not as a substring of a label somebody wrote for a human to read.
Why not parse the label
- It is written for a reader, so its shape is whatever the main loop chose that run. Nothing constrains it and nothing fails when it changes.
- The vocabulary is not declared anywhere: "Rework", "Fix red PR", "Mutation pass" are observed, not specified, so a matcher is a guess about a set nobody owns.
- A run that phrases it differently silently drops out of the grouping, and the resulting figure is quietly computed over fewer items with no signal that it was.
The producer already CLASSIFIES the item before dispatching it — the prompt's step 3b routing decides red-triage vs rework vs migration vs design. That classification exists and is then thrown away, leaving prose behind.
Proposed fix
Emit the item kind as a typed field on the agents[] entry at dispatch, from the classification the producer already performed — not derived afterwards from the label.
The vocabulary should be the one the routing step already uses rather than a new one invented here; whoever implements this should read campaign-prompt.txt's step 3b and take its categories verbatim, so the field and the routing cannot drift into two different taxonomies. Where an item genuinely does not fit a category, an explicit other is honest and a silently absent field is not.
Note what this is NOT: it is not a regex over label, and it is not a mapping table maintained beside the prompt. Either would reproduce the defect one level down — a second source of truth for a fact the router already knows.
Done when
- Every
agents[] entry carries the item kind as a typed field, written at dispatch.
- The kind's vocabulary is the routing step's own, with a test pinning the two together so a new route cannot appear without the field learning it.
- "Tool calls per rework worker, before vs after" is a query over
metrics/runs.jsonl, with no prose matching anywhere in it.
Related
rainlanguage/issue-pr-cron — per-agent toolCalls (filed alongside this). The two compose: the kind says which workers are comparable, toolCalls says what to compare. Neither is useful alone for this question, and both are useless until enough runs accumulate — KEEP_RUNS was raised from 20 to 2000 on 2026-08-17, so the history starts rebuilding from the next producer tick.
Problem
Nothing records, as a typed field, what KIND of work item a dispatched worker handled. The only carrier is
agents[].label, which is free prose the main loop writes:So grouping comparable work — every rework item, every step-3b red triage — means pattern-matching that prose. That is the exact shape the org has ruled against: a heuristic join over text, where the fix is to capture the fact typed at its source.
Why it blocks measurement
Run cost is dominated by WHICH work the queue served, not by how efficiently it was done. Measured across four producer runs:
A 2.4× spread in run cost, with the two runs whose composition is known differing in kind rather than in efficiency. Any before/after comparison over run totals is therefore measuring the queue's draw, not the change under test — which is why the backgrounding change (#327) could not be evaluated: a ~9% effect inside one worker is invisible against that.
The fix is to compare like with like — rework workers against rework workers — and that needs the kind as data, not as a substring of a label somebody wrote for a human to read.
Why not parse the label
The producer already CLASSIFIES the item before dispatching it — the prompt's step 3b routing decides red-triage vs rework vs migration vs design. That classification exists and is then thrown away, leaving prose behind.
Proposed fix
Emit the item kind as a typed field on the
agents[]entry at dispatch, from the classification the producer already performed — not derived afterwards from the label.The vocabulary should be the one the routing step already uses rather than a new one invented here; whoever implements this should read
campaign-prompt.txt's step 3b and take its categories verbatim, so the field and the routing cannot drift into two different taxonomies. Where an item genuinely does not fit a category, an explicitotheris honest and a silently absent field is not.Note what this is NOT: it is not a regex over
label, and it is not a mapping table maintained beside the prompt. Either would reproduce the defect one level down — a second source of truth for a fact the router already knows.Done when
agents[]entry carries the item kind as a typed field, written at dispatch.metrics/runs.jsonl, with no prose matching anywhere in it.Related
rainlanguage/issue-pr-cron — per-agent
toolCalls(filed alongside this). The two compose: the kind says which workers are comparable,toolCallssays what to compare. Neither is useful alone for this question, and both are useless until enough runs accumulate —KEEP_RUNSwas raised from 20 to 2000 on 2026-08-17, so the history starts rebuilding from the next producer tick.