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-3552

(motir-ai) The generator carries the OCCURRENCE explicitly — a widening bumps the clock, a wording correction never does

Done
Description

The global half. A curated lesson is widened by editing src/seed/lessons.base.ts and running pnpm lessons:generate-migration; the generator detects the drift against its content hash and emits an UPDATE. That UPDATE sets content, the three axes, updatedAt and — when an embedded field moved — embedding = NULL. It touches neither counter (scripts/generate-lessons-migration.ts:186), so widening the most-consulted rows in the corpus leaves them ageing on the clock they had before anyone learned they applied more broadly.

What to build

A per-lesson OCCURRENCE marker in the authored source. The seed row gains an optional field naming the occurrence that prompted the widening — a MOTIR-<n> key. Its presence is what makes this correction an occurrence; its absence is what makes a correction editorial.

updateStatement emits the bump ONLY when that marker is present, and as a ledger insert plus a counter write, not a bare UPDATE: INSERT INTO "LessonOccurrence" … ON CONFLICT ("lessonId", "occurrenceRef") DO NOTHING, and the lastOccurredAt / recurrenceCount write guarded on that insert having happened. The chain stays forward-only and re-appliable in any order, which is the property every statement in this generator already has.

⚠️ NEVER by drift-detection — this is the whole point of the card

The generator ALREADY emits an UPDATE for any content drift: a typo fix, a reworded sentence, a retag. Bumping on drift would make every wording correction a fabricated occurrence and would make recurrenceCount lie at corpus scale — precisely the outcome LessonHumanOverride.exempt exists to avoid, arrived at from the other direction. The marker is what separates the two, and it is written by the person doing the widening because they are the only one who knows whether an incident prompted it.

Acceptance criteria

  • A seed row can declare the occurrence that prompted its widening, and the generator's marker records it alongside the content hash.
  • A correction WITH the marker emits the ledger insert plus the counter write, and applying it advances lastOccurredAt and recurrenceCount by exactly one.
  • A correction WITHOUT the marker — a wording fix, a retag, an axis broadening with no incident behind it — emits the UPDATE exactly as it does today and leaves both counters untouched. Asserted as its own test; this is the assertion that separates the rule from every edit bumps, and without it the rule is untested.
  • Re-applying the whole migration chain twice is a no-op on the counters as well as on the rows — the ON CONFLICT DO NOTHING property the generator already guarantees, extended to the ledger.
  • The same marker used twice across two migrations bumps once, because the ledger's unique constraint is what decides.
  • The generator's header comment records the marker's meaning: the presence of an occurrence ref is a claim that a mistake actually recurred.

Context refs

  • scripts/generate-lessons-migration.tsupdateStatement (:186), insertStatement / marker and the content-hash drift detection, the header block describing the forward-only chain.
  • src/seed/lessons.base.ts — the authored source and BaseLessonSeed's shape.
  • tests/helpers/lessonMigrationGuard.ts — where the generator's emitted SQL is already asserted.
  • MOTIR-3547 (the parent) — the rule, stated once.