Root Cause
First: is this one occurrence?
A single failure inside the normal range is common cause, and acting on it makes things worse. Deming's funnel experiment: adjusting a stable process in response to ordinary variation "invariably increases variation in the results". Run this analysis on a signal, not on an incident.
A signal is a point beyond 3σ, or a non-random pattern — a run, a trend — inside the limits (NIST/SEMATECH e-Handbook §pmc31). In this repo the practical test is cheaper: has this shape happened before? Query the ledger before you analyse.
jq -r 'select(.kind=="created")|.payload.title // empty' .basicly/ledger/events-*.jsonl \
| rg -i '<the shape>'
rg -c '<the marker>' .basicly/usage/run-records.json
A record's title is on its created event and nowhere else, so a query over every
event returns nothing — and nothing reads as "no prior", which is the answer that ends
the analysis. Confirm the pipeline's first stage prints titles before believing a zero.
One occurrence with no prior: record it and stop. Filing a bead off a single event is how twelve records came to name one truncation while none asked why the trigger fired.
The method
Iterate why until the answer stops changing. Two rules make it worth doing:
- Every link cites an observation, not an inference. A
file:line, a command and its output, a ledger row, a recorded verdict. A link you reasoned to is where the chain stops being evidence and starts being a story. - Stop at a cause you control. If the next why leaves this repository — a vendor's decision, a person's attention, "the model was wrong" — you have left the zone where a control can be built. Back up one link and build there.
The two ways it lies
It yields exactly one path, chosen by the asker. Card, BMJ Quality & Safety 2017, is the repo's recorded caution: iterated-why is not reproducible between analysts, and two competent people reach different causes from the same incident. The remedy is not to abandon it but to record the branch you did not take. At any link where a second answer was available, name it and say why you went the other way.
It terminates on a person. "The agent guessed", "someone forgot", "human error" — each of those is a place the chain stopped early, because none of them is a thing you can build a control against. Ask what let the guess reach a commit.
Output contract
Three things, and prose is not one of them:
- A named control that would have refused this defect. Not "be careful", not "add a note" — a check, a gate, a schema field, a refusal.
- Its tier:
control(fails a gate),warning(reports and continues), ordocumentation(a human must read it). A documentation-tier outcome is recorded as a downgrade, with the reason no stronger control was available. - The class of defects it covers — the whole set, not this one instance. A control that covers exactly one incident is a patch wearing a rule's clothes.
Then the branch not taken, in one line.
The counterfactual, which is what makes a cause a cause
A chain that ends somewhere plausible is not the same as a chain that ends somewhere load-bearing. Two questions, and they are cheap because they are answerable from the same observations you already cited:
- Per cause: would removing this one block this pathway? If the failure still reaches the same end with the cause removed, it is a condition you noticed, not a cause you found — say so and keep going.
- At set level: would removing the retained set block the observed failure? A set that leaves a route open has a link missing, and the missing link is the one worth the control.
This is the check a second party can run against your chain without re-deriving it, which is why it is here rather than in the prose above.
Where the answer goes
Under D25 an agent never lands catalog guidance without a human. The output is a
diff against catalog YAML — a fragment, a skill, a hook, a schema field — never
advice, so catalog lint and the projection checks bound what the human approves.
Worked shape
Signal 3 of 5 lanes bounced on a shared landing anchor, twice in one pass
Why 1 the rebase conflicted (merge.py:722, conflicts= on the result)
Why 2 two lanes appended at one anchor (both scope globs name basicly.toml)
Why 3 decompose grouped by scope-glob overlap, which is file adjacency
Why 4 scope globs are the only structure the decomposer can see
STOP we control that
Branch not taken: "the anchors should be generated" — real, but it fixes three files
rather than the slicing rule that keeps producing collisions.
Control a plan-gate refusal when two children declare an overlapping anchor
Tier control
Covers every decomposition that slices by file adjacency, not just this anchor