Found while running MOTIR-3374 (motir-ai#280). The card's second acceptance criterion read:
If it is wired: a pass that calls
validate_planmid-generation gets a real verdict, asserted against a real database, and the pre-markPlannedvalidateCandidatePlancall is stated to be either redundant or complementary.
The run could not meet it as written, and the reason is structural rather than a matter of cost.
The criterion demanded evidence from a substrate that lives on the OTHER side of a service boundary from the card's targetRepo. The card is pinned motir-ai. validate_plan is an HTTP call to motir-core's POST /api/internal/ai/validate-plan, and the tree it projects lives in motir-core's database. There is no motir-core process, schema, migration or fixture in the motir-ai checkout, so no motir-ai suite can assert against that database at all — the criterion is unavailable, not expensive.
It was written by analogy with the card's own predecessor. MOTIR-3374 was filed off MOTIR-3368, whose fix — the capture pair log_planning_mistake / log_planning_bug — writes to motir-ai's OWN Postgres via lessonService, and whose suite (tests/replanCaptureIntegration.test.ts) genuinely does assert against a real database. Both cards are "an optional GenerationSink method is unimplemented", so the evidence bar was carried across with the defect shape.
The two sinks differ in exactly the way the bar depends on. logPlanningMistake terminates in a local repository write; validatePlan terminates in a fetch to another service. That difference is invisible at the altitude the criterion was written at ("wire the sink, assert it works"), which is what makes this worth a rule rather than a one-off correction: the sibling card's evidence bar is exactly as portable as its SINK is, and a defect-shape match is not a substrate match.
A criterion naming a test SUBSTRATE — a database, a live tenant, a deployed surface, a registry — is subject to the same repo-pinning discipline gate 1 already applies to a criterion naming a PATH. validate_work_item mechanises the path case as likely-repo-straddle; there is no equivalent for a substrate, and the check a human would run is the same one: does the thing this criterion measures live in this card's targetRepo?
Note this is NOT a repo straddle in gate 1's sense — the card ships wholly in motir-ai and its criteria name only motir-ai paths. The straddle is in the evidence, which is why the shipped detector does not see it. That asymmetry is the finding.
Disposed of it on the record rather than silently re-scoping (run.md, a falsified premise is REPORTED): tests/planValidationSinkWiring.test.ts uses the discipline this repo already established for this exact endpoint — the contract-seam shape of tests/planValidationSeam.test.ts (Subtask 7.28.6 / MOTIR-1390). The real executor → the real sink → the real client, driven against a faithfully-recorded fixture of motir-core's shipped WorkItemValidityDto wire shape, so a key drift on either side breaks the build. Nothing about the sink is stubbed. Amended on MOTIR-3374's own record, and in motir-ai#280's body.
plan-rules/phase-deepen.md (the gate-14 AC-axes limb, beside the existing precondition and NEGATIVE/ENUMERATION limbs) carries a SUBSTRATE limb: a criterion naming the thing it will be measured against — a database, a deployed surface, a live tenant, a registry, a platform API — owes the check that that substrate is reachable from the card's targetRepo. Where it is not, the criterion names the evidence that IS available at that boundary instead.likely-repo-straddle detector does NOT cover this: gate 1 checks the PATHS a criterion names; this checks the SUBSTRATE it measures against, and a card can pass the first while failing the second (MOTIR-3374 did).logPlanningMistake → motir-ai's own Postgres (assertable) vs validatePlan → an HTTP call into motir-core (not assertable from here).notes.html lesson is added for the same finding — the corpus's LESSON half, per plan-rules/CORPUS-MAINTENANCE.md; this card is the RULE half.tests/planValidationSinkWiring.test.tstests/replanCaptureIntegration.test.ts, which does assert against a real databasemotir-ai tests/planValidationSeam.test.ts — the contract-seam discipline used insteadplan-rules/phase-deepen.md — the NEGATIVE and ENUMERATION limbs this parallelsplan-rules/core.md gate 1 — the repo column, criterion by criterion, and what it does and does not reach