# Round Lifecycle and Active Work — Product Requirements Document

## Document status

- Status: proposed.
- Date: 2026-08-11.
- Owner: Objective Guard maintainers.
- Governing evidence: [E-005](../episodes/E-005-new-phase-without-declared-round.md), [E-024](../episodes/E-024-transition-blocked-by-accepted-debt.md), [E-028](../episodes/E-028-local-contract-blocked-rotation.md), [E-033](../episodes/E-033-test-suite-could-not-survive-round-rotation.md), and [E-035](../episodes/E-035-long-multi-agent-round-exposed-traceability-gaps.md).
- Related requirements: [product contract](02-product-contract.md), [technical architecture](04-technical-architecture.md), and [roadmap](06-roadmap-and-criteria.md).

## Executive summary

Objective Guard currently treats a successful `round-end` as evidence, not as a terminal lifecycle transition. The versioned `oguard.contract.json` remains the active contract, its preflight can become ready again, and an agent receiving a new user request may continue attributing work to a completed or obsolete round.

This PRD separates durable governance from active execution. Objectives and KRs remain deliberate, versioned artifacts. The active round pointer, lifecycle state, authorization, and agent progress become local operational state. A successful final close makes the round terminal and invalidates its preflight. New work then requires an explicit successor, while blocked or interrupted work can be parked or superseded without rewriting the durable plan.

The first delivery must solve the observed failure without requiring the complete subround architecture: after terminal closure, `task-flow` must never return `ready` for the closed round.

## Problem statement

### Observed failure

An agent can complete verification, run `round-end`, and receive `PASS`. When a new request arrives, the repository still exposes the same versioned contract as active. `task-flow` validates that contract and may return `ready`, even though the previous work was already closed and the new request has a different intent.

This creates four failures:

1. The agent cannot distinguish “continue the previous task” from “start new work.”
2. New changes can be attributed to a stale round and objective.
3. Rotating work requires rewriting versioned JSON and often a clean Git tree.
4. A blocked or interrupted round has no lightweight, explicit lifecycle transition that preserves its evidence without keeping it operationally current.

### Evidence

- [Fact] E-005 records a new phase implemented before a successor round was declared.
- [Fact] E-024 records accepted debt that blocked subsequent rotation until a special baseline acceptance was added.
- [Fact] E-028 records the local contract blocking the rotation needed to correct its scope.
- [Fact] E-033 records a dirty, failing round that could neither close nor rotate without a maintainer decision.
- [Fact] E-035 records repeated contract rewrites and coupling between durable governance and round identity.
- [Fact] In the local dogfood log, `round-end` is repeatable and substantially more frequent than `round-transition`; a successful close does not remove the active contract.
- [Fact] After `R-KPI-SEMANTICS` returned `PASS`, a later `task-flow` for a different user request still returned `ready` for `R-KPI-SEMANTICS`.

The event counts demonstrate lifecycle behavior, not prevalence across independent users. They must not be interpreted as a product-wide failure rate.

## Product principles

1. **A round is an execution unit; an objective is durable governance.** Closing one does not imply achieving the other.
2. **Terminal means terminal.** A closed, abandoned, or superseded round cannot authorize edits.
3. **New intent must be explicit.** The guard may identify incompatible state, but it cannot silently invent the next objective, scope, or task type.
4. **Evidence is append-only.** Parking, abandoning, accepting debt, or superseding work preserves prior results.
5. **Operational state is not durable governance.** Agent identity, active pointer, authorization, and progress do not require a permanent plan rewrite.
6. **Dirty work is never silently adopted.** A transition must classify or isolate pre-existing changes before attributing new edits.
7. **A passing guard proves only declared checks.** Lifecycle closure does not certify objective achievement.

## Goals

- Make round closure explicit, terminal, and idempotent.
- Prevent preflight authorization under a terminal round.
- Clearly tell an agent whether to continue, close, park, supersede, or start new work.
- Separate the versioned objective/KR plan from the local active-round pointer.
- Preserve unfinished and blocked work without forcing it to remain active.
- Support clean transitions and explicit dirty-surface carryover.
- Preserve reproducible, append-only evidence and contract hashes.
- Provide migration from the current versioned-active-contract model.

