Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Use `cr` when you want to:

Comment thread
rianjs marked this conversation as resolved.
- preview review actions before posting anything;
- run a live PR review with idempotent posting and resume behavior;
- reuse PR-scoped LLM sessions by default or named sessions across related live reviews;
- reuse a fixed PR-scoped reviewer cohort and its LLM sessions by default;
- inspect trusted reviewer agents available to a repository;
- manage local review run data and credentials from the terminal.

Expand Down Expand Up @@ -98,6 +98,8 @@ review data lives under the OS data directory for the `cr` binary.

For repo review guidance, reviewer-facing dossier context, and dossier/workbench
retention conventions, see [docs/review-guidance.md](docs/review-guidance.md).
For first runs, reruns, fresh sessions, thread checkpoints, and interrupted-run
recovery, see [docs/review-lifecycle.md](docs/review-lifecycle.md).

## Authentication And Setup

Expand Down Expand Up @@ -744,13 +746,15 @@ cr review --fail-on major https://github.com/OWNER/REPO/pull/123
```

Force a fresh local live review instead of using existing approval, override,
resume, or marker gates while continuing the PR's provider session:
resume, or marker gates while continuing the PR's orchestrator and reviewer
sessions:

```bash
cr review --rerun https://github.com/OWNER/REPO/pull/123
```

Start both a fresh local review and a fresh provider conversation:
Start both a fresh local review and fresh orchestrator and reviewer
conversations, including reviewer reselection:

```bash
cr review --rerun --fresh-session https://github.com/OWNER/REPO/pull/123
Expand All @@ -772,8 +776,9 @@ cr review https://github.com/OWNER/REPO/pull/123
```

By default, dry-run and live reviews of the same PR, profile, and posting
identity reuse one provider session across pushes. Override that scope with a
named session for a series of related live reviews:
identity reuse one orchestrator session and one PR-scoped reviewer cohort
across pushes. Override only the orchestrator scope with a named session for a
series of related live reviews:

```bash
cr review --session release-train https://github.com/OWNER/REPO/pull/123
Expand Down Expand Up @@ -1109,9 +1114,9 @@ Modes:
|------|-----------|
| `--dry-run` | Plan review actions, write local artifacts, and print the plan without posting. |
| `--no-post` | Alias for `--dry-run`. |
| `--rerun` | Bypass existing local approval, approval-override, resume, and marker gates and start a new live review while retaining provider-session reuse. Mutually exclusive with `--retry-posts`. |
| `--rerun` | Bypass existing local approval, approval-override, resume, and marker gates and start a new live review while reusing the PR's original reviewer cohort plus reviewer and orchestrator sessions. Mutually exclusive with `--retry-posts`. |
| `--retry-posts` | Retry missing or failed required posts for an existing run without rerunning LLM planning or checking approval overrides. Mutually exclusive with `--rerun` and incompatible with `--session`. |
| `--fresh-session` | Start a fresh provider conversation for this invocation without changing local review gates. Incompatible with `--retry-posts`, which does not run LLM planning. |
| `--fresh-session` | Reset the PR-scoped reviewer cohort, reselect reviewers, and start fresh reviewer and orchestrator conversations without changing local review gates. Incompatible with `--retry-posts`, which does not run LLM planning. |
| `--fast` | Enable fast execution for reviewer agents, overriding the profile default. Incompatible with `--retry-posts`. |
| `--no-fast` | Disable fast execution for reviewer agents, overriding the profile default. Mutually exclusive with `--fast`. |

Expand All @@ -1130,7 +1135,7 @@ Review selection and execution flags:
| `--reviewer-effort <effort>` | Override reviewer-stage effort only with `low`, `medium`, or `high`. Requires `--dry-run` or `--no-post`. |
| `--review-base-sha <sha>` | Review this base commit SHA instead of the PR's current base SHA. Requires `--review-head-sha` and `--dry-run` or `--no-post`. |
| `--review-head-sha <sha>` | Review this head commit SHA instead of the PR's current head SHA. Requires `--review-base-sha` and `--dry-run` or `--no-post`. |
| `--session <name>` | Override the default PR/profile/posting-identity scope with a named LLM session for live reviews. Not allowed with `--dry-run`, `--no-post`, or `--retry-posts`. |
| `--session <name>` | Override the PR's default orchestrator session with a named live-review session. Reviewer cohorts remain PR-scoped. Not allowed with `--dry-run`, `--no-post`, or `--retry-posts`. |

