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
2 changes: 1 addition & 1 deletion .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"plugins": [
{
"name": "flow-next",
"version": "9.0.1",
"version": "9.0.2",
"source": {
"source": "local",
"path": "./plugins/flow-next"
Expand Down
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
},
"metadata": {
"description": "Plan-first workflows for Claude Code and Factory Droid. Ships flow-next: zero-dep, spec-driven, unattended flow --auto runs.",
"version": "9.0.1"
"version": "9.0.2"
},
"plugins": [
{
"name": "flow-next",
"description": "Zero-dependency, spec-driven agentic SDLC: durable specs, context-fit plans, re-anchored workers, adversarial cross-model review, and receipts. Includes 21 subagents, 26 commands, 29 skills.",
"version": "9.0.1",
"version": "9.0.2",
"author": {
"name": "Gordon Mickel",
"email": "gordon@mickel.tech",
Expand Down
148 changes: 148 additions & 0 deletions .flow/specs/fn-304-plan-and-completion-review-diff-against.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"branch_name": "fn-304-plan-and-completion-review-diff-against",
"completion_review_status": "not_required",
"completion_reviewed_at": "2026-10-18T11:11:14.838825Z",
"created_at": "2026-10-18T10:55:20.527177Z",
"default_impl": null,
"default_review": null,
"default_sync": null,
"depends_on_epics": [],
"id": "fn-304-plan-and-completion-review-diff-against",
"impl_review_rounds": {
"fn-304-plan-and-completion-review-diff-against.1": 0
},
"next_task": 1,
"no_plan": true,
"plan_review_rounds": 0,
"plan_review_status": "unknown",
"plan_reviewed_at": null,
"review_attempts": [
{
"artifact_sha256": "0aab128a3e5feaaa08ec3df8ff232970aaac4c9f6a5afa576170958ce69d73f9",
"backend": "codex",
"base_sha": "271f7e36d9ebf4241edb021d5c283864eb1c35d6",
"counter_kind": "impl",
"effort": "high",
"failure_class": null,
"finalized": {
"digest": "complete",
"receipt": "complete",
"status": "not_applicable"
},
"findings_digest": {
"backend": "codex",
"digest_truncated": false,
"items": [
{
"chainRoot": "finding-18f309439ccbd5eb31e4bff737ddc8ba",
"classification": "introduced",
"findingId": "finding-18f309439ccbd5eb31e4bff737ddc8ba",
"firstSeenThisRound": true,
"severity": "P1",
"status": "open"
},
{
"chainRoot": "finding-25b1fa68b2ebbe6e8b0e1e25ffe618f2",
"classification": "introduced",
"findingId": "finding-25b1fa68b2ebbe6e8b0e1e25ffe618f2",
"firstSeenThisRound": true,
"severity": "P2",
"status": "open"
}
],
"reviewKind": "implementation"
},
"forced": false,
"hash_epoch": 0,
"head_sha": "12d09ad224cd795f4870fd61e0eb3289c9322443",
"head_sha_observed": true,
"kind": "impl",
"model": "gpt-6-astra",
"outcome": "verdict",
"output_bytes": 1914,
"output_sha256": "ab051896b4b47f6f4e738b1b3b93163ee81a372a600c1aac4d5b294bdf5ac36d",
"reservation_id": "7f7fcd0a423840618eec23b05d20d89d",
"round_consumed": true,
"scope": "impl:fn-304-plan-and-completion-review-diff-against.1",
"superseded_by": null,
"task": "fn-304-plan-and-completion-review-diff-against.1",
"timestamp": "2026-10-18T11:08:27.892058Z",
"verdict": "NEEDS_WORK"
},
{
"artifact_sha256": "bf21093b666d437843108cf49635ee7756bf2c5cd4f5290fd38816a33b7af956",
"backend": "codex",
"base_sha": "271f7e36d9ebf4241edb021d5c283864eb1c35d6",
"counter_kind": "impl",
"effort": "high",
"failure_class": null,
"finalized": {
"digest": "complete",
"receipt": "complete",
"status": "not_applicable"
},
"findings_digest": {
"backend": "codex",
"digest_truncated": false,
"items": [
{
"chainRoot": "finding-18f309439ccbd5eb31e4bff737ddc8ba",
"classification": "introduced",
"findingId": "finding-18f309439ccbd5eb31e4bff737ddc8ba",
"firstSeenThisRound": false,
"severity": "P1",
"status": "fixed"
},
{
"chainRoot": "finding-25b1fa68b2ebbe6e8b0e1e25ffe618f2",
"classification": "introduced",
"findingId": "finding-25b1fa68b2ebbe6e8b0e1e25ffe618f2",
"firstSeenThisRound": false,
"severity": "P2",
"status": "fixed"
}
],
"reviewKind": "implementation"
},
"forced": false,
"hash_epoch": 0,
"head_sha": "5ab34056b4839fbc73516435ceedce2357285434",
"head_sha_observed": true,
"kind": "impl",
"model": "gpt-6-astra",
"outcome": "verdict",
"output_bytes": 1427,
"output_sha256": "784490e4a2dca42ee726ede1b54ea94b7bbc213e72cb98d0d6582366868ca83a",
"reservation_id": "de846d7dc886411d8fa6f081ff3dd860",
"round_consumed": true,
"scope": "impl:fn-304-plan-and-completion-review-diff-against.1",
"superseded_by": null,
"task": "fn-304-plan-and-completion-review-diff-against.1",
"timestamp": "2026-10-18T11:11:01.117513Z",
"verdict": "SHIP"
}
],
"review_hash_epoch": {
"impl:fn-304-plan-and-completion-review-diff-against.1": 1
},
"review_pending_rounds": {},
"review_reservations": {},
"review_transport_failures": {
"impl:fn-304-plan-and-completion-review-diff-against.1": 0
},
"spec_path": ".flow/specs/fn-304-plan-and-completion-review-diff-against.md",
"status": "done",
"title": "Plan and completion review diff against the spec's own base",
"tracker": {
"baseHashFlow": null,
"baseHashTracker": null,
"depRelations": [],
"id": null,
"identifier": null,
"lastSyncedAt": null,
"mergeBaseFlow": null,
"mergeBaseTracker": null,
"url": null
},
"updated_at": "2026-10-18T11:15:16.003929Z"
}
53 changes: 53 additions & 0 deletions .flow/specs/fn-304-plan-and-completion-review-diff-against.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Plan and completion review diff against the spec's own base

## Goal & Context

[paraphrase] Plan review and completion review default their diff base to a literal `main` on every review backend. On a repository whose default branch is `master` both fail before dispatch (`cannot resolve review snapshot: main...HEAD`, issue #538). On a spec whose work lands on a branch other than the default branch (a release integration branch, a chained parent, a `flow --goal` integration branch), completion review would judge the wrong diff even once the default branch is found: it would include every earlier child of that branch (issue #569). The other review commands already resolve the repository's default branch; work already resolves the spec's base and keeps it for its own gates, but no review reads it.

Both issues were reported by @DanielKillenberger.

## Architecture & Data Models

[paraphrase] One base resolution for a spec's reviews:

- **Plan review** uses an explicit `--base`, else the repository's default branch, resolved the same way the other review commands resolve it.
- **Completion review** uses the first that applies: an explicit `--base`; the base branch work recorded for this spec's run; the chain parent's branch; the repository's default branch.
- **Work records the base branch name** for the spec it runs, beside the merge-base SHA it already keeps. A review computes the merge-base from that branch at review time, so a child rebased onto a newer integration branch does not see commits from other children.

[inferred] Ownership: the recorded base belongs to one spec's run. A record written for a different spec is never used for this spec's review; it falls through to the next step. A recorded branch that no longer resolves falls through the same way rather than failing the review.

## API Contracts

[paraphrase] Every review backend's `plan-review` and `completion-review` command accepts an omitted `--base` and resolves it as above; an explicit `--base` keeps its current meaning. The host completion-review workflow resolves its diff base in the same order when the caller passes no explicit base. Receipts keep recording the base they reviewed.

## Edge Cases & Constraints

- [inferred] No default branch resolvable and no other source: the review fails with the existing "no default branch resolved ... pass --base <ref>" error, before any round is reserved.
- [inferred] An explicit `--base` (or host `BASE_COMMIT`) that does not resolve still fails closed; it never falls back to another base.
- [inferred] All supported review backends behave the same (Cross-platform parity).

## Acceptance Criteria

- **R1:** [paraphrase] On a repository whose default branch is `master` (no `main`), plan review and completion review with no `--base` resolve `master` and run, on every backend. Errors: no resolvable default branch and no other source → the existing default-branch error naming the candidates tried, no round reserved.
- **R2:** [paraphrase] Completion review with no `--base`, on a spec whose work run recorded a base branch, diffs against the merge-base of that branch and HEAD computed at review time. A test shows a child spec on an integration branch that also holds another child's commits reviews only its own commits. Errors: a record for a different spec, or a recorded branch that no longer resolves, falls through to the next source.
- **R3:** [paraphrase] Completion review with no `--base` and no run record, on a chained spec, diffs against the chain parent's branch. Errors: parent branch unresolvable → falls through to the default branch.
- **R4:** [paraphrase] An explicit `--base` on either command still wins over every other source and fails closed when it does not resolve (no error surface beyond today's).
- **R5:** [paraphrase] The host completion-review workflow resolves its diff base in the R2-R4 order and no longer names a literal `main`/`master` fallback.
- **R6:** [paraphrase] Work records the base branch name for the spec it runs, alongside the merge-base SHA it already records; work's existing consumers of the SHA are unchanged (no error surface beyond the existing branch-setup fence).

## Boundaries

- No `review.base` config key.
- The base is not persisted on the spec record (no `base_branch` field).
- [inferred] No separate goal-branch derivation in flowctl: a goal child's work run is dispatched with the goal integration branch as its base, so R2 covers it.
- [inferred] Implementation review's base resolution is unchanged.

## Decision Context

[paraphrase] A config key and a persisted spec field were proposed in #569 and left out: the chain parent, the work run's recorded base and the default branch cover the reported setups (a release integration branch with children run via `--base`, and goal mode). They would only matter for a completion review run by hand in a cold session after `work --base`; revisit if that is reported.

[inferred] Goal-branch derivation was dropped from the resolution order: flowctl has no data link from a child to its goal (the goal depends on its children, and where children land is recorded in the goal's log prose), so any derivation would be a heuristic. Goal children run work with `--base=goal/<branch_name>`, which R2 records.

## Strategy Alignment

[strategy:Cross-platform parity] The same resolution on every review backend, including host.
15 changes: 15 additions & 0 deletions .flow/tasks/fn-304-plan-and-completion-review-diff-against.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"assignee": null,
"claim_note": "",
"claimed_at": null,
"created_at": "2026-10-18T10:55:57.214723Z",
"depends_on": [],
"id": "fn-304-plan-and-completion-review-diff-against.1",
"implicit_owner": true,
"priority": null,
"spec": "fn-304-plan-and-completion-review-diff-against",
"spec_path": ".flow/tasks/fn-304-plan-and-completion-review-diff-against.1.md",
"status": "done",
"title": "Implement Plan and completion review diff against the spec's own base",
"updated_at": "2026-10-18T10:55:57.214723Z"
}
23 changes: 23 additions & 0 deletions .flow/tasks/fn-304-plan-and-completion-review-diff-against.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
satisfies: [R1, R2, R3, R4, R5, R6]
---
# fn-304-plan-and-completion-review-diff-against.1 Implement Plan and completion review diff against the spec's own base

## Description
TBD

## Acceptance
Every R-ID in the parent spec's ## Acceptance Criteria is satisfied; judge this task against the spec's criteria directly.

## Done summary
Plan review falls back to the repository's default branch instead of a literal main (#538). Completion review, on every CLI backend and the host workflow, uses an explicit base, else the base work recorded for this spec (`.flow/tmp/spec_base_branch`, spec id + branch name, written by work's branch fence), else the chain parent's branch, else the default branch (#569). Host compares the record against the canonical spec id and only queries the chain when no valid record exists.

Tests: master-only repo (plan + completion), recorded integration base excludes another child's commits, foreign/dead record falls through, chain parent branch, explicit base wins / fails closed, no base names --base; work fence records the branch for chain and --base runs; host fence order incl. short alias.

stage: impl-review - ran codex 3-draw NEEDS_WORK (host short-alias P1, eager chain lookup P2; both fixed) -> re-review SHIP

stage: plan-sync - skipped(config: planSync.enabled != true)
## Evidence
- Commits: 12d09ad224cd795f4870fd61e0eb3289c9322443, 5ab34056b4839fbc73516435ceedce2357285434
- Tests: python3 scripts/run_tests_parallel.py; python3 -m unittest test_host_review_backend test_claude_review_commands test_sync_codex_check
- PRs:
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ Flow-Next changed shape with 5.0.0. One command, `/flow-next:flow`, reads whatev

## Unreleased

## [flow-next 9.0.2] - 2026-10-18

### Fixed

- **Completion review judges only your spec's own changes when the spec lands on a branch other than your default branch.** On a release integration branch, a chained spec or a `flow --goal` child, completion review now compares against the branch the spec was built on, so commits from the other specs on that branch no longer count as part of this one. Without `--base`, it uses the base work started the spec from, then the parent spec's branch, then your default branch. Thanks to @DanielKillenberger for reporting it in #569.
- **Plan review and completion review run on repositories whose default branch is `master`.** Without `--base`, both now find your default branch the way implementation review does. Before, they stopped with `cannot resolve review snapshot: main...HEAD`. Thanks to @DanielKillenberger for reporting it in #538.

## [flow-next 9.0.1] - 2026-10-18

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion plugins/flow-next/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flow-next",
"version": "9.0.1",
"version": "9.0.2",
"description": "Zero-dependency, spec-driven agentic SDLC: durable specs, context-fit plans, re-anchored workers, adversarial cross-model review, and receipts. Includes 21 subagents, 26 commands, 29 skills.",
"author": {
"name": "Gordon Mickel",
Expand Down
2 changes: 1 addition & 1 deletion plugins/flow-next/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flow-next",
"version": "9.0.1",
"version": "9.0.2",
"description": "Zero-dependency, spec-driven agentic SDLC: durable specs, context-fit plans, re-anchored workers, adversarial cross-model review, and receipts. Compatible with Codex, Claude Code, and Factory Droid.",
"author": {
"name": "Gordon Mickel",
Expand Down
2 changes: 1 addition & 1 deletion plugins/flow-next/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flow-next",
"version": "9.0.1",
"version": "9.0.2",
"description": "Zero-dependency, spec-driven agentic SDLC: durable specs, context-fit plans, re-anchored workers, adversarial cross-model review, and receipts.",
"author": {
"name": "Gordon Mickel",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,34 @@ an empty blob, and falsely refuses the next round as NOT_RETRYABLE. The fence
fails closed (no reservation) whenever the range cannot be bound or diffed.

```bash
DIFF_BASE="${BASE_COMMIT:-main}"
# master is a fallback for the DEFAULT only. An explicit BASE_COMMIT that does
# not resolve fails closed: silently reviewing against master would bind and
# hash a range the caller never asked for.
if ! git rev-parse --verify "$DIFF_BASE" >/dev/null 2>&1; then
if [[ -n "${BASE_COMMIT:-}" ]]; then
echo "BASE_COMMIT '$BASE_COMMIT' does not resolve; not reserving a round" >&2
exit 1
# The CLI backends' order: an explicit BASE_COMMIT, the base work recorded for
# this spec, the chain parent's branch, then the repository's default branch.
# An explicit BASE_COMMIT that does not resolve fails closed: silently reviewing
# against another base would bind and hash a range the caller never asked for.
if [[ -n "${BASE_COMMIT:-}" ]]; then
git rev-parse --verify --quiet "$BASE_COMMIT^{commit}" >/dev/null \
|| { echo "BASE_COMMIT '$BASE_COMMIT' does not resolve; not reserving a round" >&2; exit 1; }
DIFF_BASE="$BASE_COMMIT"
else
DIFF_BASE="" REC_SPEC="" REC_BASE="" PARENT_BRANCH=""
# Work records the canonical spec id; SPEC_ID may be a short alias.
CANONICAL_ID="$("$FLOWCTL" show "$SPEC_ID" --json 2>/dev/null | jq -r '.id // empty')" || CANONICAL_ID=""
read -r REC_SPEC REC_BASE < "$(git rev-parse --show-toplevel)/.flow/tmp/spec_base_branch" 2>/dev/null || true
if [[ -n "$REC_BASE" && "$REC_SPEC" == "${CANONICAL_ID:-$SPEC_ID}" ]] \
&& git rev-parse --verify --quiet "$REC_BASE^{commit}" >/dev/null; then
DIFF_BASE="$REC_BASE"
else
PARENT_BRANCH="$("$FLOWCTL" spec chain "$SPEC_ID" --json 2>/dev/null | jq -r '.parent_branch // empty')" || PARENT_BRANCH=""
for CANDIDATE in ${PARENT_BRANCH:+"origin/$PARENT_BRANCH" "$PARENT_BRANCH"} \
"$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null)" \
origin/main main origin/master master; do
if [[ -n "$CANDIDATE" ]] && git rev-parse --verify --quiet "$CANDIDATE^{commit}" >/dev/null; then
DIFF_BASE="$CANDIDATE"; break
fi
done
fi
DIFF_BASE="master"
[[ -n "$DIFF_BASE" ]] || { echo "cannot resolve diff base; pass BASE_COMMIT; not reserving a round" >&2; exit 1; }
fi
git rev-parse --verify "$DIFF_BASE" >/dev/null 2>&1 \
|| { echo "cannot resolve diff base; not reserving a round" >&2; exit 1; }
REVIEW_SNAPSHOT_FILE="${TMPDIR:-/tmp}/flow-completion-review-host-${SPEC_ID}.env"
REVIEW_HEAD_SHA="$(git rev-parse HEAD)" || exit 1
REVIEW_BASE_SHA="$(git merge-base "$DIFF_BASE" "$REVIEW_HEAD_SHA")" || exit 1
Expand Down
Loading
Loading