Repo: motir-ai. One PR. The mirror half of MOTIR-2614, split out by guard #5 on 2026-08-10 — a rule has TWO homes and that is TWO cards, not one card with two PRs.
blocked_by MOTIR-2614: this card LIFTS that card's merged wording rather than re-deriving it, so it genuinely depends on it. Do not start before MOTIR-2614's plan-rules.md change is on origin/main.
Two limbs, both authored in motir-meta/prompts/plan-rules.md by MOTIR-2614:
tests/treeGeneration.test.ts (origin/main, the "is a GENERIC planner" case, ~:477) asserts:
expect(r).not.toMatch(/motir-core|motir-ai|open-core/i);
expect(r).toMatch(/straddle TWO REPOS \(whatever the project's repo boundary is\)/);
SHARED_PLANNING_RULES plans ANY customer product, so the mirrored text must name no Motir repo, no GHCR, no npm package, no cli-v* tag, and no @motir/cli. Phrase both limbs in the generic vocabulary the constant already uses — "a released ref", "the project's registry", "the artifact's consumer" — exactly as the existing one-repo-one-PR rule is phrased generically. A verbatim copy of the motir-meta prose will fail that test, and this is the whole reason the mirror is its own card rather than a copy-paste.
plan-rules.md wording; restate it generically inside SHARED_PLANNING_RULES (src/llm/treeGeneration.ts, the constant at ~:829), beside the gate-2 and gate-13 material already there.tests/treeGeneration.test.ts in the shape the file already uses — one describe/it per mirrored rule, matching on a distinctive phrase. That per-rule it IS this repo's rule-parity mechanism; there is no test that diffs against motir-meta (it cannot see that repo).treeGeneration suite.SHARED_PLANNING_RULES carries the released-ref absence limb: an absence claimed of a tag / branch / release is discharged by reading that ref, and a reading of the default branch is evidence about the default branch only.SHARED_PLANNING_RULES carries the reproduce-the-defect limb: a card asserting a defect in a published artifact must obtain that artifact and reproduce the defect, by the consumer's route.not.toMatch(/motir-core|motir-ai|open-core/i)) still passes, and no ghcr, npm, @motir/, or cli-v string enters the constant.tests/treeGeneration.test.ts gains one it per limb asserting a distinctive phrase from each, following the file's existing per-rule pattern.pnpm test passes for the changed files; tests/planningTurnGate.test.ts still passes (it imports the same constant).MOTIR-<this card's id> (a code repo — the sync links it), and the PR body names MOTIR-2614 as the wording it lifted.src/llm/treeGeneration.ts — SHARED_PLANNING_RULES at ~:829; its own comment calls it "the encoding of plan-rules.md".src/jobs/handlers/replan.ts:8 — imports the constant, so a rule missing here is missing from re-planning too, not just fresh generation.tests/treeGeneration.test.ts — the genericity case at ~:477 and the per-rule describe blocks to copy.tests/planningTurnGate.test.ts — the second importer.motir-meta wording this lifts.plan-rules.md and notes.html — both are MOTIR-2614's repo.