Skip to content

feat(operator): park-record-missing check id + fresh-clone E2E (#356) - #362

Merged
pbean merged 4 commits into
mainfrom
feat/operator-park-record-validate
Jul 28, 2026
Merged

feat(operator): park-record-missing check id + fresh-clone E2E (#356)#362
pbean merged 4 commits into
mainfrom
feat/operator-park-record-validate

Conversation

@pbean

@pbean pbean commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Part 2 of 2 for #356, stacked on #361 (retargets to main when that merges).

What

  • operator.park-record-missing — the validate board-orphan arm gets its own check id, registered in checks.VALIDATE_CHECKS. Before the committed records this state was the fresh-clone norm and shared operator.registry-stale; now the record travels with the park's own commit, so a board at awaiting-operator that no record claims is always evidence of something — a failed record write (journaled as operator-index-failed), a pre-upgrade park, a checkout without the park's branch, or a deleted record. The message names the causes and remedies, and the ids split exactly where the remedy differs (discard a stale entry vs recover the record). _validate_operator_registry's docstring stops claiming the index is machine-local.
  • merge_strategy="ff" parametrization of test_worktree_parked_unit_merges_like_a_done_one — the bmad-loop confirm only works on the machine that parked the story #356 acceptance regression guard: the committed record must never cost a fast-forward merge-back.
  • Worktree fresh-clone E2E (test_a_parked_story_confirms_on_a_fresh_clone): park under worktree isolation → git cloneconfirm --yes in the clone → rc 0, clone board done, confirm commit present with the record's deletion, clean tree.
  • The index → park-entry sweep is finished (review): ten remaining user-facing strings in cli.py still called the committed per-story store "the index" — the confirm refusal, the resume and three part-way-failure messages, two validate warnings, and _apply_confirmation's docstring. They now say "the park entry", the noun a ParkedStory can actually justify (it cannot tell a record from a legacy entry, so "record" would over-claim on a legacy park). Two Unreleased CHANGELOG lines that described the store in the present tense follow. Uncontracted prose throughout — documents.py disclaims both findings[].message and parked[].drift — so no schema bump, and the check ids are untouched.
  • docs/FEATURES.md rewritten to the committed-record contract (the machine-local index paragraph, the resumable-confirmation bullet, the --json commit-provenance wording); CHANGELOG extended with the new check id.

Testing

  • Full suite green (3658 passed / 24 skipped); trunk check clean.
  • Ablation passes (delete the gate, watch its test fail, restore by re-editing):
    • report the orphan arm under operator.registry-stale again → the moved test fails on the id lookup
    • drop operator.park-record-missing from VALIDATE_CHECKS → the registry assert fires ("unregistered check id") and the test fails
    • root _write_park_record at the main project instead of the workspace → both merge-strategy legs fail on the ls-tree assertion (the record sits untracked at the target instead of riding the merge)
    • delete the _write_park_record call → the fresh-clone E2E fails: the clone's confirm refuses with the unknown-key message

Summary by CodeRabbit

  • New Features
    • Added clearer validation warnings when parked story records are missing or no longer match board state.
    • Parked stories can now be confirmed from committed records on a fresh clone, without relying on local run state.
  • Bug Fixes
    • Improved recovery and messaging for interrupted confirmations and drifted park entries.
    • Parked work now merges consistently across supported merge strategies.
  • Documentation
    • Updated command and workflow documentation to reflect committed park records and revised confirmation output.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: acf30da8-d425-4601-8f20-5b6e32bd6df0

📥 Commits

Reviewing files that changed from the base of the PR and between f8080f8 and 76c8da6.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • docs/FEATURES.md
  • src/bmad_loop/checks.py
  • src/bmad_loop/cli.py
  • src/bmad_loop/operatoractions.py
  • tests/test_cli.py
  • tests/test_engine_worktree.py

Walkthrough

The change defines operator.park-record-missing, updates validation and confirmation messaging for committed park records, revises related documentation, and adds coverage for merge modes and fresh-clone confirmation.

Changes

Park-record confirmation

Layer / File(s) Summary
Missing park-record validation
src/bmad_loop/checks.py, src/bmad_loop/cli.py, src/bmad_loop/operatoractions.py, tests/test_cli.py
Validation accepts and emits operator.park-record-missing when an awaiting-operator board entry lacks a matching park record.
Park-entry confirmation recovery
src/bmad_loop/cli.py, docs/FEATURES.md, CHANGELOG.md
Confirmation diagnostics and documentation now describe committed park entries, record-based drift detection, and resumable confirmation.
Fresh-clone and merge-mode coverage
tests/test_engine_worktree.py
Tests cover parked-unit merging with merge and fast-forward strategies and confirmation from a fresh clone.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

Poem

A rabbit checks the park-record trail,
Fresh clones confirm where old paths fail.
The board moves on, the record departs,
Merge and fast-forward play their parts.
“Hop!” says the bun, “the flow is clear!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main changes: a new operator park-record-missing check and fresh-clone E2E coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/operator-park-record-validate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread CHANGELOG.md Outdated
Comment thread docs/FEATURES.md Outdated
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR gives the board-orphan validate arm its own check id (operator.park-record-missing), adds the ff merge-strategy leg to the park-unit merge test, and ships a fresh-clone E2E test that proves the #356 acceptance criterion end-to-end. It also sweeps ten user-facing strings from "index" to "park entry" and updates the docs to the committed-record contract.

  • Check-id split (checks.py, cli.py): the orphan arm is moved from operator.registry-stale to the new operator.park-record-missing; the new message names all four causes and their distinct remedies, and the test asserts the old id is absent from the same finding set.
  • Merge-strategy parametrization (test_engine_worktree.py): existing park-merge test now runs under both "merge" and "ff", where the ff leg is the regression guard that confirms the committed record never breaks a fast-forward merge-back.
  • Fresh-clone E2E (test_a_parked_story_confirms_on_a_fresh_clone): parks under worktree isolation, clones, sets git identity, runs confirm --yes, and asserts rc 0, board done, the record deletion in the commit, and a clean tree.

Confidence Score: 5/5

Safe to merge — all changes are narrowly scoped to diagnostic reporting, terminology, and test coverage, with no changes to the confirmation or park execution paths.

The check-id split is a purely additive diagnostic change (warnings only, never gates a run), the string sweep is consistent throughout the codebase, and both new tests directly exercise the invariants they describe. The ablation notes in the test docstrings confirm the failure modes were verified manually.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
src/bmad_loop/checks.py Adds operator.park-record-missing to VALIDATE_CHECKS; one-line, correctly positioned alongside the other operator checks.
src/bmad_loop/cli.py Splits the board-orphan arm from operator.registry-stale to operator.park-record-missing, updates the _validate_operator_registry docstring to remove the machine-local claim, and sweeps ten user-facing strings from "index" to "park entry". All changes are internally consistent.
src/bmad_loop/operatoractions.py Module docstring updated to enumerate all four validate check IDs; correctly reflects the split at operator.park-record-missing.
tests/test_cli.py Test renamed and assertions updated to verify operator.park-record-missing including severity, message text, detail payload, and absence of operator.registry-stale in the same finding set.
tests/test_engine_worktree.py Parametrizes existing park-merge test with both "merge" and "ff" strategies (ff is the #356 regression guard), and adds test_a_parked_story_confirms_on_a_fresh_clone — a complete E2E test covering the full park → clone → confirm flow.
CHANGELOG.md Adds operator.park-record-missing entry with all four causes (including the "pull it" recovery addressed in d255a73), and refreshes two "index" references to "park entry".
docs/FEATURES.md Rewrites the park-completion and resumability bullets to the committed-record contract, removing the machine-local index language and adding operator.park-record-missing with all four causes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[validate: board story at awaiting-operator] --> B{park record\nexists?}
    B -- yes --> C{story.resumable?}
    C -- yes --> D[operator.confirm-interrupted\nre-run confirm to finish]
    C -- no --> E{drift from\ncommitted truth?}
    E -- yes --> F[operator.registry-stale\ndiscard stale entry]
    E -- no --> G[operator.actions-malformed?\nno readable actions]
    G -- yes --> H[operator.actions-malformed\nrepair spec list]
    G -- no --> I[clean — no warning]
    B -- no --> J[operator.park-record-missing NEW\nfailed write / pre-upgrade park /\nmissing branch pull / deleted record]
Loading

Reviews (5): Last reviewed commit: "docs(operator): list every operator chec..." | Re-trigger Greptile

@pbean
pbean changed the base branch from feat/operator-committed-park-records to main July 28, 2026 22:02
@pbean

pbean commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pbean

pbean commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d255a73ace

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/bmad_loop/cli.py
f"operator-actions index has no entry for them — the index is machine-local "
f"and not committed, so `bmad-loop confirm` cannot complete them from here; "
f"confirm them on the machine that ran the story, or finish them by hand",
"operator.park-record-missing",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bump the schema before replacing the stable check ID

When validate --json encounters an awaiting-operator board entry with no matching park entry, this replaces the previously emitted operator.registry-stale identity while VALIDATE_SCHEMA_VERSION remains 1. Consumers are explicitly instructed to branch on check as the stable identity, so existing schema-v1 automation will silently stop recognizing this condition; publish the new identity behind a schema-version bump or retain a compatible v1 representation.

AGENTS.md reference: AGENTS.md:L30-L30

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining this one — the premise doesn't hold. Worth checking, though, since it names a hard invariant in AGENTS.md.

operator.registry-stale has never shipped, so there is no schema-v1 automation to break.

  • git tag --contains c828475 (the commit that introduced the id) → empty. It is contained in zero tags.
  • git show v0.9.0:src/bmad_loop/checks.py | grep -c operator0. The last release ships no operator.* check id at all.
  • Every operator entry in the CHANGELOG sits under ## [Unreleased], and __version__ is still 0.9.0, the same as the latest tag — nothing has been released since.

The id was born 2026-07-28, seven days after the v0.9.0 tag. The whole awaiting-operator feature (#335, #356) postdates that tag, so no released binary has ever emitted operator.registry-stale.

Three further reasons the suggested remedy would be wrong even setting that aside:

  1. Nothing was renamed or replaced. operator.registry-stale is still registered (checks.py:78) and still emitted (cli.py:693), with its meaning unchanged. This PR factors one of its two arms out into a new id — net effect is one id added and one narrowed, not an identity replaced.

  2. The repo's documented rule points the other way. From an earlier entry, CHANGELOG.md:843-851: "the schema version is deliberately unchanged, since the document contracts each check id, not a given check's outcome." machine.py:7-10 and docs/FEATURES.md:213 scope the version to the envelope and field shape, additive-only.

  3. Precedent is unanimous. Check ids have been added in 7 separate commits, none with a version bump; there have been 0 renames and 0 removals in the repo's history. Twice the project deliberately minted a new id rather than repurpose an existing one — ecc98e3 (operator.confirm-interrupted) and 215cb99 (this one) — under the codified rule "check ids split where the remedy does" (docs/FEATURES.md:67). The only schema bump ever made (ed9f52f, probe 1→2) was a field removal plus addition.

Bumping VALIDATE_SCHEMA_VERSION would advertise a breaking change that did not occur, and would drag the TUI tripwire (tui/widgets.py:341) and its pins (tests/test_cli.py:3841, tests/test_tui_app.py:397) along with it.

The review did surface a real gap, just not this one: operatoractions.py still described validate as carrying only operator.registry-stale and operator.actions-malformed, omitting both operator.confirm-interrupted and the new id. Corrected in 76c8da6.

@pbean

pbean commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@pbean
pbean merged commit aec88f5 into main Jul 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant