Repository navigation
feat(telemetry): a flow the tool named is a flow - #768
Merged
Merged
Conversation
`by_flow` decided a record's flow from journal intervals and nothing else. A record whose own transcript states the orchestrating skill it ran under was reported as belonging to no flow, however plainly it said so. Measured on this machine's sink, 30 days, 30,222 requests: `by_step` names `aidd-orchestrator:01-sdlc` on 2,220 requests as `tool-stated`, and `by_flow` placed 2,208 of them outside every flow. They come from a session whose journal holds six `step_end` lines and no `step_start` at all — the shape a session resumed after its context was compacted leaves behind, since nothing is invoked again and no hook fires, while the transcript goes on stating the step on every record it produces. `flowKeyOf` gains one fallback: a record no interval covers, whose own `step_attribution` is `tool-stated` and whose `step` names an orchestrating skill, joins a row for that skill. The row is a distinct kind, keyed on the name, carrying `attribution: "tool-stated"` and no `started_at` — a name is not a run, so it can neither tell two runs apart nor claim a single opening moment. An interval still wins where a record falls inside one, and the reason is granularity, not strength: `withStepBackfill` prefers a tool-stated step over a journal-interval one because there the question is *which skill*; here it is *which run*, and only an interval can say. Twelve records show it in the real data — tool-stated and inside the interval, they stay on the interval's row. `by_flow` still reconciles: the unnamed row drops by exactly what the new row gains. No envelope bump — the flow row gains a field a consumer may ignore, and no existing field changes meaning. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
blafourcade
force-pushed
the
fix/a-flow-the-tool-named-is-a-flow
branch
from
September 4, 2026 22:30
22f6f4b to
56c03e2
Compare
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
by_flowdecided a record's flow from journal intervals and nothing else (cost-report.ts'sflowKeyOf). A record whose own transcript states the orchestrating skill it ran under was reported in the unnamed row, however plainly it said so.Measured on this machine's sink,
--days 30, 30,222 requests, on the binary built from9fc819ef:aidd-orchestrator:01-sdlcby_steptool-statedby_stepjournal-intervalby_flowThe two axes were not disagreeing about one flow. They were looking at two sessions: the 1,052 come from the session holding the corpus's only orchestrating
step_start; the 2,220 come from a second session whose journal has sixstep_endlines and nostep_startat all.Why a session states a step its journal never opened
step_startis written by a hook when a skill is invoked. A session resumed after its context was compacted carries its skills forward in the restored context, so nothing is invoked again and no hook fires — while the transcript goes on stating the step on every record it produces. That journal shows exactly this: ends without starts.Not an error to repair at the write path. It is a capture the journal cannot make, and a second source that already made it.
Change
flowKeyOfgains one fallback. A record no interval covers, whose ownstep_attributionistool-statedand whosestepnames an orchestrating skill, joins a row for that skill.attributionstarted_atFlowIntervalobject, by referencejournal-intervaltool-statedKeeping them apart preserves what
buildFlowIntervalsargues for: two orchestrated runs of one skill in one session are two rows, because two intervals are two objects. A name is not a run, so the tool-stated row cannot make that distinction and does not pretend to.An interval wins over a tool-stated step, and the reason is granularity, not strength.
withStepBackfillprefers a tool-stated step over a journal-interval one; this preference runs the other way, and the two are not in conflict. There the question is which skill — the tool naming its own beats an inference from a moment. Here it is which run — and only an interval can say.Proof, two binaries against one sink
by_flowrow9fc819efaidd-orchestrator:01-sdlc,journal-intervalaidd-orchestrator:01-sdlc,tool-statedThe unnamed row drops by exactly 2,208, and all eleven breakdowns sum to 30,222 on both. 2,208, not 2,220: the twelve missing are tool-stated records that fall inside the journal's own interval — the precedence rule visible in real data, staying on the interval's row rather than being moved or double-counted.
Guards
ORCHESTRATING_SKILLScheck — 1by_flowstill sums to the period total--axis flowtable's own four-column shapeflow !== undefinedgate — 1The limits each row kind states
flowLimitsused to gate on "a named row exists". Atool-statedrow has a name, so both of the journal's own limits would have printed for a period whose only flow came from a record's own tool — describing a walk that never ran. They are now gated on ajournal-intervalrow, and atool-statedrow states the limit that is actually its own: it is every run of that skill at once, so its total is not one orchestration's.Envelope
No bump. The flow row gains
attribution, a field a consumer may ignore, and no existing field changes meaning — the rule stated atcost-report-envelope.ts. Every consumer updated in the same commit: the envelope type and mapper, the--axis flowartefact (which gains an Attribution column, the same third columnstepArtefactalready carries and for the same reason), the contract, and the cost skill's own reading instructions. The terminal display renders no flow axis.The contract also carried a stale sentence from #763 — a flow closing at "the next orchestrating
step_startor aturn_end". Corrected here, in the section this PR rewrites.Not in scope
by_prompt's doc comment calls it "the one breakdown that is complete by construction". Measured the same day on the same sink: 843 of 30,714 records carry noprompt_id. Recorded in this PR's plan with its evidence; it is a different axis and its own argument.Bundle is at 592.9 KB against a 593 KB budget — 0.1 KB of headroom. Not raised here; the next change to touch the bundle will have to.
Gates: 3443 tests / 308 files,
tsc,biome ci(2 pre-existing warnings, 0 new), knip, jscpd, bundle within budget, 369 repository script tests, 0 broken links, cli layering clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp