Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-3065Done

Planning bug: an acceptance criterion keyed on a COUNT that the code's own idempotency argument forbids — the falsifying comment sat one file from the card's own context refs

The record. MOTIR-3051's AC 2 read "the auto-plan cadence is not paused by a plan carrying zero items … a test covers a project whose only undecided plan is empty and asserts cadence still fires." Built as written, that ships a regression.

lib/jobs/definitions/autoPlanCadenceTick.ts sets retryPolicy: 'idempotent' and states the whole argument for it in its header: "a project that already fired now HAS an undecided plan, so the gate skips it on the re-run. Re-running the whole sweep therefore converges rather than double-firing." Between submitExpand's createPlan and motir-ai's first addProposals that plan holds zero items — so a gate keyed on the COUNT lets an Inngest retry of the tick fire a second expand_item at the same stub, replacing a permanent pause with the stacked proposal the gate exists to prevent. And nothing in the suite would have said so: every plan tests/integration/ai/autoPlanCadence.test.ts seeds carries a sourceJobId, and no test exercises the retry path.

Caught at motir run before a line was written; the criterion was AMENDED on the card with its evidence (sourceJobId IS NULL — the PRODUCER, not the count) rather than narrowed silently, and both tests now exist.

The shape, generalized. The card's Context refs named autoPlanCadenceService.ts — the SWEEP. The falsifying sentence was in autoPlanCadenceTick.ts — the JOB DEFINITION that schedules it, one file away, never opened at plan time. A predicate's correctness is not a property of the predicate: it is a property of everything that RE-EVALUATES it, and a retry policy is a caller that re-evaluates it on purpose. The three-option menu was otherwise well-verified — the mechanism was correct, CLI_TOKEN_GRANT was quoted correctly, the gate was read correctly — which is exactly why the one unchecked clause read as safe.

Nearest existing lesson: notes.html #306 (a bug card stated its MECHANISM as settled and enumerated the permitted repairs — the enumeration bounded the next run's search). This is the softer form: the mechanism WAS right and only the repair's PREDICATE was under-specified. #288 is the other half (every existing test synchronised between steps, so the one bug that appears only when they are not was invisible to the whole suite) — here, every seeded plan had a job.

Acceptance criteria

  1. A notes.html mistake entry exists carrying this lesson, with the counter bumped and the .mistake markup matched: a criterion that gates on a COUNT / an emptiness owes a read of every caller that RE-EVALUATES that gate — the retry policy, the cron, the poller — not only the service that owns it.
  2. The entry names the concrete tell: the falsifying comment lived in the JOB DEFINITION beside the service the card's context refs did name, so "I read the file the card pointed at" was true and insufficient.
  3. Whether this rises to a RULE (plan-rules/phase-deepen.md's precondition-verification limb, which already owns verify the claim against shipped reality) is judged, and the judgement is stated — a single occurrence is a LESSON by default per plan-rules/CORPUS-MAINTENANCE.md.

Not blocked by MOTIR-3051 — stated rather than left silent

This card's deliverable is a notes.html entry in motir-meta. It reads on the AMENDMENT already applied to MOTIR-3051 in the live tenant, which exists now, and on comments already on origin/main (autoPlanCadenceTick.ts). It consumes nothing from MOTIR-3051's unmerged branch, so no blocked_by is owed — relates_to carries the provenance.