Review progress on stderr reports the merged reviewer catalog, final selected
IDs and reasoning, and each reviewer assignment with winning provenance and
Expand Down Expand Up @@ -1166,11 +1171,13 @@ whether it was ignored as unsupported, and the speed actually reported by the
provider, or `unknown` when unavailable.

Local run state and provider session state are independent. By default, each
PR/profile/posting-identity tuple gets one durable provider session shared by
dry-run and live reviews and retained when the PR head changes. `--session`
selects an explicit named live-review session instead. `--fresh-session`
skips provider resume for one invocation and replaces that scope's durable
session after successful planning (and, for live review, successful posting).
PR/profile/posting-identity tuple gets one durable orchestrator session and one
ordered reviewer cohort whose members retain their own provider sessions. Plain
follow-up reviews and `--rerun` reuse that cohort and those exact sessions even
when the PR head changes. `--session` selects an explicit named orchestrator
session only; the reviewer cohort remains PR-scoped. `--fresh-session` clears
both scopes for the invocation, runs selection again, and persists the new
orchestrator session and reviewer cohort.

Live review uses a local gate before planning or posting. If the posting
identity has already approved the PR, `cr review` exits immediately in Go code
Expand Down Expand Up @@ -1321,7 +1328,8 @@ builds the comparison model, and writes JSON and Markdown artifacts.
cr sessions list [--json]
```

Lists named LLM sessions in name order. Text output shows name, profile,
Lists named orchestrator sessions in name order. Reviewer cohorts are automatic
PR-scoped state and are not listed here. Text output shows name, profile,
provider, adapter, model, host, and last-used time. JSON output includes the
provider session ID plus created and last-used timestamps.

Expand All @@ -1331,17 +1339,18 @@ provider session ID plus created and last-used timestamps.
cr sessions show <name> [--json]
```

Shows one named LLM session. Missing sessions return an error. Text output
includes the provider session ID.
Shows one named orchestrator session. Missing sessions return an error. Text
output includes the provider session ID.

### `cr sessions delete`

```text
cr sessions delete <name> [--json]
```

Deletes one named LLM session row. It does not delete provider-side session
state. Missing sessions return an error.
Deletes one named orchestrator session row. It does not delete provider-side
session state or the PR-scoped reviewer cohort. Missing sessions return an
error.

