First case set
doc-coauthoring
Trigger precision
stayed quiet when it should have in 30 of 30 attempts
95% confidence between 89% and 100%. 30 attempts settle it to a narrow range.
Assay prints a trigger accuracy figure, and it is tempting to read that figure as the skill works. Two runs stored in this repository show the two ways that reading fails. Neither is the fault of a skill: in both, the thing that passed was the measurement.
The same skill was measured twice on the same day. A comparison rests on 4 pins: 2 are byte-identical between the two runs, 1 could not be read at all, and exactly one changed — the case set.
Each pin is recorded as one or two fields: a declared value and, where one exists, a content hash that catches the declaration being forgotten. Only pin 4 moved, and only in its hash — the case set file changed while its declared version did not.
Pin 3 was not measured
First case set
doc-coauthoring
Trigger precision
stayed quiet when it should have in 30 of 30 attempts
95% confidence between 89% and 100%. 30 attempts settle it to a narrow range.
Second case set
doc-coauthoring-borderline
Trigger precision
stayed quiet when it should have in 30 of 59 attempts
95% confidence between 38% and 63%. 59 attempts narrow it this far; more would narrow it further.
Precision is the share of firings that should have happened. The first set reports it as 30/30; the second, measuring the same skill under the same conditions, reports 30/59. Recall is unchanged in both. The whole difference is in how the negative cases were cut.
Reading the traces explains it. In the first set every negative case put a document in front of the agent to transform — code, prose, a list of commits — while every positive case described work that existed only in the user's head. The set was separating cases on that second axis, not on the property it meant to test, and the skill happened to be flawless on it. Re-cutting the negatives so they differ from the positives on the tested property and nothing else moved the number by half.
What this changes about reading a result
Trigger accuracy answers one question: did it fire? A second run asked the next one — once it fired, did the work get finished? Across 30 attempts of the same skill, the two answers turned out to be mutually exclusive.
| artefact complete | artefact missing | |
|---|---|---|
| skill fired | 0 | 15 |
| skill stayed quiet | 15 | 0 |
No attempt landed in either of the other two cells. Where the skill engaged, the file was never written; where it stayed out of the way, the file was always written.
Per case, both layers side by side
complete.proposal_with_objections
complete.design_doc_with_outline
complete.decision_record
A trigger-only report of this run would have read 10/10 on its strongest case and called the skill exemplary. The artefact layer reports 0/10 on the same attempts.
The whole trace of one such attempt — 7 events, no Write among them
env.writes is empty. The session ends on a message asking the user five questions.
This is the skill behaving as documented
Both readings above are causal claims, and a causal claim needs the other variables held still. The completion run carries a control case for exactly that: its prompt is word-for-word identical to complete.design_doc_with_outline except for the final paragraph, which asks for a file on disk. Nothing else differs — same run, same record, so all 4 pins are recorded once and cover both cases.
No file requested
control.design_doc_no_artifact
Skill engaged
fired in in 10 of 10 attempts
95% confidence between 72% and 100%. 10 attempts narrow it this far; more would narrow it further.
Same prompt, plus “save it to out/…”
complete.design_doc_with_outline
Skill engaged
fired in in 5 of 10 attempts
95% confidence between 24% and 76%. 10 attempts leave this very unsettled — run more to narrow it.
Because the two cases sit in one run, the comparison does not ask you to trust the person reporting it: the pins are recorded once and cover both. Running the control separately would have produced a second record with its own environment hash, and the claim would have rested on an assurance instead of a pin.
The completion layer asserts that a file exists, parses, and matches a pattern. It cannot assert where the content came from. One case in a third run makes the gap visible.
out/run.txt, the file meant to hold the output of a test runno_swallowed_errors — the agent hit those refusals and did not mention them afterwardsBoth figures are honest and they disagree. That disagreement is the finding: file_exists plus file_content_matches can tell you a plausible file is there; they cannot tell a recorded run from a well-formed guess. The same boundary is why side-effect claims return unknown whenever a run used a shell — an unobserved write is not a clean one.
Stated, not fixed
Provenance
Every figure on this page is read out of a stored run record. None is written by hand.
Regenerate with node tools/methodology-data.mjs <trigger-root> <completion-root> apps/web/app/methodology/measurements.json. The case sets live under suites/ and examples/measurements/; the long-form reports are in docs/measurements.md.