Skip to content

feat(provenance): add guided checkpoint recovery - #31

Open
mrwogu wants to merge 1 commit into
mainfrom
feat/guided-stranded-recovery
Open

mrwogu wants to merge 1 commit into
mainfrom
feat/guided-stranded-recovery

Conversation

@mrwogu

@mrwogu mrwogu commented Sep 16, 2026

Copy link
Copy Markdown
Member

Summary

annotate still fails closed when checkpoint belongs to another base, but failure now explains that attribution is pending and points to git-byline recover.

New recover command is read-only by default. It shows each unrelated checkpoint, object availability, blocking local or remote-tracking branches, and next action. recover --drop refuses reachable bases, rechecks reachability, drops only stranded records, then retries annotation. --json gives same preview and result for tooling.

status now shows pending annotation, unrelated, stranded and blocked counts, plus recommended action. Branch inspection also catches successful Git commands that reported broken refs on stderr, so cleanup stays fail closed.

Scope

  • User-visible behavior: new recover [--drop] [--json] command, better annotate hook failure, richer text and JSON status.
  • Interfaces or compatibility: additive command and status fields. Existing annotate --drop-stranded remains available. No persisted format change.
  • Documentation: README command reference and architecture recovery flow updated.
  • Generated artifacts: none. PromptScript drift validation passes.
  • Security and privacy: destructive cleanup still needs explicit --drop. No automatic evidence deletion, network, telemetry, or new stored content. Git stderr and returned refs are validated before reachability can allow cleanup.

Related issue

Follow-up to #28. #30 tracks branch-scoped checkpoint lanes.

Validation

go test ./internal/gitcmd ./internal/provenance ./internal/app -count=1
  ok github.com/comarch/git-byline/internal/gitcmd
  ok github.com/comarch/git-byline/internal/provenance
  ok github.com/comarch/git-byline/internal/app

go run ./tools/validate
  ok gofmt, vet, test, coverage, build, deps, imports, installers, promptscript, scans
  validate: all 10 stages passed

git diff --check origin/main..HEAD
  clean

Checklist

  • Focused change with unrelated refactors excluded
  • Tests added or updated
  • Documentation updated when behavior changed
  • Generated artifacts regenerated and reviewed
  • PromptScript sources updated instead of generated files
  • No secrets, private data, or unsafe fixtures included
  • Security impact considered
  • Breaking changes documented
  • Release impact understood

Preview unrelated checkpoint reachability before destructive cleanup.
Expose pending attribution through status and actionable hook failures.
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Summary by CodeRabbit

  • New Features

    • Added a recover command to preview unrelated checkpoints, object availability, and branches that still reference them.
    • Added recover --drop to remove only verified stranded checkpoints and retry annotation.
    • Added JSON output for recovery results and expanded status details with pending, blocked, and stranded checkpoint information.
  • Bug Fixes

    • Improved annotation errors with recovery guidance and checkpoint details.
  • Documentation

    • Documented the recovery workflow and retained annotate --drop-stranded for compatibility.

Walkthrough

The pull request adds a recover command that previews unrelated checkpoints and optionally drops unreachable records before retrying annotation. It adds recovery data to status output, strengthens Git reachability helpers, updates annotate errors, and documents the workflow.

Changes

Recovery workflow

