BUG — `motir done --session` CLOBBERS the provenance the run recorded: the close-out stamps `motir-cli/<version>` over the agent's name and nulls its model
MOTIR-2419 made motir auto and motir run record implementation provenance honestly — implementationHarness names the agent the loop launched, implementationModel carries the agent's own self-report. The very next step of the documented lifecycle undoes it.
motir done --session <branch> sends implementationHarness: motir-cli/<version> and no model. recordImplementationProvenance writes the triple UNCONDITIONALLY:
implementationHarness: provenance.harness ?? null,
implementationModel: provenance.model ?? null,
So the full cycle a run actually follows —
motir autointegrates:claude/claude-opus-5✅- a human merges the session pull request
motir done --session <branch>:motir-cli/0.2.0/null❌
— ends where it started. Every card that reaches Done through the supported path carries the CLI's version string as the thing that built it, and no model. The fix for 2419 is invisible in the data of any completed item.
Why 2419 did not fix it
Its scope boundary named motir auto's integration call and excluded markIntegrated's semantics, and neither of the two minimal fixes fits inside that:
- Send nothing from the close-out. Prior stamps then stand untouched (the service skips the write entirely when
implementationisundefined) — but--print-mode items, which never reachmark_integrated, lose theimplementationSource: byokstamp that is currently their only one. 2419's acceptance criteria say not to touch the source axis. - Make the write null-guarded — a field the caller did not report leaves the stored value alone. This is a change to
recordImplementationProvenance/markIntegrated, which 2419 excluded.
Choosing between them is the work of this card.
The shape of the answer
Note what each actor knows, which is the question that has resolved every seam like this on the story:
- The run knows the harness and (via self-report) the model, and records them at integration.
- The close-out knows neither. It runs after a human merged, minutes or days after the agents exited. It legitimately knows
implementationSourcefor an item that has none — the work was BYOK — and nothing else.
That points at the null-guarded write: a partial report updates only what it reports. It also fixes the same clobber for any future caller that reports one field, and it makes the close-out's byok stamp safe to keep. The alternative — dropping the report — is smaller but silently loses the source axis for the --print lane, which is a second data hole rather than a fix.
Whichever is chosen, ⚠️ do not backfill. Rows already stamped motir-cli/<version> recorded that honestly under the old code; rewriting them would claim knowledge nobody has.
Acceptance criteria
- A full cycle — integrate through
motir auto, then close out withmotir done --session— leavesimplementationHarnessnaming the AGENT andimplementationModelholding the model the agent reported. Asserted end to end across BOTH writes, not on either alone: this bug is invisible to a test of either one. - An item whose provenance was never stamped still gains
implementationSource: byokat close-out. The--printlane must not lose its only stamp. - A close-out report that omits a field leaves that field's stored value ALONE — asserted directly at whichever seam takes the decision, with a row that already has a harness and a model.
- No historical row is rewritten, and no backfill script is added or run.
motir done <key>(the single-item form) is unchanged — it reports no provenance today and this does not give it any.
Context refs
lib/services/workItemsService.ts—recordImplementationProvenance,markIntegrated,completeSession.lib/api/v1/workLoop/schema.ts—toProvenanceInput, whoseharness: body.implementationHarness ?? nullis where a partial report becomes a full overwrite.packages/cli/src/commands/dispatch.ts—HARNESS, now used only bydoneCommand --session.- MOTIR-2419 — the fix this one completes; its closing comment records the boundary.
- Story: MOTIR-1855.