`sessions delete` emits progress on stderr for layout resolution, legacy
migration, ledger open, and session deletion.
Expand Down
2 changes: 1 addition & 1 deletion cmd/cr/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestRun(t *testing.T) {
{name: "me command wired", args: []string{"me", "--help"}, wantCode: 0, wantStdout: "Resolve and cache", wantStdoutSubstring: true},
{name: "agents command wired", args: []string{"agents", "--help"}, wantCode: 0, wantStdout: "Inspect trusted review agents", wantStdoutSubstring: true},
{name: "review command wired", args: []string{"review", "--help"}, wantCode: 0, wantStdout: "Run an automated pull-request review", wantStdoutSubstring: true},
{name: "sessions command wired", args: []string{"sessions", "--help"}, wantCode: 0, wantStdout: "Manage named LLM sessions", wantStdoutSubstring: true},
{name: "sessions command wired", args: []string{"sessions", "--help"}, wantCode: 0, wantStdout: "Manage named orchestrator sessions", wantStdoutSubstring: true},
{name: "data command wired", args: []string{"data", "--help"}, wantCode: 0, wantStdout: "Manage local review data", wantStdoutSubstring: true},
{name: "benchmark command wired", args: []string{"benchmark", "--help"}, wantCode: 0, wantStdout: "Validate, inspect, and run benchmark suites", wantStdoutSubstring: true},
{name: "benchmark select command wired", args: []string{"benchmark", "select", "--help"}, wantCode: 0, wantStdout: "Run selector-only benchmark suites", wantStdoutSubstring: true},
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
This document records review-pipeline boundaries that are intended to stay
stable as the implementation evolves.

The canonical runtime sequence, state ownership, rerun/fresh-session semantics,
and interrupted-run recovery contract live in
[review-lifecycle.md](review-lifecycle.md).

## Durable LLM Execution Boundary

All production structured LLM actions must flow through
Expand Down
6 changes: 4 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Collective standards and automation remain canonical in their own repositories.
codereview-cli is the Open CLI Collective code-review CLI and ships the `cr`
binary. It provides configuration and credential commands, trusted-agent
inspection, dry-run and live pull-request review orchestration, inline thread
response handling through `cr respond`, named LLM session management, and local
data lifecycle commands.
response handling through `cr respond`, named orchestrator sessions, automatic
PR-scoped reviewer cohorts, and local data lifecycle commands.

The current Go code is a Cobra command tree in `internal/cmd/*` with a thin
`cmd/cr` entrypoint, shared exit-code mapping in `internal/cmd/exitcode`, and
Expand All @@ -28,6 +28,8 @@ Architecture guardrails for LLM execution, model resolution, Git provider
writes, command and review harness boundaries, inline thread lifecycle, and
Comment thread
rianjs marked this conversation as resolved.
retention live in
[`docs/architecture.md`](architecture.md).
The canonical first-run, rerun, fresh-session, checkpoint, and recovery sequence
lives in [`docs/review-lifecycle.md`](review-lifecycle.md).

The temporary architecture refactor workstream context for issue #420 lives in
[`docs/architecture-refactor-workstream.md`](architecture-refactor-workstream.md).
Expand Down
92 changes: 92 additions & 0 deletions docs/review-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Review Lifecycle

This document is the canonical end-to-end lifecycle for `cr review`. It covers
the resumable reviewer sessions and early thread checkpoints introduced by
issue #529. Task artifact details remain in
[llm-task-artifacts.md](llm-task-artifacts.md).

## State ownership

| State | Scope | Authority |
|---|---|---|
| Review run, findings, and planned actions | PR head/base, profile, posting identity, attempt | Local ledger and run artifacts |
| Reviewer cohort and reviewer provider sessions | PR, profile, posting identity | Local ledger |
| Default orchestrator session | PR, profile, posting identity | Local named-session row |
| Explicit `--session` orchestrator session | Supplied name | Local named-session row |
| Posted thread summary | Posting identity and inline thread | PR marker; local action/task state is fallback until posting succeeds |
| Posted review actions | Run and action marker | PR state reconciled with the local outbox |

The reviewer cohort is ordered and fixed after its first selection. Each member
stores its agent ID, broad or scoped assignment, runtime compatibility fields,
and latest provider session ID. The explicit `--session` flag never changes the
reviewer cohort's PR scope.

## First run

1. Resolve the PR, changed files, discussion, reviewer catalog, and runtime.
2. Run selection on the orchestrator provider session.
3. Persist the selected reviewer cohort before starting reviewers.
4. Analyze eligible human replies sequentially on the same orchestrator
session.
5. Persist thread reply and resolution actions, then run the partial outbox.
Replies are attempted before resolutions. The run remains open.
6. Give every active reviewer the analyzed discussion outcome and actual early
action status (`posted`, `pending`, or dry-run-only), then run reviewers.
7. Run rollup on the latest orchestrator provider session, merge final planning
around the early action IDs and statuses, and run the final outbox.

A scoped cohort member whose rebased assignment is empty remains in the cohort
but does not receive a no-op LLM call.

## Later runs and `--rerun`

Plain follow-up runs and `--rerun` both load the original PR-scoped cohort,
validate every member against the current catalog and reviewer runtime, and
deterministically rebase assignments onto the current changed files. Selection
does not run again. Active reviewers resume their exact saved provider session.

`--rerun` changes local gate behavior only: it bypasses approval, override,
resume, and marker gates to allocate a new review attempt. It does not select a
new cohort or start new provider conversations.

Reuse fails with `--fresh-session` guidance when a cohort member is missing or
runtime-incompatible, `--max-agents` is smaller than the saved cohort, or the
cohort cannot cover every changed file.

## `--fresh-session`

`--fresh-session` clears the active PR-scoped reviewer cohort for this review,
does not resume the orchestrator provider session, runs selection again, and
persists the replacement cohort. It can be combined with `--rerun` when both
gate bypass and new reviewer/orchestrator conversations are required.

## Interrupted-run recovery

Structured LLM task metadata resumes an interrupted task inside the same run.
If interruption occurs after early thread actions are persisted, those actions
are valid partial planning state: recovery retries/reconciles them and continues
the remaining planning phases. Final planning preserves their IDs, attempts,
posted state, and pending errors.

A posting-identity-authored `codereview:thread-summary` marker suppresses repeat
thread analysis. A newer human reply after that marker reopens the thread. Local
task and action state suppresses duplicate work only while the marked reply has
not reached the PR.

## Failure and idempotency behavior

- Reviewer-local LLM failures remain isolated and retain any provider session
ID reported by the failed attempt.
- Early posting failures remain pending and do not prevent reviewer execution.
Final posting retries them.
- A moved head or base stops checkpoint posting before reviewer work.
- Outbox reconciliation checks markers before every dispatch, so a process
crash after a provider write but before the local update does not duplicate
the reply or final review.
- Thread replies are ordered before resolutions, and a resolution is not sent
until its reply is posted or reconciled.
- Completed summary markers on the PR outrank stale local fallback state.

`cr sessions` lists and manages named orchestrator sessions only. Reviewer
cohorts are automatic PR state and are replaced through
`cr review --fresh-session`.
23 changes: 23 additions & 0 deletions internal/app/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,22 @@ func buildReviewRunner(ledgerStore *ledger.Store, repoProvider gitprovider.GitPr
RetentionManualOnly: req.RetentionManualOnly,
ResolveRepoRoot: resolveRepoRoot,
GitCommand: gitCommand,
ThreadCheckpoint: func(ctx context.Context, run ledger.Run, pipelineReq pipeline.Request) error {
result, err := outbox.PostCheckpoint(ctx, outbox.Options{Store: ledgerStore, Provider: liveProvider, Limiter: limiter}, outbox.Request{
Run: run,
PRRef: pipelineReq.PRRef,
PostingIdentity: pipelineReq.PostingIdentity,
DesiredOutcome: ledger.OutcomeComment,
ResolveThreadPermissionAdvisory: postingUsesGitHubApp(profile),
})
if err != nil {
return err
}
if result.Aborted {
return gitprovider.ErrStaleSHA
}
return nil
},
}
return reviewRunner{
pipeline: pipelineOpts,
Expand Down Expand Up @@ -441,6 +457,13 @@ func buildReviewRunner(ledgerStore *ledger.Store, repoProvider gitprovider.GitPr
}
}

func postingUsesGitHubApp(profile config.Profile) bool {
if profile.ReviewerCredentials != nil {
return profile.ReviewerCredentials.AuthMode == config.GitAuthModeGitHubApp
}
return profile.Git.AuthMode == config.GitAuthModeGitHubApp
}

func buildApprovalOverrideClassifier(profile config.Profile, adapter llm.Adapter, warnings io.Writer) approvaloverride.Classifier {
return &lazyApprovalOverrideClassifier{
profile: profile,
Expand Down
7 changes: 4 additions & 3 deletions internal/cmd/datacmd/datacmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,10 @@ func TestDataPruneDefaultIgnoresConfiguredRetention(t *testing.T) {
}
layout := mustLayout(t)
store := openLedgerForTest(t, layout)
allocateRun(t, store, layout, "live-31d", ledger.PostModeLive, testNow().Add(-31*24*time.Hour))
allocateRun(t, store, layout, "live-91d", ledger.PostModeLive, testNow().Add(-91*24*time.Hour))
allocateRun(t, store, layout, "dry-8d", ledger.PostModeDryRun, testNow().Add(-8*24*time.Hour))
now := time.Now().UTC()
allocateRun(t, store, layout, "live-31d", ledger.PostModeLive, now.Add(-31*24*time.Hour))
allocateRun(t, store, layout, "live-91d", ledger.PostModeLive, now.Add(-91*24*time.Hour))
allocateRun(t, store, layout, "dry-8d", ledger.PostModeDryRun, now.Add(-8*24*time.Hour))
if err := store.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
Expand Down
Loading
Loading