## Non-goals

- Automatically infer the user's true objective from natural language.
- Automatically declare a parent objective achieved after one round passes.
- Hide, stash, commit, discard, or rebase dirty work without explicit authority.
- Replace Git branches, worktrees, CI, issue trackers, or orchestrators.
- Implement distributed multi-agent scheduling in the first delivery.
- Make statistical or LLM classification authoritative for lifecycle transitions.

## Actors and jobs to be done

### Programming agent

- Know which round, if any, currently authorizes edits.
- Know whether a new request can continue the active round.
- Receive one precise remedy when work is closed, blocked, or stale.

### Supervising developer

- Close, park, abandon, or supersede work without losing evidence.
- Decide how dirty work is handled before a new round starts.
- Distinguish round completion from objective achievement.

### CI and harness integrator

- Read stable machine states and reason codes.
- Invalidate preflight when lifecycle changes.
- Avoid depending on mutable prose or a model judgment.

### Evaluator

- Reconstruct objective → round → transition → evidence without treating repeated `round-end` executions as distinct completed tasks.

## Domain model

### Objective lifecycle

| State | Meaning | May contain active rounds? | Terminal |
| --- | --- | --- | --- |
| `active` | The durable objective remains pursued. | Yes | No |
| `achieved` | A human or authorized workflow accepted objective-level evidence. | No | Yes |
| `abandoned` | The objective will no longer be pursued. | No | Yes |
| `superseded` | A named successor replaces the objective. | No | Yes |

Objective transitions are explicit and are never inferred from `round-end`.

### Round lifecycle

| State | Meaning | Edit authorization |
| --- | --- | --- |
| `draft` | Intent is revisable and grants no authorization. | Denied |
| `active` | Frozen round contract and matching baseline exist. | Eligible |
| `blocked` | Closure or policy found an unresolved blocker. | Policy-dependent; default denied for unrelated work |
| `parked` | Work is intentionally paused and remains resumable. | Denied |
| `closed` | Required closure completed successfully. | Denied permanently |
| `abandoned` | Work ended without completion, with reason and actor. | Denied permanently |
| `superseded` | A successor round replaced this work. | Denied permanently |

`closed`, `abandoned`, and `superseded` are terminal. Resuming parked work creates an append-only resume event and returns the same round to `active`; terminal rounds cannot be resumed.

### Active work pointer

At most one round per worktree is the active edit authority in the first delivery. The local pointer contains:

```json
{
  "schema_version": 1,
  "objective_id": "O-1",
  "round_id": "R-2",
  "round_contract_hash": "...",
  "state": "active",
  "activated_at": "...",
  "baseline_fingerprint": "...",
  "authorization_epoch": 3
}
```

The pointer is local and Git-ignored. The immutable round contract hash and every transition remain in append-only events and history.

## Storage model

### Versioned durable governance

- `oguard.plan.json`: objective hierarchy, KRs, and approved amendments.
- `oguard.contract.json`: repository policy and contract schema during migration; after migration it must not act as the sole active-round pointer.
- `oguard.kpis.json`: versioned KPI policy.
- Optional exported evidence under `evidence/` for review and CI.

### Local operational state

- `.oguard/active-round.json`: current edit authority or explicit no-active-round state.
- `.oguard/rounds/<round-id>.json`: immutable local round snapshot.
- `.oguard/log/events.jsonl`: append-only lifecycle and verification events.
- `.oguard/session-baseline.json`: active baseline pointer during compatibility migration.
- `.oguard/history/`: immutable hash-addressed snapshots.

The local state is reconstructable from durable artifacts and events where possible, but reconstruction must fail visibly when evidence is incomplete.

## Functional requirements

### FR-01 — Terminal successful closure

`oguard round-end --mode final` with a passing result must append `round-closed`, set the active round to `closed`, increment the authorization epoch, and invalidate every preflight for that round.

