Backlog Lifecycle
The file-backed backlog under .oat/repo/pjm/backlog/ tracks work as one Markdown file per item. This page describes the lifecycle those items move through, the atomic close-out command that keeps the pieces in sync, and the diagnostics that flag drift when a close-out is done by hand and something is missed.
For the flag-by-flag command reference, see oat backlog archive. For the two-layer PJM surface that hosts the backlog, see Tool Packs.
Adoption comes first
oat backlog mutations require this repository to have adopted PJM. Installing
the project-management pack makes the capability available — including at user
scope, which is the default — but it does not adopt PJM for a repository, and
oat backlog init is not an alternate adoption path.
Run oat pjm init once per repository. Until adoption is recorded, backlog
mutations write nothing and return an error naming the repository path and
oat pjm init as the recovery. Check the current state with
oat pjm doctor --json and read its adoption.state field.
Where a backlog item lives
items/<id>.md- active, file-backed records. Each carries frontmatter with astatusand anupdatedtimestamp.archived/<id>.md- the resting place for closed-out items, preserving the full item file as history.completed.md- a newest-first summary log of completed work, one line per entry (YYYY-MM-DD — BL-YYMMDD-slug — Title — one-line outcome).index.md- the human-facing curated overview plus a managed, generated index table rebuilt from item frontmatter.
Item statuses
An item's status frontmatter field is one of exactly four values:
open- captured, not yet started.in_progress- actively being worked.closed- completed.wont_do- abandoned or intentionally declined.
closed and wont_do are the two terminal statuses. These are the only valid values — never invent variants like done. A terminal item belongs in archived/, not items/.
Closing out an item
When an item reaches a terminal state, close it out with the atomic command rather than editing files by hand:
# completed work
oat backlog archive BL-260705-example --summary "shipped the thing"
# abandoned work
oat backlog archive BL-260705-example --wont-do --summary "superseded by BL-260706-other"A single oat backlog archive run performs the whole close-out so its parts cannot drift apart:
- Sets the terminal
status(closedby default,wont_dowith--wont-do) and stampsupdated. - Validates and trims a nonblank
--summarybefore mutating acloseditem, then appends its canonical newest-firstcompleted.mdentry.wont_doitems may omit the summary and get an entry only when one is provided. - Moves
items/<id>.mdintoarchived/— withgit mvinside a work tree, or a plain rename outside git. - Regenerates the managed backlog index.
The command is safe to re-run: an item already in archived/ produces a no-op warning with no writes. A missing closed-item summary or an out-of-enum current status (for example a hand-set done) is a hard error before mutation and includes recovery guidance. See the command reference for exit codes and the --json payload.
Catching lifecycle drift
The manual close-out this command replaces is exactly where the two motivating repos drifted — an item was marked closed in frontmatter and summarized in completed.md but never moved to archived/, and one shipped with the invalid status done. oat pjm doctor (and therefore oat doctor, which aggregates the pjm:* checks) now surfaces that drift:
pjm:backlog_terminal_in_items(fail) - aclosedorwont_doitem is still sitting initems/. Fix: runoat backlog archive <id>to finish the move.pjm:backlog_invalid_status(fail) - an item carries an out-of-enum or missingstatus. The message lists the offending file paths and the valid statuses.pjm:backlog_archived_open(warn) - anopenorin_progressitem is inarchived/, which usually means it was archived prematurely.pjm:backlog_completed_unarchived(warn) -completed.mdreferences an item whose file still lives initems/.pjm:backlog_duplicate_id(fail) - the same<id>.mdexists in bothitems/andarchived/.oat backlog archiverefuses to auto-resolve this (it would clobber the archived record), so the duplicate must be reconciled by hand.
These backlog checks run once the repository is adopted. oat doctor keys the
pjm:* family on repository adoption state rather than on
tools.project-management, so a repository whose PJM capability lives at user
scope still gets full backlog diagnostics. An unadopted repository reports
pjm:adoption with the oat pjm init recovery instead of backlog drift.
Doctor reports drift; it never auto-fixes. A human or agent runs oat backlog archive (or corrects the status) and re-runs doctor to confirm the backlog is clean again.