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 (project management: OAT's file-backed backlog, roadmap, and decision records) 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 (standard OAT format) newest-firstcompleted.mdentry.wont_doitems may omit the summary and get an entry only when one is provided. - Moves
items/<id>.mdintoarchived/with a filesystem rename, leaving Git staging to the caller. - Rewrites inbound references to the moved file across Markdown under
.oat/repo/**(tracked and untracked files that Git does not ignore; a code span whose whole content is the item's path, such as an external plan's source citation, is rewritten, while other code spans and fenced code are left as written with a warning) — external plans, decision records, and other backlog items — so no link dangles atitems/<id>.md, and reports each rewritten file. A reference it cannot resolve is left alone with a warning. - Regenerates the managed backlog index.
The command is safe to re-run: an item already in archived/ produces a no-op warning and only retries the reference rewrite and index regeneration, so an interrupted close-out finishes on the next run. 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.
The archive result includes normalized absolute affectedPaths for the whole
operation. Capture --json for each item, union the lists, and include owned
handoff deletions. Format existing affected text with the repository's
formatter, then use oat internal commit-paths with a unique retained identity
and the exact list. Include the old tracked item deletion and new destination
as well as ledger/index/references; staging only the old missing path fails.
Hooks remain enabled, and unrelated staged/unstaged work stays intact. Verify
helper availability first, stop on failure, and follow its retry diagnostics.
Settled noops claim only actual repairs; pending Git retries recover earlier
operation outputs from HEAD evidence. See exact-path maintenance
commits.
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.
oat-pjm-add-backlog-item
Invocation: Ask, “Use oat-pjm-add-backlog-item to capture a retry-safety
review for imports, including acceptance criteria.” Use the skill name,
/oat-pjm-add-backlog-item, or $oat-pjm-add-backlog-item in Codex. These
forms are agent instructions, not shell commands; the same convention
applies to the other PJM skills below.
Prerequisites: Repository PJM adoption and enough context to describe the
work. Like every PJM skill that writes files on this page, it refuses to
write until the repository has adopted PJM with oat pjm init. The skill
first runs oat pjm doctor --json and inspects adoption.state; only
declared or inferred-legacy permits writes. For none or
partial-initialization it stops and tells you to run oat pjm init. Installed
tools alone do not establish adoption. No active OAT project is required.
Example scenario: A support incident exposed uncertainty about duplicate records after retries. Capture an investigation with acceptance criteria covering the write path, relevant failure cases, and an evidence-backed recommendation, rather than creating an implementation project before the problem is understood.
The skill collects the title, context, criteria, and optional triage fields, then proposes a scope estimate for confirmation. The CLI creates the item and managed index atomically. A collision requires a more specific title, not overwriting an active or archived record. After creation, the agent fills confirmed acceptance criteria while preserving generated frontmatter.
Expected output: A BL-YYMMDD-slug item under
.oat/repo/pjm/backlog/items/, a refreshed managed index, and any useful
curated-overview note. The summary includes the actual ID/path and confirmed
scope estimate. Creating the item records work to consider; it does not
start execution or publish an external issue.
What it does without asking: After you confirm the scope estimate, it
runs oat backlog new, which writes the item file and regenerates the
managed backlog index. It then fills in the acceptance criteria you
confirmed and may add a short note to the curated overview in
.oat/repo/pjm/backlog/index.md. Its workflow has no commit or push step
and sends nothing to an external tracker.
Next step: Prioritize it with oat-pjm-review-backlog or explicitly start a suitable project. Follow the atomic close-out flow above when the work reaches a terminal state.
oat-pjm-review-backlog
Invocation: /oat-pjm-review-backlog, optionally with a backlog root and
--roadmap=<path> or --output=<path>. The default source is the file-backed
repository backlog. --archive-dated also saves a dated snapshot alongside
the living review.
Prerequisites: Adopted repository PJM, verified through
oat pjm doctor --json before writes, and accessible backlog items. A
roadmap is optional alignment evidence; an active OAT project is not
required. The skill stops and tells you to run oat pjm init if adoption
is absent or partial.
Example scenario: Your team has time for one maintenance initiative, but the backlog mixes isolated fixes with work blocked on shared infrastructure. Review item value, effort, and dependencies against the roadmap, then discuss a realistic kickoff stack rather than treating every high-priority item as safe to run concurrently.
Expected output: A living review at
.oat/repo/pjm/backlog/reviews/backlog-and-roadmap-review.md unless overridden,
with ratings, dependency mapping, parallel lanes, roadmap alignment, and a
recommended sequence. Dated snapshots remain beside the living report.
References pair each backlog ID with a human-readable title so readers do
not need a separate board lookup.
Priority alignment and kickoff handoffs are optional collaborative steps.
They need operator context and an agreed kickoff stack; the review does not
silently choose capacity, lane count, or project starts. Any generated
handoff points back to the item and authoritative inputs rather than
replacing them. The execution view is written to
.oat/repo/pjm/backlog/reviews/priority-alignment.md and each handoff to
.oat/repo/pjm/handoffs/<BL-id>.md; handoffs for items dropped from the
kickoff stack are deleted with git rm.
What it does without asking: It writes the living review (and the dated
snapshot when you pass --archive-dated), creating the reviews/ folder if
needed. Only after you accept the walkthrough and agree a kickoff stack does
it write or update priority-alignment.md, write or refresh handoff files,
and git rm handoffs for items that left the stack. It hands items to
oat-repo-improve only if you accept that offer, and it never generates
external plans itself.
Next step: Accept a priority walkthrough if you need an execution view,
or explicitly choose items for external-plan generation through
oat-repo-improve. A review recommendation is not implementation approval.
oat-pjm-decision
Invocation: Ask, “Use oat-pjm-decision to record our choice to make import retries idempotent, with its context and trade-offs.” This skill is for an explicit request or confirmed capture, not automatic recording of every routine choice.
Prerequisites: Repository PJM adoption verified with
oat pjm doctor --json, the decision scaffold, and meaningful context,
decision, and consequences. No active OAT project is required.
Review existing relevant decisions before finalizing a durable choice.
Example scenario: The team confirmed that a stable import identity must span retries, accepting additional storage and cleanup work. Record why this approach was chosen and what it enables or constrains so a future maintainer does not reverse it based only on the extra implementation cost.
Expected output: A CLI-created DR-YYMMDD-slug record under
.oat/repo/reference/decisions/, completed Context, Decision, and
Consequences sections, and a generated index entry. The default status is
proposed; select accepted only when that is the actual decision state.
Preserve generated identity and use the CLI to refresh indexed metadata,
never hand-editing the managed index or replacing a collision.
What it does without asking: It runs oat decision new, which writes
the record and regenerates the managed decision index, then fills in the
Context, Decision, and Consequences sections and reruns
oat decision regenerate-index if it changed the status or title. Its
workflow has no commit or push step. If the decision index markers are
missing, it stops and tells you to repair the repository with
oat pjm init.
Next step: Review the record and link relevant work to it. If later evidence changes the choice, preserve history through the appropriate decision status or successor record rather than silently rewriting the original rationale.
oat-pjm-remote
This skill connects OAT records to a remote tracker such as GitHub, Linear, or Jira. A binding is OAT's stored link between one local backlog item or project and one remote issue. Intake reads a remote issue into a local backlog item and creates a binding. Publish creates or updates the remote issue from a local record.
Invocation: Ask, “Use oat-pjm-remote to refresh this repository's existing remote binding from the configured tracker.” Name the intended operation and exact remote context; do not assume local backlog capture authorizes publishing it remotely. The skill supports these operations:
intakereads one remote issue into a local backlog item (--to-backlog <id>) and creates the binding.publishcreates or updates the remote issue for one selected binding, backlog item, or project.refreshupdates OAT's saved copy of a binding without writing remotely.reconcilecompares local, remote, and last-known changes before proposing any write.operation continuesubmits the result of a pending step so the same operation can finish.
Run these through the skill rather than as one-line shell commands. Each
oat pjm remote command expects a host agent (the AI coding tool running
the skill) to supply capability evidence on standard input: a short,
sanitized description of which connector or provider CLI it can use for
that tracker. The skill gathers that evidence for you.
Prerequisites: PJM adoption verified by oat pjm doctor --json (the
skill stops and points you to oat pjm init otherwise), applicable local
policy, and an available host capability matching the provider, operation,
and exact account, workspace, site, or repository. No active OAT project is
required. Remote mutation authority defaults to read-only, so publish
and every other remote write are refused until your repository policy
grants a higher authority (user-approved, user-authorized, or
autonomous). Mutations also need the current caller-owned authority
evidence (a record of who authorized this write) that the CLI asks for. The
skill looks for granted connectors first; if none matches, it can inspect
an already configured provider CLI with live help before the first attempt.
If neither surface qualifies, it stops rather than installing tools,
requesting credentials, or improvising a route.
The way a binding was created controls which fields can move. A binding created by intake only pulls the title, description, and priority into OAT. A binding created by publish lets those fields flow in both directions. See Remote Project Management for policy, previews, approvals, and the remaining commands.
Example scenario: A tracker item changed after its OAT binding was last refreshed. Request a refresh of that binding, preserving the local policy boundary. A successful host read or visible tracker update is only evidence for the CLI to verify, not an authoritative success verdict.
The host discovers live capability, executes only the exact semantic action emitted by the OAT CLI (the provider-neutral step the CLI asks it to perform, such as reading or updating one issue) at most once, and submits one bounded sanitized observation (a short summary of the result with credentials and raw provider responses removed). Continue only when the same durable operation emits another action; do not broaden a field mask (the list of fields the action may touch) or switch execution surfaces mid-attempt. Discussion reads remain bounded read-only evidence, not comment synchronization or an invitation to persist raw threads.
Expected output: The CLI's terminal operation envelope and verified
binding state, or an explicit pending/blocked outcome. Only ok after
authoritative read-back establishes remote success. Durable evidence excludes
credentials, native requests/responses, tool catalogs, and raw provider
payloads.
What it does without asking: It runs the adoption preflight
(oat pjm doctor --json), inspects the connectors your host has granted,
runs the operation you asked for, and executes each action the CLI emits
against the tracker at most once. The CLI saves operation state and
snapshots under .git/oat/pjm-remote/, and saves backlog bindings under
.oat/repo/pjm/remote/bindings/, which is not gitignored. When the target
backlog item does not exist yet, intake creates it from the remote title,
description, and priority. A remote write
happens only when your policy grants mutation authority and any preview or
approval the CLI requires has been given; under the default read-only
authority, the CLI refuses it. See the storage notes on the
Remote Project Management
page before committing.
Next step: Inspect the CLI verdict and address a stated policy, authority, or capability blocker before another action. Make any requested local distillation a separately scoped edit, not a hidden remote mutation.
oat-pjm-update-repo-reference
Invocation: Ask, “Use oat-pjm-update-repo-reference to reconcile the retry work's shipped outcome, deferred follow-ups, and decision history.” Identify the actual changed capability and the evidence to use.
Prerequisites: Repository PJM adoption verified with
oat pjm doctor --json, canonical backlog/reference scaffolds, and evidence
of what changed. It uses recent project artifacts when they exist, but you
do not need an active OAT project to run it.
Example scenario: Retry handling shipped, but an operator dashboard was deferred and the roadmap still lists the whole effort as upcoming. Reconcile the shipped portion, capture the deferred work, and preserve the decision behind the retry model without declaring the dashboard complete.
Expected output: Applicable updates to current-state, Now/Next/Later
roadmap guidance, curated backlog context, item records, completed history,
and durable decisions. Completed items use oat backlog archive for atomic
status/history/move/index close-out; decisions use the canonical decision
commands. Managed indexes are regenerated, not hand-edited. The final
sanity check distinguishes valid active paths from retired references.
What it does without asking: It edits current-state.md, roadmap.md,
the curated overview, and backlog item files under .oat/repo/pjm/ as it
judges applicable, with no separate approval step. It runs
oat backlog archive for completed items, which moves each item file into
archived/ with a filesystem rename, and it creates decision
records with oat decision new. It automatically commits the exact archive
close-out paths and any owned kickoff handoff deletion with hooks enabled.
Other reference edits remain uncommitted, and the workflow has no push step,
so review the close-out commit and remaining working-tree diff afterwards.
If the backlog index markers are missing, it stops and tells you to repair
the repository with
oat pjm init.
Next step: Review the changes against the implementation evidence and rerun diagnostics for lifecycle consistency. Start deferred work separately; reference synchronization must not turn a follow-up into a shipped claim.