A repeated close returns `already-closed` with the original close event and does not append another completed-round observation or rerun checks unless `--verify-again` is explicitly requested. Reverification never reactivates the round.

### FR-02 — Preflight lifecycle enforcement

`oguard task-flow` must read lifecycle state before evaluating contract readiness.

- No active round: `action-required/no-active-round`.
- Closed terminal round: `action-required/round-closed`.
- Parked round: `action-required/round-parked`.
- Superseded or abandoned round: `action-required/round-terminal`.
- Active matching round: continue existing contract validation.
- Lifecycle state cannot be read: `error/lifecycle-state-unavailable`, never `ready`.

The pre-edit gate must bind authorization to the round ID, contract hash, baseline fingerprint, and authorization epoch.

### FR-03 — Explicit successor creation

`oguard next-round` creates a successor from an explicit objective, round, KR, task type, scope, checks, and obligations, or from a reviewed local intent draft.

When the prior round is terminal and the tree is clean, the transition must be one command. It archives the prior snapshot, creates the successor, activates it, and logs the causal predecessor.

### FR-04 — Park and resume

`oguard round park --reason --actor` pauses an active or blocked round without rewriting durable governance. It records the current diff fingerprint and invalidates authorization.

`oguard round resume` requires the same round snapshot and a compatible diff fingerprint. A mismatch returns `action-required/parked-surface-changed`.

### FR-05 — Abandon and supersede

`oguard round abandon --reason --actor` makes a round terminal without claiming success.

`oguard round supersede --reason --actor --successor <id>` records a terminal relationship to a successor. It cannot erase failures, unknown criteria, or dirty-surface evidence.

### FR-06 — Dirty-tree transition decision

Starting unrelated work with a dirty tree must never return `ready` automatically. The guard returns the dirty surface and supports only explicit dispositions:

- `continue-current`: keep the current round active;
- `checkpoint`: user commits or otherwise creates an external checkpoint, then retries;
- `park`: pause the round, but do not authorize a new round in the same dirty worktree;
- `new-worktree`: create or select an isolated worktree outside Objective Guard core;
- `carry-surface`: an authorized successor declares inherited files and preserves them as unattributed or predecessor-attributed surface.

`carry-surface` requires a reason, actor, predecessor ID, exact fingerprint, and declared file set. It does not make those changes evidence for the successor's KR.

### FR-07 — Objective closure

`oguard objective close --outcome achieved|abandoned|superseded --reason --actor` is separate from round closure. `achieved` requires objective-level criteria or explicit owner acceptance. The command refuses closure while a round remains active unless it is closed, parked, abandoned, or superseded in the same audited workflow.

### FR-08 — Status and agent guidance

`oguard status --json` and `task-flow --json` expose:

- objective lifecycle;
- active round or `null`;
- round state and timestamps;
- most recent close or blocker;
- dirty-tree classification;
- authorization expiry and invalidators;
- exact available actions.

Human output must lead with one of: `continue`, `close`, `resume`, `start-successor`, or `resolve-dirty-work`.

### FR-09 — Intent mismatch protocol

The harness may submit a candidate intent hash derived from explicit structured fields. Objective Guard compares IDs and declared scope; it does not decide semantic equivalence from prose.

If a request arrives after terminal closure, the result is always `start-successor`. If a request arrives during an active round and structured intent differs, the result is `action-required/intent-mismatch`, with `continue-current`, `park`, or `supersede` as explicit actions.

An optional model may propose a candidate transition, but cannot activate it.

### FR-10 — Event model

Add append-only events:

- `round-activated`;
- `round-blocked`;
- `round-parked`;
- `round-resumed`;
- `round-closed`;
- `round-abandoned`;
- `round-superseded`;
- `objective-closed`;
- `dirty-surface-carried`.

Every event includes objective ID, round ID, prior state, next state, contract hash, baseline fingerprint, timestamp, actor, reason where required, and predecessor/successor IDs where applicable.

### FR-11 — Backward compatibility