Layer / File(s) Summary
Git reachability and command execution primitives
internal/gitcmd/gitcmd.go, internal/gitcmd/gitcmd_test.go
Git helpers report containing branches and object existence, preserve stderr, validate control and whitespace characters in refs, and cover missing objects, remote refs, invalid revisions, and unreadable refs.
Recovery classification and status contracts
internal/provenance/recovery.go, internal/provenance/service.go, internal/provenance/*_test.go
Provenance classifies unrelated, stranded, and blocked checkpoints. It reports object and branch metadata, recommended actions, pending annotation, warnings, and dropped-checkpoint counts.
Recover command and application reporting
internal/app/app.go, internal/app/product.go, internal/app/recover.go, internal/app/*_test.go
The application registers recover, supports preview and JSON output, blocks drops for reachable checkpoints, retries annotation after valid drops, and adds recovery details to annotate errors and status output.
Recovery command documentation
README.md, docs/ARCHITECTURE.md
The documentation describes read-only preview, conditional recover --drop, compatibility support for annotate --drop-stranded, and non-destructive hook-driven annotation.

Priority: ⬇️ Low

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Merge Risk: 🟡 Moderate · up to 1a0f3

Recovery may become excessively slow with many missing bases, while a concurrent branch update could cause checkpoint evidence to be deleted. These issues should be resolved before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 30 functions across 10 files. (2 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: guided checkpoint recovery for provenance.
Description check ✅ Passed The description directly explains the new recover command, recovery behavior, status changes, compatibility, documentation, and validation.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 30 functions across 10 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/guided-stranded-recovery

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.

@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.11290% with 27 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/app/recover.go 85.50% 10 Missing ⚠️
internal/provenance/recovery.go 87.87% 8 Missing ⚠️
internal/app/product.go 88.00% 3 Missing ⚠️
internal/gitcmd/gitcmd.go 93.47% 3 Missing ⚠️
internal/provenance/service.go 90.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

⚠️ Outside the diff (1)

🟠 Major · Make stranded-checkpoint cleanup safe against concurrent ref creation.

internal/provenance/service.go:653-664
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make stranded-checkpoint cleanup safe against concurrent ref creation.

cachedReachable calls repo.AnyBranchContains, which checks refs/heads and refs/remotes with git for-each-ref. dropStrandedCheckpoints then deletes records with DropCheckpointRecords while holding only byline/operation.lock. A concurrent local branch creation or remote-tracking ref update can make the base reachable after the check and before deletion.

Use synchronization shared by supported Git ref updates. If external Git operations cannot participate, quarantine the records and make final deletion recoverable after a final reachability check.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 56578af6-f932-437f-8f69-1be442d9ddb8

📥 Commits

Reviewing files that changed from the base of the PR and between 9225cf0 and 1a0f36b.

📒 Files selected for processing (12)
  • README.md
  • docs/ARCHITECTURE.md
  • internal/app/app.go
  • internal/app/product.go
  • internal/app/recover.go
  • internal/app/recover_test.go
  • internal/gitcmd/gitcmd.go
  • internal/gitcmd/gitcmd_test.go
  • internal/provenance/recovery.go
  • internal/provenance/recovery_test.go
  • internal/provenance/service.go
  • internal/provenance/service_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment on lines +868 to +871
if branches, exists, err := repo.BranchesContaining(first); err != nil || !exists ||
strings.Join(branches, ",") != "refs/heads/main" {
t.Fatalf("BranchesContaining(main commit) = %v, %v, %v", branches, exists, err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Use table-driven Git boundary cases.

The new cases extend one imperative test sequence. Move the branch-containment scenarios into named table cases with isolated setup and assertions.

As per coding guidelines, “Use table-driven tests for engine, model, store, and Git boundaries.”

Source: Coding guidelines

Comment thread internal/gitcmd/gitcmd.go
"--no-dangling",
); fsckErr != nil {
return false, fmt.Errorf("verify object database: %w", fsckErr)
if err := repo.verifyObjectDatabase(); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Bound object-database verification per recovery scan.

Line 617 runs a full git fsck for every distinct missing checkpoint base. previewRecovery calls BranchesContaining once per distinct base, and Status also invokes that preview. Rewritten history with many missing bases can therefore run many complete object scans and make recover and status take up to the cumulative command timeouts.

Verify the object database once per recovery/status snapshot, while preserving a verification failure as a fail-closed error. Add a regression test with multiple distinct missing bases.

As per path instructions, internal/**/*.go must have “bounded resource use.”

Source: Path instructions

Comment thread README.md
| --- | --- |
| `checkpoint <preset>` | Record a human or AI edit snapshot from hook input |
| `annotate [--drop-stranded]` | Replay pending snapshots and annotate `HEAD`; explicitly discard unreachable unrelated checkpoints when recovering |
| `annotate [--drop-stranded]` | Replay pending snapshots and annotate `HEAD`; compatibility recovery flag remains available |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document the destructive behavior of annotate --drop-stranded.

This compatibility path discards stranded checkpoint records before retrying annotation. State that it can remove unreachable unrelated checkpoints and direct users to preview with recover first.

  • README.md#L465-L465: Describe the records discarded by annotate --drop-stranded.
  • docs/ARCHITECTURE.md#L89-L90: Describe the same destructive behavior in the compatibility workflow.
📍 Affects 2 files
  • README.md#L465-L465 (this comment)
  • docs/ARCHITECTURE.md#L89-L90

Comment thread README.md
Comment on lines +488 to +490
remote-tracking branch that reaches that base. Use `recover --drop` only after
the preview marks records as stranded. The command rechecks reachability,
drops only unreachable records, and retries annotation. Hook-driven annotation

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Document the blocked-checkpoint precondition for recover --drop.

The command refuses to drop records when any blocked checkpoint remains. State that all unrelated checkpoints must be stranded before the command can drop records and retry annotation.

  • README.md#L488-L490: State the all-stranded requirement or direct users to resolve blocking branches first.
  • docs/ARCHITECTURE.md#L86-L88: Document that blocked checkpoints prevent dropping and retrying annotation.
📍 Affects 2 files
  • README.md#L488-L490 (this comment)
  • docs/ARCHITECTURE.md#L86-L88

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