Planning bug: an ABSENCE-shaped acceptance criterion left the repo column BLANK — gate 1's sweep cannot run on "there is no read path for this", and the straddle detector has no path to match
Opened by Zhu Yue ·
A motir run on MOTIR-4058 halted at guard #5 (ONE SUBTASK = ONE REPO = ONE PR). The card was pinned targetRepo: motir-ai with targetRepos: ['motir-ai'] and every one of its eight context refs a motir-ai path, and one of its fifteen acceptance criteria could not be discharged in that repository at all:
"PENDING PLANS are read. A project with a plan already in flight against the same area is surfaced to the planner. This is the one input with no existing read path, so it is the one most likely to be silently skipped."
Verified on origin/main of both repositories: motir-ai reaches motir-core only through src/core/coreClient.ts over the /api/internal/ai/* routes (code-scanning, dev, get-item, get-subtree, job-token, live-projects, org-context, plan-proposals, plan-tree, search-work-items, similar-work-items, skeleton, validate-plan, validate-plan-forest, validate-plan-sprint, walk-blocking, work-items), none of which lists a project's plans; plan-proposals' own GET header records that its job-scoped resolution is deliberate ("a job token cannot read another job's plan"); and plansService.listPlans exists but only behind app/(authed)/plans/. So the criterion needed a new motir-core producer plus a motir-ai consumer — two repositories on a one-repository card.
The gap, narrowly
Gate 1's repository column cannot be RUN on a criterion whose subject is an ABSENCE. The check is go criterion by criterion and name the repository each one is discharged in; two answers is a split. It works because criteria name things and a name usually carries a repository. "There is no read path for this" names no file by construction — the whole content of the claim is that the file does not exist. So the column comes back blank, not with a second answer, and a blank is indistinguishable from a correctly-pinned single-repository card. A careful author running the sweep honestly gets the wrong verdict.
Every mechanical signal agreed, and each was correct on its own terms: readiness = ready with openBlockers: []; validate_work_item = valid: true; and the advisories array carried only reference and subsumption entries with no shape entry at all — likely-repo-straddle matches a PATH in a criterion against the card's pin, and there was no path to match.
The near-miss is worse than a miss: the criterion carried its own warning — "the one most likely to be silently skipped" — so the most carefully flagged sentence on the card was the one that made it unbuildable, and that flag read as diligence.
Acceptance criteria
plan-rules/core.mdgate 1 gains a limb for the ABSENCE-shaped criterion: a criterion whose subject is no existing read path / nothing exposes this / no mechanism for it today is a commissioning sentence, and the two questions it does not answer — what would have to be BUILT, and which repository would that thing live in — are asked as a named step. The limb states that a BLANK in the repository column means the check did not apply, never that it passed.motir-aiSHARED_PLANNING_RULEScarries the same limb, in the segment that already holds gate 1's repo-column text — the two-homes rule (core.mdgate 19). Read the constant to place it; do not infer the region from the subsection's topic.- The limb is ADDED, not a re-wording of the existing repo-column text:
tests/planningRuleConservation.test.tspermits a whole added sentence and fails a replace, and that is the shape to land. - The routing is unchanged and asserted:
tests/planningRuleRouting.test.ts's baseline moves only by the new segment's own identifier, and no existing segment changes the cells it reaches. - Both homes ship, one pull request each, each linked with
link_pull_request; the card completes only when both merge.
Scope BOUNDARY
- IN: the gate-1 limb, in both homes, and its routing/conservation evidence.
- OUT: WIDENING THE SHIPPED DETECTOR, and the reason is the finding itself.
likely-repo-straddlereads a criterion for a PATH and compares it against the card's pin. An absence-shaped criterion contains no path, so no widening of that arm can reach this class — which is precisely the argument that the remedy is a RULE a reader runs and not a check a machine runs. Stated here so the next reader does not re-open it as an omission. (core.md's own do NOT write prose to duplicate a check that already runs points the other way and does not apply: there is no check here to duplicate.) - OUT: the correction to MOTIR-4058 itself. Already submitted as plan
cmtj28b1m002hhwn8bl5f0xzi— twoadds and amodify, awaiting approval. This card holds up nothing. - OUT: the LESSON. Landed as a curated GLOBAL row in
motir-aisrc/seed/lessons.base.ts+ its migration,motir-ai#349 (unlinked,no-work-item). The store holds the lesson; this card holds the RULE, and they are different tiers.
Context refs
motir-metaprompts/plan-rules/core.md— gate 1, the repository column and its existing limbs.motir-aisrc/llm/planningRulePacks.ts—SHARED_PLANNING_RULES, the second home. ⚠️ Usegrep -a.motir-aitests/planningRuleConservation.test.ts·tests/planningRuleRouting.test.ts— the added-sentence verdict and the routing baseline.motir-corelib/services/workItemValidationService.ts(or whereverlikely-repo-straddleis computed) — EVIDENCE for the OUT entry above, not a deliverable.
Discussion
No comments yet.
Adding to this discussion signs you in on app.motir.co and brings you back to this request.