Repositories with only `oguard.contract.json` enter `legacy-active` compatibility mode.

Migration rules:

1. If the current contract hash has a latest passing final `round-end`, initialize it as `closed` and leave no active round.
2. If its latest close blocks or is incomplete, initialize it as `blocked`.
3. If no close exists and the baseline matches, initialize it as `active`.
4. If state is ambiguous, return `action-required/migration-ambiguous`; never choose `active` silently.

Migration writes local state and an event. It does not rewrite the versioned plan or historical verdicts.

## CLI surface

```text
oguard status [--json]
oguard task-flow [--intent <path>] [--json]
oguard round close [--mode final] [--json]
oguard round park --reason <text> --actor <id> [--json]
oguard round resume [--json]
oguard round abandon --reason <text> --actor <id> [--json]
oguard round supersede --successor <id> --reason <text> --actor <id> [--json]
oguard next-round ... [--from-intent] [--carry-surface <manifest>]
oguard objective status [--json]
oguard objective close --outcome <achieved|abandoned|superseded> --reason <text> --actor <id> [--json]
```

`oguard round-end` remains as a compatibility alias for `oguard round close` during one major version and emits a deprecation field in JSON, not noisy text when called by automation.

## Decision table

| Current round | Tree | Incoming work | Decision |
| --- | --- | --- | --- |
| None | Clean | New | Start successor/new round |
| Closed | Clean | New | Start successor |
| Closed | Dirty | New | Resolve dirty work; never reactivate closed round |
| Active | Clean | Same structured intent | Continue |
| Active | Clean | Different structured intent | Continue, park, or supersede explicitly |
| Active | Dirty | Different intent | Continue, checkpoint, park, new worktree, or audited carryover |
| Parked | Compatible | Resume | Resume explicitly |
| Parked | Changed | Resume | Action required; surface mismatch |
| Blocked | Any | Unrelated | Resolve, park, abandon, or supersede; never silently continue |

## User experience requirements

### Closed round

```text
[objective-guard] Previous round R-42 is closed.
No round currently authorizes edits.
Next: review intent and open a successor round.
```

### Different work during an active round

```text
[objective-guard] R-42 is still active, but the submitted task identity differs.
Choose: continue R-42, park it, or supersede it with a declared successor.
```

### Dirty work

```text
[objective-guard] New work cannot inherit 4 modified files silently.
Choose a checkpoint, isolated worktree, or audited carry-surface transition.
```

Diagnostics must name the repository root, current round, state, exact invalidator, and executable remedy. They must not instruct an agent to commit, stash, discard, or create a worktree without user authority.

## Security and integrity requirements

- Lifecycle files use atomic write and lock semantics equivalent to finding state.
- Terminal transitions are idempotent and compare expected prior state.
- Event timestamps alone do not order concurrent writers; events include monotonic sequence or state revision.
- A stale agent cannot reactivate or close a round after its authorization epoch changes.
- Actor and reason are mandatory for destructive semantic transitions: abandon, supersede, objective close, debt acceptance, and dirty carryover.
- Local-state deletion causes visible reconstruction or ambiguity, never implicit `ready`.

## Telemetry and success metrics

### Primary metrics

- `ready_after_terminal_round_rate = 0`.
- `edits_authorized_without_active_round = 0` in structured hooks.
- `duplicate_terminal_close_rate = 0`.
- At least 95% of new requests after a close receive `start-successor` on their first preflight.
- At least 90% of clean successor transitions complete with one command.

### Friction metrics

- Median commands from terminal close to successor activation.
- Rate of `migration-ambiguous`, dirty-surface, and intent-mismatch outcomes.
- Number and age of active, blocked, and parked rounds.
- Human interventions caused by lifecycle ambiguity.

### Integrity guardrails

- No historical verdict is rewritten by migration or transition.
- No inherited dirty file counts as successor KR evidence by default.
- No terminal round receives a fresh authorization epoch.
- Objective achievement is never inferred from a passing round.

## Acceptance criteria

