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-2239Done

11.7.5 Expansion + the two PLAN reads over `/api/v1` — submit a job, read its outcome, read the plan with its proposals

Three endpoints: one submit and two reads, all about a Plan — the object that is proposals, not work items.

What to build

  1. Submit an expansion of one container item — aiPlanEditsService.submitExpand. Returns { jobId, planId } the instant motir-ai accepts the job; it never waits for the planner. Both ids address the same expansion: planId is the review surface, jobId is what the status read also accepts. Scope: work_items:write, per the shipped map's reasoning that this is "the narrowest shipped scope that admits a plan-mutating, billable submit".
  2. Read a plan's OUTCOMEaiPlanEditsService.getOutcome. Reports the plan's status AND, while it is still generating, whether the job is alive or already dead — because a failed job otherwise leaves a plan generating forever, and a client with no way to distinguish those polls indefinitely. Scope: read.
  3. Read a PLAN with its proposalsplansService.getPlan. What was proposed, not just how many. Scope: read.

The contract these three publish

Nothing here creates a work item. An expansion produces a Plan of PROPOSALS; approval in Motir is the only path from a proposal to a work_item row, and it is a human act. 11.7.1 Q3 pins how the response shape says so — status code, body, and whether a poll URL rides along. Follow it; do not re-decide it here, and do not let the submit response read like a completed outcome.

A submit is BILLABLE. It spends the owner's AI credits. That is why it is a write scope rather than a read, and why a retry-on-timeout by a naive client is a real cost, not just a duplicate row — worth a line in the endpoint's description so an integrator sees it before they wrap it in a retry loop.

Scope BOUNDARY

Ends at these three endpoints. It does NOT ship the planning CONVERSATION (11.7.6) — a plan session is a different resource with a different address, even though both end in a submit. It does NOT ship session close-out (11.7.4) or activity (11.7.7). It does NOT change what an expansion job DOES, how the planner works, or anything in motir-ai. It does NOT add an approval endpoint — approving a plan stays a web-app action and is deliberately not public surface in this story.

Acceptance criteria

  • All three endpoints exist, are declared, appear in the emitted spec, and carry the scope their MCP counterparts hold in lib/mcp/scopes.ts (work_items:write for the submit, read for both reads) — asserted against that map.
  • The submit returns its job handle without waiting for the planner — asserted with a planner stubbed to be slow, so a synchronous implementation fails the test rather than merely being slower.
  • The status read distinguishes generating and alive from generating but the job is dead, asserted for both.
  • The plan read returns proposals, and its schema makes clear they are proposals rather than work items — a client cannot mistake one for the other from the payload alone.
  • No endpoint here creates, updates or approves a work item — asserted by comparing the item table before and after a full submit-and-read cycle.
  • The endpoint description states that a submit spends credits.
  • Every domain error these services raise — including motir-ai being unreachable and credits being exhausted — has a deliberate row in the v1 status map, proven by a test that drives it.
  • All three payloads match their MCP counterparts field for field.
  • Cross-tenant keys return 404; a token lacking the mapped scope is refused 403.
  • The per-file coverage floor (≥90%) holds on every new file.

Context refs

  • 11.7.3 — the schema module, including the job-handle shape.
  • 11.7.1 Q3 — the "accepted, not finished" decision these three implement.
  • lib/services/aiPlanEditsService.tssubmitExpand and getOutcome; lib/services/plansService.tsgetPlan.
  • lib/mcp/tools/expandItem.ts · getPlan.ts — the semantics to mirror, including the alive-vs-dead job distinction. Read, not imported.
  • lib/dto/plans.ts — the plan and proposal shapes mapped FROM.
  • lib/ai/errors.ts (MotirAiError) + lib/ciMetering/errors.ts (CiCreditsExhaustedError) — two of the failure modes the status map must cover deliberately.
  • tests/mcp/expand-item.test.ts · get-plan.test.ts — the shipped behaviour to agree with.
  • Story: 11.7.