Skip to content

ci(#6656): reset pool repo git history on allocation - #6665

Merged
ralphbean merged 3 commits into
mainfrom
agent/6656-reset-pool-repo-history
Aug 27, 2026
Merged

ci(#6656): reset pool repo git history on allocation#6665
ralphbean merged 3 commits into
mainfrom
agent/6656-reset-pool-repo-history

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Reset pool repo git history at the start of each behaviour test suite run to prevent the 12+ minute shallow-clone deepening step that was causing fork PR workflow timeouts.

Changes

  • Add resetRepo method to repoEnsurer that deletes existing pool repos before ensureRepoExists recreates them, giving each suite run a fresh single-commit repo
  • Remove the alreadyInstalled optimization in doEnsure since repos are always freshly created after reset — settle (Actions workflow readiness) now runs unconditionally
  • Add 5 unit tests for resetRepo covering: existing repo deletion, missing repo skip, GetRepo error propagation, DeleteRepo error propagation, and ErrNotFound race idempotency
  • Update 3 existing tests to reflect the new delete-and-recreate behavior

Testing

  • All 73 tests in pkg/behaviourtest/drivers/install pass with -race
  • New resetRepo function has 100% test coverage
  • doEnsure function has 92.9% coverage (above 80% threshold)
  • go vet passes
  • gofmt passes
  • Secret scan passes

Closes #6656

Post-script verification

  • Branch is not main/master (agent/6656-reset-pool-repo-history)
  • Secret scan passed (gitleaks — 2666f7d4d409964a1990678c686ec35e7c98b206..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Pool repos (test-repo-01 through test-repo-12) accumulate git history
across hundreds of behaviour test runs without cleanup, growing to
1.9 GB. The pre-review shallow-clone deepening step takes 12+ minutes
fetching this bloated history, pushing against the dispatch timeout.

Add a resetRepo step at the start of doEnsure that deletes the
existing repo before ensureRepoExists recreates it. This gives each
suite run a fresh single-commit repo. The alreadyInstalled
optimization is removed since repos are always freshly created —
settle (Actions workflow readiness) now runs unconditionally.

Closes #6656
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 26, 2026 20:36
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Aug 26, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 26, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:38 PM UTC · Completed 8:55 PM UTC

Commit: f317cdb · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.79

@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ralphbean

Copy link
Copy Markdown
Member

/fs-fix fix behavior test failures in CI: https://github.com/fullsend-ai/fullsend/actions/runs/33011272114/job/98317906901?pr=6665 I saw a 404 error there. Is there a problem where we delete the repo and do not wait sufficiently long (with expontential retry and backoff) for it to be recreated and settled in the github API?

@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Aug 26, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 26, 2026

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Anchored to prior score of 2. Tier 1 signals unchanged: 2-file change with 0.50 test ratio, no protected/security/CI/dependency risks, trusted bot author. Tier 2 slightly elevated by fix-commit density (8 in 90 days). Linked issue is fresh and narrowly scoped.

Previous run

Risk Assessment: moderate (2/5)

Details

Anchored to prior score of 2. Tier 1 signals unchanged: 2-file change with 0.50 test ratio, no protected/security/CI/dependency risks, trusted bot author. Tier 2 elevated by fix-commit density (5-7 in 90 days). Linked issue is fresh and narrowly scoped.

Previous run (2)

Risk Assessment: moderate (2/5)

Details

Small, well-tested 2-file change to behaviour-test infrastructure by a trusted bot author. No protected paths, security-sensitive files, CI workflows, or dependency changes. Test ratio is ideal at 0.50. Elevated from low to moderate by high recent churn (11 commits in 30 days with 8 fix/iteration commits in 90 days). Linked issue is fresh, well-understood, and fix is narrowly scoped.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [stale-process-description] docs/guides/dev/behaviour-testing.md:225 — The "Repo allocation via unified Driver" numbered list describes the old ensure flow (1. lease, 2. create if missing, 3. validate and conditionally install, 4. cache). The new flow is: 1. lease, 2. reset (delete existing repo and any forks, await deletion propagation), 3. create (await creation propagation), 4. install, 5. validate, 6. settle (always), 7. cache. Steps 2–3 in the doc no longer match the code.
    Remediation: Rewrite the numbered list to reflect the reset-first flow: (1) lease slot, (2) delete existing repo and any derived forks to reset git history, (3) create fresh repo via auto_init, (4) run fullsend github setup, (5) validate post-install, (6) settle (await Actions workflow readiness), (7) cache result.

  • [stale-behavioral-claim] docs/guides/dev/behaviour-testing.md:425 — Lines 425–427 describe the old re-vendor optimization and settle-skip behavior that this PR removes. Repos are now always deleted and recreated fresh; there is no re-vendor path. Settle runs unconditionally.
    Remediation: Replace with text reflecting the new behavior: the ensurer deletes and recreates pool repos on each allocation to clear accumulated git history, then runs a full install and settle.

Low

  • [test-assertion-gap] pkg/behaviourtest/drivers/install/ensure_test.go:270TestEnsurer_DeletesAndRecreatesExistingRepo asserts deleteRepoCalled == 1 but does not assert createRepoCalled == 1. The stub's DeleteRepo sets getRepoErr = ErrNotFound, so CreateRepo IS called and the full delete+recreate cycle IS exercised. An explicit createRepoCalled assertion would improve self-documentation.

  • [stale-process-description] docs/guides/dev/behaviour-testing.md:232 — Line 232 describes the singleflight as serializing "the create+install flow". After this PR the flow is "reset+create+install", omitting the new reset step.

  • [stale-process-description] docs/guides/dev/behaviour-drivers.md:40 — Line 40 says "Lazily creates and installs numbered pool repos on demand". After this PR the ensurer also deletes (resets) existing repos before creating them to clear accumulated git history.

  • [incomplete-lifecycle-description] docs/guides/dev/behaviour-testing.md:308 — The fork lifecycle table says forks are "Deleted by CleanupScenario". After this PR, fork repos are also deleted by resetRepo during source repo allocation to prevent orphaned forks. The table is not wrong but incomplete.

Previous run

Review

Findings

Medium

  • [stale-process-description] docs/guides/dev/behaviour-testing.md:225 — The "Repo allocation via unified Driver" numbered list describes the old ensure flow (create if missing → validate → conditionally install). The new flow is: reset → create → install → validate → settle. Steps 2–3 are now inaccurate.
    Remediation: Rewrite the numbered list to match the new flow: (1) Leases a slot, (2) Resets the repo (deletes if it exists, awaits deletion propagation), (3) Creates the repo (auto_init), (4) Installs fullsend, (5) Validates post-install, (6) Settles for Actions readiness, (7) Caches results.

  • [stale-behavioral-claim] docs/guides/dev/behaviour-testing.md:425 — Lines 425–427 describe the old re-vendor optimization and settle-skip behavior that this PR removes. Repos are now always deleted and recreated fresh; settle runs unconditionally.
    Remediation: Replace with text reflecting the new behavior: repos are always reset (deleted and recreated), so every allocation is a fresh install and settle always runs.

Low

  • [test-assertion-gap] pkg/behaviourtest/drivers/install/ensure_test.goTestEnsurer_DeletesAndRecreatesExistingRepo asserts deleteRepoCalled == 1 but does not assert createRepoCalled == 1. The stub now correctly simulates eventual consistency (DeleteRepo sets getRepoErr = ErrNotFound), so the full delete+recreate cycle IS exercised. An explicit createRepoCalled assertion would improve self-documentation.

  • [edge-case] pkg/behaviourtest/drivers/install/ensure.go — When awaitDeletion exhausts all 5 retry attempts without confirming deletion (returns nil), ensureRepoExists sees the repo as still existing and skips recreation. The flow silently degrades to the old behavior. This is a reasonable degradation-over-failure tradeoff acknowledged in comments.

  • [code-duplication] pkg/behaviourtest/drivers/install/ensure.goawaitDeletion and awaitCreation share similar polling loop structure but differ meaningfully in success condition (NotFound vs nil), timeout behavior (graceful vs strict), and log noun. Consider extracting a shared helper if a third polling function is added.

  • [naming-consistency] pkg/behaviourtest/drivers/install/ensure.go:29resetRetryDelay is a var outside the const block where sibling retry config lives (settleMaxAttempts, settlePoll). This is a standard Go idiom for test overridability.

  • [stale-process-description] docs/guides/dev/behaviour-testing.md:232 — Line 232 describes the singleflight as serializing "the create+install flow". After this PR the flow is "reset+create+install", omitting the new reset step.


Labels: PR modifies behaviour test repo allocation infrastructure under pkg/behaviourtest/

Previous run (2)

Review

Findings

Medium

  • [test-fidelity] pkg/behaviourtest/drivers/install/ensure_test.go:234TestEnsurer_DeletesAndRecreatesExistingRepo only verifies deletion (deleteRepoCalled == 1) but does not verify recreation. The stubClient.GetRepo always returns success regardless of whether DeleteRepo was called, so ensureRepoExists sees the repo as still existing and never calls CreateRepo. The full reset-and-recreate cycle is not exercised end-to-end.
    Remediation: Make the stubClient stateful so DeleteRepo causes subsequent GetRepo calls to return forge.ErrNotFound, then assert both deleteRepoCalled == 1 and createRepoCalled == 1.

  • [stale-process-description] docs/guides/dev/behaviour-testing.md:225 — The "Repo allocation via unified Driver" section describes the old ensure flow (create if missing → validate → conditionally install). The new flow is: reset (delete existing) → create → install → validate → settle. Steps 2–3 in the numbered list are now inaccurate.
    Remediation: Update the numbered list to reflect the new flow: (1) Leases a slot, (2) Deletes existing repo to reset accumulated git history (no-op if missing), (3) Creates the repo, (4) Installs fullsend and validates, (5) Waits for Actions readiness, (6) Caches results.

  • [stale-behavioral-claim] docs/guides/dev/behaviour-testing.md:425 — Lines 425–427 describe the old re-vendor optimization and settle-skip behavior that this PR removes. Repos are now always deleted and recreated fresh, eliminating the re-vendor concept; settle runs unconditionally.
    Remediation: Replace lines 425–427 to reflect that pool repos are deleted and recreated on each allocation, so every install is fresh and settle always runs.

Low

  • [edge-case] pkg/behaviourtest/drivers/install/ensure.go:164resetRepo uses a check-then-act pattern (GetRepo → DeleteRepo). The race where the repo is deleted between check and delete is correctly handled via forge.IsNotFound. However, GitHub's repo deletion is eventually consistent — the repo name may not be immediately available for reuse, potentially causing CreateRepo in ensureRepoExists to fail with a name conflict if called immediately after deletion.

fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 26, 2026
@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 8:56 PM UTC · Completed 9:12 PM UTC

Commit: f317cdb · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.95

…nd create

After resetRepo deletes a pool repo, the GitHub API may still return
the repo due to eventual consistency. Without waiting for propagation,
ensureRepoExists can see the stale repo, skip re-creation, and
subsequent installFullsend operations fail with 404 on the
disappearing repo.

Add awaitDeletion (polls GetRepo until 404) and awaitCreation (polls
GetRepo until success) with exponential backoff (1s, 2s, 4s, 8s)
after delete and create respectively. awaitDeletion is non-fatal if
max attempts are exhausted; awaitCreation fails to prevent operating
on an unavailable repo.

Update stubClient to track delete/create state transitions so tests
reflect realistic API behaviour. Add 10 unit tests covering the new
await functions (immediate confirmation, retry-until-confirmed,
max-attempt exhaustion, error propagation, context cancellation).

Addresses review feedback on #6665
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Added exponential-backoff waits after repo delete and create to handle GitHub API eventual consistency. After resetRepo deletes a pool repo, awaitDeletion polls GetRepo until 404 (up to 5 attempts, ~15s). After ensureRepoExists creates a repo, awaitCreation polls GetRepo until success. This prevents the 404 errors seen in CI where installFullsend operated on a repo that hadn't fully propagated.

Fixed (3):

  1. Missing retry/backoff after repo deletion (pkg/behaviourtest/drivers/install/ensure.go): Added awaitDeletion method that polls GetRepo with exponential backoff (1s, 2s, 4s, 8s) after DeleteRepo until 404 confirms deletion propagated. Called from resetRepo after successful delete. Non-fatal if max attempts exhausted.
  2. Missing retry/backoff after repo creation (pkg/behaviourtest/drivers/install/ensure.go): Added awaitCreation method that polls GetRepo with exponential backoff (1s, 2s, 4s, 8s) after CreateRepo until success confirms repo is available. Called from ensureRepoExists after successful create. Returns error if max attempts exhausted.
  3. stubClient does not simulate delete/create state transitions (pkg/behaviourtest/drivers/install/ensure_test.go): Updated stubClient.DeleteRepo to set getRepoErr=ErrNotFound and stubClient.CreateRepo to set getRepoErr=nil, simulating realistic API eventual consistency. Added countingRepoClient for retry-specific tests. Added 10 new unit tests covering awaitDeletion and awaitCreation.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 26, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 9:14 PM UTC · Ended 9:14 PM UTC

Commit: 95c68f6 · View workflow run →

@ralphbean

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 26, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:16 PM UTC · Completed 9:36 PM UTC

Commit: 95c68f6 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $5.93

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the component/e2e End-to-end tests label Aug 26, 2026
@ralphbean

Copy link
Copy Markdown
Member

/fs-fix The behaviour test failures show that resetRepo needs to clean up fork repos too. When test-repo-01 is deleted and recreated, test-repo-01-fork becomes orphaned. The fork creation step then fails because the fork repo exists but isn't a valid fork of the new source. We should delete repos matching the test-repo-*-fork pattern as part of the reset.

@ralphbean

Copy link
Copy Markdown
Member

fix is failing due to fullsend-ai/agents#1062

@ralphbean

Copy link
Copy Markdown
Member

@ralphbean

Copy link
Copy Markdown
Member

Fix is landing here: fullsend-ai/agents#1063, then we can re-run /fs-fix here as above.

@ralphbean

Copy link
Copy Markdown
Member

/fs-fix The behaviour test failures show that resetRepo needs to clean up fork repos too. When test-repo-01 is deleted and recreated, test-repo-01-fork becomes orphaned. The fork creation step then fails because the fork repo exists but isn't a valid fork of the new source. We should delete repos matching the test-repo-*-fork pattern as part of the reset.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 2:05 AM UTC · Completed 2:17 AM UTC

Commit: 95c68f6 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $2.92

@ralphbean
ralphbean enabled auto-merge August 27, 2026 02:14
resetRepo now deletes {repoName}-fork before deleting the source repo.
When a pool repo is deleted and recreated, any existing fork becomes
orphaned — the fork creation step then fails because the fork repo
exists but isn't a valid fork of the new source. Deleting the fork
first prevents this.

Adds 4 unit tests for fork cleanup: fork deleted before source, fork
missing is skipped, fork delete error propagated, fork delete NotFound
continues to source.

Addresses review feedback on #6665
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Added fork repo cleanup to resetRepo so that {repoName}-fork is deleted before the source repo, preventing orphaned forks that break subsequent fork creation. Includes 4 new unit tests. All 73 tests pass with -race.

Fixed (3):

  1. resetRepo needs to clean up fork repos too: Added fork repo cleanup at the beginning of resetRepo. Before deleting the source repo, the function now checks for {repoName}-fork and deletes it (with awaitDeletion for propagation). This prevents orphaned forks that cause fork creation failures when the source is recreated.
  2. stubClient needs fork-aware GetRepo and DeleteRepo: Updated stubClient to route fork repos (names ending in -fork) through separate forkExists/forkDeleteErr/forkDeleteCalled fields. Existing tests are unaffected because forkExists defaults to false.
  3. unit tests for fork cleanup behavior: Added 4 new tests: TestResetRepo_DeletesForkBeforeSource, TestResetRepo_SkipsForkDeleteWhenForkMissing, TestResetRepo_PropagatesForkDeleteError, TestResetRepo_ForkDeleteNotFound_ContinuesToSource.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 27, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:19 AM UTC · Completed 2:37 AM UTC

Commit: 7d05848 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $5.71

@fullsend-ai-review fullsend-ai-review 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.

See the review comment for full details.

Comment thread pkg/behaviourtest/drivers/install/ensure_test.go
@ralphbean
ralphbean added this pull request to the merge queue Aug 27, 2026
Merged via the queue into main with commit 73ce2b2 Aug 27, 2026
51 checks passed
@ralphbean
ralphbean deleted the agent/6656-reset-pool-repo-history branch August 27, 2026 12:11
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 27, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 12:13 PM UTC · Completed 12:27 PM UTC

Commit: 7d05848 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $5.15

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #6665 — Reset pool repo git history on allocation

Timeline

Issue #6656 was filed as a research task to diagnose a 12+ minute shallow-clone regression in E2E behaviour tests. After a premature first code run (which produced PR #6659, a tactical timeout bump — already covered by the retro on that PR), ralphbean posted the root cause (1.9 GB accumulated git history in pool repos) and manually re-triaged. The second code run produced PR #6665 with a delete-and-recreate approach for pool repos.

The PR went through 2 human-directed fix iterations:

  1. Fix docs: Add agent-compatible code problem document #1 (backoff/retry): ralphbean spotted 404 errors in E2E behaviour tests caused by GitHub API eventual consistency after repo deletion and directed the fix agent via /fs-fix. The review agent had flagged this as a [low] edge case ("GitHub's eventual consistency may cause CreateRepo name conflicts") but didn't escalate.
  2. Fix Add problem areas: Tekton pipeline review, migration path, multi-tenancy #2 (fork cleanup): ralphbean identified that deleting pool repos orphaned their fork repos, causing fork creation failures. The first /fs-fix attempt was blocked by fullsend-ai/agents#1062 (infrastructure bug), requiring a ~30 min delay until the fix at agents#1063 landed.

After both fixes, all 24 E2E behaviour scenarios passed. Two human reviewers approved and the PR was merged ~16.5 hours after issue creation.

Analysis

Review quality: The review agent's findings were useful but operated on a different dimension than the human's. The agent focused on code quality (test fidelity, naming conventions, assertion gaps), while the human caught runtime behavior issues (API eventual consistency delays, fork dependency cascading effects) through E2E test observation. The review agent correctly identified the eventual-consistency risk but rated it [low] — a defensible rating since the code handled IsNotFound, though the actual impact was E2E test failures. The fork orphaning issue required domain knowledge about the test infrastructure that the review agent didn't have.

Rework rate: 3 commits (initial + 2 fixes). Fix #1 (backoff) was arguably avoidable if the code agent had applied resilience patterns similar to the existing settle function in the same file. Fix #2 (fork cleanup) required domain insight about the fork dependency relationship that was not documented in the codebase.

Token cost: Approximately $34 total across all agent runs. The premature first code run ($3.85 + $0.64 triage) is covered by agents#1055.

Evidence for existing issues

What went well

  • The fix agent was responsive and effective in both iterations when given clear human direction ($3.95 and $2.92 respectively).
  • E2E behaviour tests correctly caught both side effects of the pool repo reset (404s and orphaned forks), serving as an effective validation layer.
  • The review agent's test fidelity finding (medium severity) led to a genuinely improved test design with a stateful stub.
  • The re-triage → re-code cycle worked as designed once the human provided root cause context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/e2e End-to-end tests ready-for-review Triggers review agent dispatch requires-manual-review Review requires human judgment risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate fork PR workflow performance regression in behaviour tests

2 participants