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

(motir-core) `createTestWorkItem` writes a ZERO-PADDED `position`, so the next TOP-LEVEL create in that project 500s on `invalid order key head: 0` — the exact trap `scripts/plan-seed/seed.ts` warns about in prose

Found at run time by motir run MOTIR-1854, building 11.4.6's conformance guard. An out-of-scope finding logged rather than absorbed (notes.html #27): the guard was rewritten to seed through the real route, so nothing in Story 11.4 depends on the fixture — this card is the defect itself.

Reproduction — a failing case, not a code-reading theory

const caller = await createV1ProjectCaller({ scopes: ['read', 'work_items:write'] });
await createTestWorkItem(caller.fixture, { kind: 'story', title: 'A story' });

// POST /api/v1/projects/{projectKey}/work-items — a TOP-LEVEL create
// → 500 {"error":"Internal server error."}
// stderr: [api/v1] unhandled error … Error: invalid order key head: 0

Run against origin/main + this story's branch; it reproduces on either.

The cause

tests/fixtures/workItemFixtures.ts:125 writes the row directly through the repository with

position: String(key).padStart(6, '0'),   // → "000001"

workItemsService.createWorkItem (lib/services/workItemsService.ts:819-820) reads the LAST SIBLING's position and calls keyForAppend(lastPosition)generateKeyBetween("000001", null). The fractional-index library rejects "000001" outright: a valid key's integer part must not begin with 0. So the create throws a raw Error, classifyApiV1Error does not recognise it, and the wrapper renders a bare 500 — correctly, per ADR §4.

It is scoped to SIBLINGS, which is why it has stayed dormant. createWorkItem computes the append position among the new item's siblings (parent-scoped, top-level when parentId is null). Every shipped suite that creates through the API after seeding with the fixture creates a CHILD of the seeded row — e.g. tests/api/v1/work-item-write-routes.test.ts:108-113 seeds a parent and posts a subtask with parentKey — so the sibling list is empty and keyForAppend(null) is fine. The first suite to create a top-level SIBLING of a fixture-seeded row hits it.

Why this is worth a card rather than a note

The codebase has already learned this exact lesson once, in prose, in the other seeder. scripts/plan-seed/seed.ts:396-405:

"A VALID, GLOBALLY-UNIQUE fractional-index position per item… position MUST be a real fractional-index key (the shape lib/workItems/positioning.ts mints) — NOT a zero-padded number."

The seed script was fixed and documented; the test fixture was not, and there is nothing that would tell the next author. The failure it produces is maximally unhelpful: a 500 with no code, from a create that looks correct, in a suite whose subject is something else entirely — the finding costs its discoverer an hour and teaches them nothing they can reuse.

What the fix is

createTestWorkItem should mint its position the way the product does — keyForAppend(lastSiblingPosition) from lib/workItems/positioning.ts, chained across the items a fixture creates, exactly as scripts/plan-seed/seed.ts already does. The padded string is not load-bearing anywhere: position is compared as an opaque string in ORDER BY position ASC, so the only property the fixture needs is that siblings sort in creation order, which a real fractional-index key gives.

Check the blast radius before changing it. createTestWorkItem is used across the suite, and any test asserting a literal "000001" position — or asserting an ORDER that only holds under zero-padded numeric strings — has to move with it. That is the work of this card, and it is why it is a card rather than a one-line edit.

Acceptance criteria

  • createTestWorkItem (and any sibling fixture that writes position directly) mints a real fractional-index key via lib/workItems/positioning.ts, chained so siblings sort in creation order.
  • The reproduction above passes: seeding with the fixture and then creating a TOP-LEVEL item through POST /api/v1/projects/{projectKey}/work-items returns 201, asserted by a test that would have failed before the fix.
  • No test asserts a zero-padded position literal; every ordering assertion that depended on the old format is updated, found by grep rather than by running until green.
  • The fixture carries the same warning scripts/plan-seed/seed.ts:396-405 does, so the next author cannot reintroduce it.
  • The full suite passes.

Context refs

  • tests/fixtures/workItemFixtures.ts:125 — the padded write.
  • lib/services/workItemsService.ts:812-825keyForAppend(lastPosition), sibling-scoped, and where it throws.
  • lib/workItems/positioning.tskeyForAppend / keyBetween, the only legitimate source of a position.
  • scripts/plan-seed/seed.ts:396-405 — the same lesson, already learned and written down for the other seeder.
  • tests/api/v1/work-item-write-routes.test.ts:107-127 — the shipped test whose CHILD create is why this stayed dormant.
  • tests/api/v1/openapi-drift-guard.test.ts — the suite that hit it, and the comment recording why it seeds through the real route instead.