Planning bug: an acceptance criterion quoted durations from a run where the jobs were SKIPPED — a skipped job reports zero, not an absence, and the ceiling it implied would have failed a green run
Type · chore (planner corpus) · Parent · MOTIR-1465, the planner-bug home · Discovered in · the run of MOTIR-3768, 2026-08-28 (PR moooon-B-V/motir-core#2443) · Repo · motir-meta
What the plan got wrong
MOTIR-3768's acceptance criterion 3 handed the runner two figures as evidence:
"(
sandbox-imageslegs ran 0.1–5.8 min andrunner-image1.3 min on run 33167406878 …)"
On run 33167406878 both of those jobs were skipped.
$ gh api repos/moooon-B-V/motir-core/actions/runs/33167406878/jobs \
-q '.jobs[] | select(.name|test("Sandbox|Runner image")) | "\(.name) \(.conclusion)"'
Runner image skipped
Sandbox images skipped
ci.yml's sandbox and runner-image jobs are path-gated on needs.changes.outputs.images, and MOTIR-3768's own description says so, two sections above the criterion that then quotes durations from a run that did not touch the image inputs.
Why it is a planning bug and not a typo
Following the criterion exactly produces a ceiling that fails a healthy run — which is the one outcome the card's own Out of scope section forbids ("no ceiling here may be tight enough to fail a slow-but-healthy run").
- The card's stated upper bound for
sandbox-profilesis 5.8 min. Applying the ~3× headroom the card asks for gives 18. - The slowest successful
sandbox-profilesleg on record is 16.68 min (aider, tier 2, run33214411817,conclusion: success). - 18 clears 16.68 by 1.08×. The next slow-but-healthy
aiderbuild goes red.
So the defect is not that a number was stale — it is that a false number was more dangerous than no number, because it looked like the measurement the criterion demanded and suppressed the re-measurement that would have caught it.
Root cause — the mechanism, not the carelessness
A skipped GitHub Actions job is not absent from /jobs. It is returned with conclusion: "skipped" and started_at / completed_at that yield a duration of 0.0 minutes (or null timestamps, depending on the shape). An aggregation that filters on nothing — or on status: completed, which a skipped job satisfies — silently folds zeros into its range, and a range whose floor is 0.1 reads as "a fast job" rather than "a job that did not run".
That is exactly what produced 0.1–5.8: the 0.1 is skipped legs, and the 5.8 is one of the few runs where the filter happened to let them through. run.md's existing rules do not reach this. Re-measure on a REF secures the ref; re-measure the PREDICATE secures the population. This is a third axis: the ref is right, the population is right, and the individual OBSERVATIONS are not observations.
The rule change this earns
motir-meta/prompts/run.md, beside A bug card's FIRST deliverable is the REPRODUCTION → the enumeration/predicate limb (steps 3–4 and the RE-MEASURE THE PREDICATE block). A new sibling limb, roughly:
⚠️ AND CHECK THAT EACH MEASUREMENT IS A MEASUREMENT — a skipped, cancelled or failed unit reports a NUMBER, not an absence. Where a card quotes an observed duration, size, count or rate taken from a platform, the reading is only evidence if the unit it came from actually DID the work. A skipped CI job returns
conclusion: "skipped"with a ~0 duration; a cancelled one returns a partial; a failed one returns a duration that measures the failure. All three survive astatus: completedfilter and all three drag a range's floor toward zero. Filter on the SUCCESS predicate, and say in the card which one you used —select(.conclusion=="success"), notselect(.status=="completed"). A range whose low end is implausibly small for the work described is the tell, and it is the direction that makes a derived ceiling too TIGHT, so it fails green runs rather than missing red ones.
Acceptance criteria
motir-meta/prompts/run.mdcarries the limb above, beside the existing enumeration/predicate rules, with this card's fixture (the 18-vs-16.68 arithmetic) as its evidence.- The limb states the concrete
gh api … select(.conclusion=="success")form, because the failure was a filter, not an attitude. - A LESSON is written for the same takeaway, scope decided by the strip-the-identifiers test and stated in the PR body.
- ORDERING — every criterion reads on this card's own branch. Nothing depends on MOTIR-3768 merging.
Context refs
- MOTIR-3768 — the card carrying the falsified criterion; its AC 3 is amended on the record.
- moooon-B-V/motir-core#2443 — the run that found it; its PR body carries the full correction and the re-measured table.
motir-meta/prompts/run.md— the enumeration and predicate limbs this sits beside.
Resolution: open.