MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3914

Planning bug: a card planned a live-run indicator on the READY list, whose membership predicate excludes every item that has a run — and removing it exposed that a run had no general door at all

To Do
Description

A RECORD of a planning defect, filed by the re-plan that corrected it. No code ships from this card; its close-out is the disposition below.

The defect

MOTIR-1797 planned "the /ready run STRIP — the dispatch surface's live indicator", and MOTIR-1795 drew it (design/runs/ready-strip.mock.html + .png, six panels). Its central acceptance criterion:

A ready row whose item has a live run shows the running indicator and the current step, and links through.

That row cannot exist. Verified on origin/main:

  • lib/services/workItemsService.tscollectReadyLeaves is the single source listReady / getNextReady / countReady derive from, and its contract says it "COLLECT[s] the ready, childless, todo leaves". The doc block above it states the predicate outright: "a ready item is one to START (in_progress/done …)".
  • app/(authed)/ready/page.tsx reads workItemsService.listReady + countReady directly — that is the whole of the page's data.
  • claimNextReady"LOCK the best still-claimable candidate (FOR UPDATE SKIP LOCKED) and flip it → in_progress" — and claimScope beside it, for every member of a scope claim, before the first agent starts.

So the transition that creates the state the indicator shows is the same transition that removes the row it would render on. The card was unbuildable, and so was the half-asset drawn for it.

Found by: Yue, reading the design mid-motir run MOTIR-1789"when the item is running, the status is changed to in progress, in progress items are not in /ready list, so you are designing something doesn't exist and shouldn't exist."

Why every check passed

Nothing was skipped. The card's data seam was real and verified (GET /api/projects/[key]/dispatch-runs/active exists and returns the legs). Its host surface was real and verified (/ready ships). Its design was drawn, its blockers were correct, validate_work_item returned valid: true, and its advisory array was clean.

The contradiction lives between the card and a query neither the card nor any gate reads. Precondition-verification asks does the thing this card consumes exist? The host-surface rule asks does the surface it renders inside exist? Both answer yes. Neither asks can the state I am indicating and the list's own membership filter hold at the same time? — because the filter is not something the card consumes, cites or names. It is a property of the surface being decorated.

The larger defect it was concealing

Removing the strip left the run view with no general door. Every remaining route into a run started from something the reader already had — a run id, or a card already known to be in the set. A run that finished last night was unreachable; a run in flight was unreachable unless you opened one of its cards. The strip had been standing in for a surface nobody had planned: an index of runs. That is the more expensive half of this defect, and it was invisible for as long as the impossible door was in the plan.

SECOND finding, same re-plan pass — dead work-item chips

The earlier re-plan of this story wrote [label](motir:<id>) chips using plan-item temp ids rather than motir-ref:<tempRef>, which is the form materialize rewrites. Six cards (MOTIR-1795, MOTIR-1796, MOTIR-1797, MOTIR-1798, MOTIR-1800, MOTIR-3895) carried chips pointing at cmte9rma5003fhwphibj1ddty and cmte9renv003chwph81nm9y40, neither of which is the id of MOTIR-3893 or MOTIR-3895. They render as links to nothing. Remedied in this re-plan's modify patches; recorded here because the cause is a mechanism, not a typo, and the next re-plan will reach for the same token.

⚠️ Repeat-defect count — THREE under this story's plan

The gate fires at three. Non-archived planning bugs against MOTIR-1789's decomposition, this card included:

  1. MOTIR-1801AgentRun named two tables across the open-core boundary, so the execution record was never owned. Cause: a shared name planned twice without either side owning it.
  2. MOTIR-3890 — that collision was RECORDED and then adopted anyway. Cause: evidence gathered and not acted on; the record substituted for the fix.
  3. This card. Cause: a rung-2 fact about a surface's membership predicate never read.

They do NOT all share a root shape — but two of them do, and this is recorded rather than argued away. (2) is genuinely different: the check was performed and its result discarded. (1) and (3) share one shape — a claim about shipped reality written into a card without opening the code that decides it.

Discharge (b): the re-shape for that shape is a GLOBAL LESSON, filed in the same act as this record"An indicator planned onto the rows of a FILTERED collection must be checked against that collection's own membership predicate — the state worth indicating is often the state that removes the row" (motir-ai src/seed/lessons.base.ts, sourceRef: MOTIR-1797). A rule already exists for (1)/(3)'s general form and was not run; what did not exist is the specific, recognisable trigger — you are decorating a filtered list — which is what a lesson is for and a restatement of the general rule is not. This card is a record and gates nothing, so there is no patch card to block.

Still-open decisions the re-plan did NOT settle

  • A container's own run history has a read and no surface. The new run-history read wires dispatchRunRepository.listByScope behind a scopeWorkItemKey filter — runs pointed at THIS story or sprint as their scope — and no card renders it. It is one option away from being free (a ?scope=<KEY> on the runs index, linked from the container's page), and it is deliberately not decided here rather than smuggled into a card's scope. If it is not wanted, listByScope should be dropped rather than left as a parameter nothing drives.
  • The current/past partition control on the runs index (a switch, two sections, or one list with a divider) and the nav glyph are left to the design card to decide and record, which is the right place for both.
  • The /runs nav row's permission requirement — the index card requires it be read from the shipped gate rather than assumed.

Operational consequence, already in flight

PR #2455 currently carries ready-strip.mock.html and ready-strip.png. MOTIR-1795 is parked at Blocked and those two files, plus the strip's rows in design-notes.md, must be removed before it merges.

Acceptance criteria

  • This card records the defect, its rung-2 evidence, the repeat-defect count with each root cause named, and the re-shape it was discharged with — it is closed by that record being complete and correct, and it ships no code.
  • The global lesson exists in motir-ai src/seed/lessons.base.ts with sourceRef: 'MOTIR-1797' and a generated seed migration.
  • The dead-chip remedy is verifiable: no card under MOTIR-1789 carries a motir: chip whose id resolves to no work item.

Context refs

  • motir-core/lib/services/workItemsService.tscollectReadyLeaves, claimNextReady, claimScope: the predicate and the two writes that falsify it.
  • motir-core/app/(authed)/ready/page.tsx — the surface, and the two service calls that are the whole of its data.
  • MOTIR-1797 — the archived card, carrying the full evidence comment.
  • MOTIR-1795 — the design card whose second surface was drawn for it, now re-scoped and parked at Blocked.
  • MOTIR-1789 — the story whose plan this is about, with the corrected shape in its second re-plan note.