### AC-01 — Successful close is terminal

Given an active round with passing final checks, when it closes, then status is `closed`, authorization is invalidated, and a later `task-flow` returns `action-required/round-closed` or `no-active-round`.

### AC-02 — Repeated close is idempotent

Given a closed round, when close runs again, then no second terminal completion is recorded and the original result is returned as `already-closed`.

### AC-03 — New clean work has a one-command successor path

Given a closed round and clean tree, when reviewed successor intent is supplied, then one command archives the predecessor, activates the successor, and grants eligibility for preflight.

### AC-04 — Dirty work is not silently adopted

Given modified files from a predecessor, when unrelated work is requested, then preflight is denied until an explicit disposition is recorded. Carryover preserves predecessor attribution and does not prove successor KRs.

### AC-05 — Park and resume preserve identity

Given a parked round with unchanged surface fingerprint, when resumed, then the same round becomes active with a new authorization epoch and an append-only resume event.

### AC-06 — Terminal rounds cannot resume

Given closed, abandoned, or superseded work, when resume is requested, then the command fails with a terminal-state reason and no state mutation.

### AC-07 — Objective and round closure remain separate

Given a closed passing round under an active objective, objective status remains `active` until an explicit objective-level transition occurs.

### AC-08 — Legacy migration is conservative

Given a legacy contract with a passing final close, migration creates no active round. Given ambiguous history, migration returns action-required rather than choosing active.

### AC-09 — Concurrent stale agent is rejected

Given an agent authorized under epoch N, when another transition increments the epoch, then edits and closure from epoch N are denied with the current state and remedy.

### AC-10 — Evidence remains reconstructable

Given close, park, resume, and supersede transitions, exported evidence reconstructs prior/next state, hashes, actors, reasons, and causal links without reading mutable current-state files.

## Delivery plan

### P0 — Terminal close and stale-round prevention

- Add round lifecycle state and authorization epoch.
- Make passing final close terminal and idempotent.
- Make `task-flow` lifecycle-aware.
- Invalidate preflight on every lifecycle transition.
- Add legacy-state migration and regression tests reproducing the `R-KPI-SEMANTICS` case.

Gate: after a passing close, zero structured edits can be authorized until a successor is activated.

### P1 — Durable governance / operational state split

- Add local active-round pointer and immutable local snapshots.
- Stop treating the versioned plan as the operational pointer.
- Add `status`, one-command clean successor activation, and compatibility aliasing.
- Export lifecycle evidence without local paths.

Gate: three consecutive rounds under one objective require no rewrite of `oguard.plan.json` and remain fully reconstructable.

### P2 — Interrupted and concurrent work

- Add park/resume, abandon, supersede, dirty carryover, and authorization epochs.
- Add objective-level closure.
- Add inherited subrounds and generic external correlation IDs.

Gate: two agents can operate in isolated worktrees under one objective; a stale agent cannot edit after lifecycle transition, and contradictory evidence remains preserved.

## Risks and mitigations

| Risk | Mitigation |
| --- | --- |
| Local state is deleted | Conservative reconstruction; ambiguity blocks readiness. |
| More lifecycle commands increase friction | Lead with one recommended action and support one-command clean transitions. |
| Dirty carryover hides attribution | Exact manifest, predecessor linkage, and no default KR credit. |
| Agents treat round close as objective achievement | Separate commands, states, and acceptance evidence. |
| Versioned/local split weakens reviewability | Export immutable lifecycle evidence and hashes for CI/PR review. |
| Concurrent agents race | Revision/epoch compare-and-swap and stale authorization denial. |

## Open decisions

1. Whether `oguard.contract.json` becomes durable repository policy or remains a versioned template for round snapshots.
2. Whether local round snapshots use JSON files first or move directly to SQLite.
3. Whether `round-end` remains indefinitely as an alias or is removed after one major version.
4. Which identity source supplies `actor` in unattended harnesses.
5. Whether parked work may coexist with one active round in the same worktree; the initial recommendation is no.

None of these decisions blocks P0.
