Skip to content

test(#6169): suite uses unified Driver for repo allocate/deallocate/finalize - #6172

Closed
fullsend-ai-coder[bot] wants to merge 5 commits into
mainfrom
agent/6169-unified-driver-lifecycle
Closed

test(#6169): suite uses unified Driver for repo allocate/deallocate/finalize#6172
fullsend-ai-coder[bot] wants to merge 5 commits into
mainfrom
agent/6169-unified-driver-lifecycle

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Refactor the behaviour test suite lifecycle so that suite and scenario code speaks only to the unified install.Driver interface (AllocateRepo/DeallocateRepo/Finalize/Capacity) instead of manually composing RepoEnsurer + RepoPool + MintDriver. This is the child of #6135 that consolidates the suite/scenario lifecycle surface.

Changes

  • Rename install.Driverinstall.MintDriver to free the Driver name for the unified repo-allocation interface
  • Add unified install.Driver interface with AllocateRepo, DeallocateRepo, Finalize, Capacity (matching the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 contract)
  • Add install.Factory type for future driver construction
  • Add composedDriver (install.NewComposedDriver) that wraps a MintDriver with an internal slot pool and RepoEnsurer. The constructor calls mint.Install, creates internal pool and ensurer, and returns a unified Driver
  • Replace World.Ensurer with World.RepoDriver (unified Driver)
  • Simplify InitScenario(sc, template) — remove the *RepoPool param. AllocateRepo is now called by the Given the enrolled test repository step (not the Before hook), so scenarios that never allocate a repo don't consume a pool slot
  • afterScenario calls DeallocateRepo instead of pool.Release
  • suite_test.go creates a MintDriver, wraps it with NewComposedDriver, and passes only the unified Driver to the template
  • Concurrency > Capacity is now an advisory warning (not fatal) per the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 contract
  • cfmint.NewDriver and legacy.NewDriver return install.MintDriver
  • Update behaviour-drivers.md and behaviour-testing.md API docs

Testing

  • All pkg/behaviourtest/... tests pass with -race
  • 93-100% coverage on composed.go functions
  • e2e/behaviour/suite_test.go compiles with -tags behaviour
  • go vet ./pkg/behaviourtest/... passes
  • Full go build ./... passes
  • Secret scan passes

Closes #6169

Post-script verification

  • Branch is not main/master (agent/6169-unified-driver-lifecycle)
  • Secret scan passed (gitleaks — fafb2df30a66eb2dfa0ae9e4dff6641bfebe41a4..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

…inalize

Refactor the behaviour test suite lifecycle so that suite and scenario
code speaks only to the unified install.Driver interface
(AllocateRepo/DeallocateRepo/Finalize/Capacity) instead of manually
composing RepoEnsurer + RepoPool + MintDriver.

Key changes:

- Rename install.Driver → install.MintDriver to free the Driver name
  for the unified repo-allocation interface defined in #6135.
- Add install.Factory type and unified install.Driver interface with
  AllocateRepo, DeallocateRepo, Finalize, and Capacity methods.
- Add composedDriver (install.NewComposedDriver) that wraps a
  MintDriver with an internal slot pool and RepoEnsurer. The
  constructor calls mint.Install, creates internal pool and ensurer,
  and returns a unified Driver. Finalize handles both outstanding
  lease reclamation and mint teardown.
- Replace World.Ensurer with World.RepoDriver (unified Driver).
- Simplify InitScenario(sc, template) — remove the *RepoPool param.
  AllocateRepo is now called by the "Given the enrolled test
  repository" step (not the Before hook), so scenarios that never
  allocate a repo do not consume a pool slot.
- afterScenario calls DeallocateRepo (not pool.Release) for cleanup.
- suite_test.go creates a MintDriver, wraps it with
  NewComposedDriver, and passes only the unified Driver to the
  template. Concurrency > Capacity is now an advisory warning (not
  fatal) per the #6135 contract.
- cfmint.NewDriver and legacy.NewDriver return install.MintDriver.
- Update behaviour-drivers.md and behaviour-testing.md API docs.

Note: pre-commit could not run (network restriction in sandbox).
The post-script runs it authoritatively on the runner.

Closes #6169
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 13, 2026 10:11
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 13, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:12 AM UTC · Completed 10:28 AM UTC

Commit: 15c622b · View workflow run →

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

Site preview

Preview: https://06367c3b-site.fullsend-ai.workers.dev

Commit: fdbf82646667b5cef670b2c3b0d90eddc1d4c9c9

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.60920% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/behaviourtest/drivers/install/cfmint/driver.go 6.66% 14 Missing ⚠️
pkg/behaviourtest/drivers/install/composed.go 97.18% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [error-handling] e2e/behaviour/suite_test.go:111 — The guard for GODOG_CONCURRENCY exceeding driver capacity was downgraded from t.Fatalf (hard failure) to t.Logf (advisory warning). While this is intentional per the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 contract (AllocateRepo blocks until a slot frees), excess goroutines will block until the go test timeout fires, producing a confusing timeout failure. The log warning appears early in output and may be missed.
    Remediation: Consider using t.Errorf instead of t.Logf so the misconfiguration is visible in test results as a failure, even though the run continues.

  • [dead-code] pkg/behaviourtest/world/pool.go:9world.RepoPool is no longer used by any consumer after this refactor. All external references (suite_test.go, init.go, init_test.go) were removed in this PR. The type and its tests (pool_test.go) remain exported, potentially confusing external consumers of the library.
    Remediation: Deprecate or remove world.RepoPool and pool_test.go in a follow-up.

  • [naming-consistency] pkg/behaviourtest/suite/init_test.go:88 — The field deallocateRv on fakeDriver uses the Rv (return value) suffix, which is inconsistent with the codebase's established convention for error fields in test fakes. Every other test fake uses <method>Err (e.g., installErr, teardownErr, allocateErr on the same struct).
    Remediation: Rename deallocateRv to deallocateErr for consistency.

  • [naming-consistency] pkg/behaviourtest/drivers/install/driver.go:28 — The interface rename from Driver to MintDriver inverts the typical package convention where Driver is the primary interface name (e.g., scm.Driver, ci.Driver). The current naming gives the simpler name to the new unified interface, which is the primary abstraction callers use — a reasonable design choice.

Previous run

Review

Findings

Low

  • [error-handling] e2e/behaviour/suite_test.go:107 — The guard for GODOG_CONCURRENCY exceeding driver capacity was downgraded from t.Fatalf (hard failure) to t.Logf (advisory warning). While this is intentional per the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 contract (AllocateRepo blocks until a slot frees), excess goroutines will block until the go test timeout fires, producing a confusing timeout failure. The log warning appears early in output and may be missed.
    Remediation: Consider using t.Errorf instead of t.Logf so the misconfiguration is visible in test results as a failure, even though the run continues.

  • [dead-code] pkg/behaviourtest/world/pool.go:9world.RepoPool is no longer used by any consumer after this refactor. All external references (suite_test.go, init.go, init_test.go) were removed in this PR. The type and its tests (pool_test.go) remain exported, potentially confusing external consumers of the library.
    Remediation: Deprecate or remove world.RepoPool and pool_test.go in a follow-up.

  • [naming-consistency] pkg/behaviourtest/drivers/install/driver.go — The interface rename from Driver to MintDriver inverts the typical package convention where Driver is the primary interface name (e.g., scm.Driver, ci.Driver). The current naming gives the simpler name to the new unified interface, which is the primary abstraction callers use — a reasonable design choice, but worth noting for consistency awareness.

Previous run (2)

Review

Findings

Low

  • [error-handling] e2e/behaviour/suite_test.go:107 — The guard for GODOG_CONCURRENCY exceeding pool size was downgraded from t.Fatalf (hard failure) to t.Logf (advisory warning). While this is intentional per the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 contract (AllocateRepo blocks until a slot frees), excess goroutines will block until the go test timeout fires, producing a confusing timeout failure. The log warning appears early in output and may be missed.
    Remediation: Consider using t.Errorf instead of t.Logf so the misconfiguration is visible in test results as a failure, even though the run continues.

  • [test-inadequate] pkg/behaviourtest/drivers/install/composed_test.go:281TestNewComposedDriver_ThreadsMintURL does not verify that the mint URL reaches the internal RepoEnsurer. The test passes e2eCfg by value and only asserts no error, so the mint URL threading path has no meaningful assertion. The production code is correct (the mutation happens inside NewComposedDriver before NewRepoEnsurer is called), but the test provides no regression protection for this path.
    Remediation: Type-assert to *composedDriver and inspect the ensurer's config, or verify via AllocateRepo's returned state.

  • [dead-code] pkg/behaviourtest/world/pool.go:9world.RepoPool is no longer used by any consumer after this refactor. All external references (suite.InitScenario, beforeScenario, afterScenario, suite_test.go) were removed in this PR. The type and its tests remain exported, potentially confusing external consumers of the library.
    Remediation: Deprecate or remove world.RepoPool in a follow-up.

Previous run (3)

Review

Findings

Medium

  • [stale-section] docs/guides/dev/behaviour-testing.md:211 — The "Lazy create+install (RepoEnsurer)" section describes RepoEnsurer and EnsureRepo as public APIs that scenarios use directly. After this PR, RepoEnsurer is internal to the composed install.Driver and scenarios call driver.AllocateRepo instead. The section text "the step calls EnsureRepo(ctx, org, repoName)" is outdated.
    Remediation: Update the section to reflect the unified driver model: rename the header to reference the driver (e.g., "Lazy create+install (unified driver)"), explain that AllocateRepo triggers the internal RepoEnsurer, and update the flow description to match the actual implementation.
Previous run (4)

Review

Findings

Medium

  • [edge-case] pkg/behaviourtest/steps/triage.go:39givenEnrolledTestRepository does not guard against being called twice in the same scenario. Each invocation calls AllocateRepo, leasing a new pool slot, but w.LeasedRepoName is overwritten — the first slot leaks until Finalize catches it. The old code was safe because it checked w.LeasedRepoName != "" before calling EnsureRepo (a cached no-op on re-entry). This is a functional regression that should be addressed before merge.
    Remediation: Add a guard at the top: if w.LeasedRepoName != "", return nil or an error.

  • [stale-reference] docs/guides/dev/e2e-testing.md:81 — References the removed RepoPool API: "Behaviour tests lease numbered ... names from a RepoPool". This has been replaced by the unified install.Driver interface.
    Remediation: Update to reference install.Driver.AllocateRepo.

  • [breaking-api] pkg/behaviourtest/drivers/install/driver.go:15 — Intentional breaking changes to the install package's exported API surface: DriverMintDriver rename, new Driver interface, InitScenario signature change, World.EnsurerWorld.RepoDriver, and return-type changes in cfmint.NewDriver/legacy.NewDriver. All changes are compile-time safe with a migration guide in behaviour-testing.md.
    Remediation: External consumers must follow the migration guide.

Low

  • [dead-code] pkg/behaviourtest/drivers/install/driver.go:25Factory type is declared but never referenced (no callers, implementations, or tests).
    Remediation: Remove until needed, or add a comment referencing the planned use.

  • [error-handling] pkg/behaviourtest/drivers/install/composed.go:140 — In Finalize, when both leak and teardown errors occur, only the leak error is returned; the teardown error is logged but not propagated.
    Remediation: Use errors.Join to combine both errors.

  • [comment-format] e2e/behaviour/suite_test.go:112 — Multi-line advisory warning comment could be consolidated to match the suite's terse inline-comment style.

  • [stale-reference] docs/guides/infrastructure/mint-administration.md:79 — References RepoEnsurer as if externally accessible; now internal to the composed install.Driver.


Labels: PR modifies e2e behaviour test infrastructure and install driver packages


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added component/e2e End-to-end tests component/install CLI install and app setup testing labels Aug 13, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:39 AM UTC · Completed 10:50 AM UTC

Commit: 15c622b · View workflow run →

- Add double-call guard in givenEnrolledTestRepository to prevent slot
  leaks when the step is called twice in the same scenario
- Use errors.Join in Finalize to propagate both leak and teardown errors
- Remove unused Factory type from install package
- Consolidate advisory warning comment in suite_test.go
- Update stale RepoPool/RepoEnsurer references in e2e-testing.md and
  mint-administration.md to reflect unified install.Driver
- Add tests for double-call guard and combined Finalize errors

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed 6 of 7 review findings with code/doc fixes. Disagreed with 1 (intentional breaking API changes — informational finding requiring no action). Key fix: added double-call guard in givenEnrolledTestRepository to prevent slot leaks. Also improved Finalize error propagation with errors.Join, removed dead Factory type, and updated stale doc references.

Fixed (6):

  1. givenEnrolledTestRepository does not guard against double-call, leaking pool slots (pkg/behaviourtest/steps/triage.go): Added early-return guard when w.LeasedRepoName is already set, preventing a second AllocateRepo call from leaking the first slot. Added unit tests for the guard and nil-driver cases.
  2. e2e-testing.md references removed RepoPool API (docs/guides/dev/e2e-testing.md): Updated stale reference from RepoPool/RepoEnsurer to install.Driver.AllocateRepo and composed driver.
  3. Factory type is declared but never referenced (pkg/behaviourtest/drivers/install/driver.go): Removed unused Factory type declaration and its now-unused forge import.
  4. Finalize drops teardown error when both leak and teardown errors occur (pkg/behaviourtest/drivers/install/composed.go): Changed Finalize to use errors.Join to combine both leak and teardown errors. Added tests for the combined-error case and teardown-only error case.
  5. Multi-line advisory warning comment could be consolidated (e2e/behaviour/suite_test.go): Consolidated the 3-line advisory warning comment to a single terse line matching the suite's inline-comment style.
  6. mint-administration.md references RepoEnsurer as externally accessible (docs/guides/infrastructure/mint-administration.md): Updated stale reference from RepoEnsurer to composed install.Driver and AllocateRepo.

Disagreed (1):

  1. Intentional breaking API changes to install package: These are documented intentional changes with a migration guide in behaviour-testing.md. No code action needed — external consumers follow the migration guide.

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 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:51 AM UTC · Completed 11:05 AM UTC

Commit: 5915a65 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 13, 2026 11:05

Superseded by updated review

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

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ❌ Failure · Started 11:07 AM UTC · Completed 11:15 AM UTC

Commit: 5915a65 · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

⚠️ Post-fix script failed — Pre-commit blocked (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Workflow run: https://github.com/fullsend-ai/.fullsend/actions/runs/31694023090

Details:
bandit...............................................(no files to check)Skipped
Detect hardcoded secrets.................................................Passed
shellcheck...........................................(no files to check)Skipped
pinact (SHA-pin check)...............................(no files to check)Skipped
Lint GitHub Actions workflow files...................(no files to check)Skipped
gofmt....................................................................Passed
go vet...................................................................Passed
lint mint embed sync.................................(no files to check)Skipped
lint interface doc sync..................................................Passed
lint ADR statuses....................................(no files to check)Skipped
lint ADR numbers.....................................(no files to check)Skipped
lint ADR frontmatter.................................(no files to check)Skipped
lint broken symlinks.....................................................Passed
lint docs link scope.....................................................Passed
lint markdown links......................................................Failed
- hook id: lint-md-links
- exit code: 2

Issues found in 2 inputs. Find details below.

[docs/guides/dev/e2e-testing.md]:
[ERROR] file:///tmp/agent-fix-3168-1786619240/docs/guides/dev/behaviour-testing.md#lazy-createinstall-repoensurer (at 81:274) | Cannot find fragment

[docs/guides/infrastructure/mint-administration.md]:
[ERROR] file:///tmp/agent-fix-3168-1786619240/docs/guides/dev/behaviour-testing.md#lazy-createinstall-repoensurer (at 79:204) | Cannot find fragment

🔍 45 Total (in 1ms) 🔗 32 Unique ✅ 37 OK 🚫 2 Errors 👻 6 Excluded

lint workflow file size..............................(no files to check)Skipped
lint-staged (web)....................................(no files to check)Skipped

Please check the workflow logs for full details and retry with /fs-fix if appropriate.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix Pre-commit failed on the previous attempt: lint-md-links because e2e-testing.md and mint-administration.md still link to #lazy-createinstall-repoensurer. When updating the "Lazy create+install (RepoEnsurer)" section in behaviour-testing.md, also update those fragment links to match the new heading slug (and keep the section text consistent with AllocateRepo / internal RepoEnsurer).

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 11:17 AM UTC · Completed 11:25 AM UTC

Commit: 5915a65 · View workflow run →

Rename the "Lazy create+install (RepoEnsurer)" heading in
behaviour-testing.md to "Lazy create+install (AllocateRepo)" to reflect the
unified Driver API, and update the fragment links in e2e-testing.md and
mint-administration.md to match the new slug. Fixes lint-md-links
pre-commit failure from the previous iteration.

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Updated the RepoEnsurer heading in behaviour-testing.md to AllocateRepo (matching the unified Driver API) and fixed both stale fragment links in e2e-testing.md and mint-administration.md. This resolves the lint-md-links pre-commit failure from iteration 1.

Fixed (3):

  1. heading slug mismatch: e2e-testing.md and mint-administration.md link to #lazy-createinstall-repoensurer (docs/guides/dev/behaviour-testing.md): Renamed heading from 'Lazy create+install (RepoEnsurer)' to 'Lazy create+install (AllocateRepo)' and updated section body text to describe the unified Driver.AllocateRepo entry point with internal RepoEnsurer delegation
  2. stale fragment link #lazy-createinstall-repoensurer in e2e-testing.md (docs/guides/dev/e2e-testing.md): Updated fragment link from #lazy-createinstall-repoensurer to #lazy-createinstall-allocaterepo to match the renamed heading
  3. stale fragment link #lazy-createinstall-repoensurer in mint-administration.md (docs/guides/infrastructure/mint-administration.md): Updated fragment link from #lazy-createinstall-repoensurer to #lazy-createinstall-allocaterepo to match the renamed heading

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 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:27 AM UTC · Completed 11:40 AM UTC

Commit: 4f705d2 · View workflow run →

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 11:42 AM UTC · Completed 11:51 AM UTC

Commit: 4f705d2 · View workflow run →

Type-assert the returned Driver to *composedDriver and the internal
ensurer to *repoEnsurer, then verify that re.e2eCfg.MintURL equals
the mint URL from the MintDriver install state. This replaces the
previous placeholder comment that acknowledged the by-value limitation
but did not actually verify the URL reached the RepoEnsurer.

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Strengthened TestNewComposedDriver_ThreadsMintURL to type-assert the returned Driver to *composedDriver and inspect the internal *repoEnsurer, verifying that the mint URL from MintDriver install state is correctly threaded to the ensurer's e2eCfg.MintURL field. Left GODOG_CONCURRENCY advisory as t.Logf per #6135 and world.RepoPool removal for a follow-up, as directed.

Fixed (1):

  1. TestNewComposedDriver_ThreadsMintURL lacks assertion that mint URL reaches RepoEnsurer: Type-asserted returned Driver to *composedDriver and its ensurer to *repoEnsurer, then asserted re.e2eCfg.MintURL equals the expected mint URL from the MintDriver install state

Disagreed (2):

  1. GODOG_CONCURRENCY advisory should use a different log level: Per human instruction and test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 contract, the GODOG_CONCURRENCY advisory is correct as t.Logf — no change needed
  2. world.RepoPool should be removed: Per human instruction, world.RepoPool removal is deferred to a follow-up PR

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 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:52 AM UTC · Completed 12:09 PM UTC

Commit: e2cc0d2 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix Restore and use install.Factory so we don't lose the #6135 consolidation path.

Goal

Suite construction should go through a single install.Factory, not ad-hoc MintDriver + NewComposedDriver composition in suite_test.go.

Required changes

  1. Restore install.Factory on the package with the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 shape:
    type Factory func(
        org string,
        client forge.Client,
        token, binary, gcpProjectID string,
        logf func(string, ...any),
    ) (Driver, error)
  2. Provide at least one Factory implementation for the current cfmint path (closure/wrapper is fine) that:
    • builds the mint driver from env/gcpProjectID/PEMs as needed
    • calls NewComposedDriver (or equivalent)
    • returns only Driver (wire Finalize via suite cleanup; do not make the suite thread mint State / ensurer / pool)
  3. Update e2e/behaviour/suite_test.go to take/call that Factory instead of constructing MintDriver + NewComposedDriver itself.
  4. Docs: briefly note in behaviour-testing / behaviour-drivers that suite code selects an install.Factory and does not compose mint + ensurer + pool.
  5. Keep NewComposedDriver as an internal/helper if useful — but suite-facing construction should be Factory.

Do not expand into the full #6170 concrete-driver fold/rename; just land the Factory seam so follow-ups consolidate behind it.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:01 AM UTC · Completed 10:16 AM UTC

Commit: e2cc0d2 · View workflow run →

Add install.Factory type with the #6135 consolidation shape so suite
code selects a Factory and does not compose MintDriver + ensurer + pool
itself. Provide cfmint.NewFactory as the first implementation. Update
suite_test.go to use the Factory instead of direct cfmint.NewDriver +
install.NewComposedDriver composition.

Add install.StateProvider interface so the composed Driver can expose
its install state without threading State through the Factory return
value. Update docs to reflect Factory-based construction.

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 4 (human-triggered)

Restored install.Factory type with the #6135 consolidation shape. Added cfmint.NewFactory as the first implementation. Updated suite_test.go to use Factory-based construction instead of direct MintDriver + NewComposedDriver composition. Added StateProvider interface so the Driver exposes install state without threading it through the Factory return. Updated docs to reflect the new pattern. NewComposedDriver is retained as an internal helper used by Factory implementations.

Fixed (6):

  1. Restore install.Factory type with test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 shape (pkg/behaviourtest/drivers/install/driver.go): Added Factory func type to the install package with the signature (org, client, token, binary, gcpProjectID, logf) (Driver, error). Also added StateProvider interface so Driver implementations can expose suite-level install state without threading State through the Factory return value.
  2. Provide cfmint Factory implementation (pkg/behaviourtest/drivers/install/cfmint/driver.go): Added cfmint.NewFactory(ctx, e2eCfg, poolSize, cfg) that returns an install.Factory closure capturing env config and cfmint parameters. Internally creates MintDriver via NewDriver and wires it through install.NewComposedDriver.
  3. composedDriver should expose install state via StateProvider (pkg/behaviourtest/drivers/install/composed.go): Added InstallState() method on composedDriver and compile-time check for StateProvider interface satisfaction.
  4. Update suite_test.go to use Factory instead of direct MintDriver + NewComposedDriver composition (e2e/behaviour/suite_test.go): Replaced direct cfmint.NewDriver + install.NewComposedDriver composition with cfmint.NewFactory call. Suite now selects a Factory (the test(behaviour): consolidate install driver, RepoEnsurer, and RepoPool behind one driver interface #6135 consolidation seam) and extracts install state via the StateProvider interface.
  5. Update behaviour-drivers and behaviour-testing docs (docs/guides/dev/behaviour-drivers.md): Added Factory and StateProvider to the interfaces table. Updated runner configuration paragraph to describe Factory-based construction.
  6. Update API changes section in behaviour-testing docs (docs/guides/dev/behaviour-testing.md): Updated the API changes section code sample to show Factory-based construction with cfmint.NewFactory and StateProvider pattern instead of direct NewComposedDriver usage.

Tests: passed

Strategy change: Iteration 4 (past escalation threshold): took a fundamentally different approach by introducing the Factory abstraction layer and StateProvider interface, rather than continuing to adjust the direct composition pattern in suite_test.go.

Decision points
  • Chose StateProvider as a separate optional interface rather than adding InstallState() to the Driver interface (alternatives: Add InstallState() to Driver interface, Have Factory return (Driver, State, error); rationale: A separate StateProvider interface avoids changing the Driver contract and requiring all test mocks (fakeDriver, fakeRepoDriver) to add the method. It follows Go's small-interface convention and keeps the Driver interface focused on repo allocation. The composedDriver satisfies StateProvider via type assertion.)

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 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:17 AM UTC · Completed 10:31 AM UTC

Commit: fdbf826 · View workflow run →

t.Fatalf("installing fullsend on %s: %v", org, err)
// Advisory: excess workers block in AllocateRepo (#6135).
if concurrency > driver.Capacity() {
t.Logf("WARNING: GODOG_CONCURRENCY=%d exceeds driver capacity %d; excess workers may block in AllocateRepo", concurrency, driver.Capacity())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] error-handling

The guard for GODOG_CONCURRENCY exceeding driver capacity was downgraded from t.Fatalf (hard failure) to t.Logf (advisory warning). While this is intentional per the #6135 contract (AllocateRepo blocks until a slot frees), excess goroutines will block until the go test timeout fires, producing a confusing timeout failure. The log warning appears early in output and may be missed.

Suggested fix: Consider using t.Errorf instead of t.Logf so the misconfiguration is visible in test results as a failure, even though the run continues.

allocated []string
deallocated []string
allocateErr error
deallocateRv error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] naming-consistency

The field deallocateRv on fakeDriver uses the Rv (return value) suffix, which is inconsistent with the codebase's established convention for error fields in test fakes. Every other test fake uses Err (e.g., installErr, teardownErr, allocateErr on the same struct).

Suggested fix: Rename deallocateRv to deallocateErr for consistency.

//
// Renamed from Driver so that the Driver name can refer to the unified
// repo-allocation interface (#6135).
type MintDriver interface {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] naming-consistency

The interface rename from Driver to MintDriver inverts the typical package convention where Driver is the primary interface name (e.g., scm.Driver, ci.Driver). The current naming gives the simpler name to the new unified interface, which is the primary abstraction callers use — a reasonable design choice.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix Drop StateProvider and suite-level World.Install seeding — there is no suite/world config repo.

Context

  • #6168 / merged #6171 already moved step config access to Org + allocated RepoName + fixed .fullsend/.
  • This branch is behind main and still has old w.Install.ConfigOwner() / ConfigRepo() paths plus the new StateProvider workaround from the Factory change.
  • #6135 Factory returns (Driver, error) only; inventing StateProvider to re-thread suite-level State was the wrong fix.

Required changes

  1. Rebase (or merge) onto latest main so #6171 step changes are present — do not reintroduce ConfigOwner/ConfigRepo indirection in steps.
  2. Remove install.StateProvider and composedDriver.InstallState() (and docs/table mentions).
  3. Stop seeding World.Install from the Factory/suite in e2e/behaviour/suite_test.go. Factory stays (Driver, error); suite only keeps RepoDriver (+ Finalize cleanup).
  4. Scenario Install: keep setting per-scenario state only after allocate (as today: NewPerRepoState(org, repoName, …) in the enrolled-repo step), or prefer using Org/RepoName + constants where #6168 already did. Do not imply a suite-level config repo.
  5. Update behaviour-testing / behaviour-drivers docs to match (Factory → Driver only; no StateProvider).

Keep the install.Factory seam. Do not expand into the full #6170 concrete-driver fold/rename.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ❌ Failure · Started 11:25 AM UTC · Completed 11:37 AM UTC

Commit: fdbf826 · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

⚠️ Post-fix script failed — Secret scan blocked (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Workflow run: https://github.com/fullsend-ai/.fullsend/actions/runs/31944245704

Details:
Secret scan blocked the push. See workflow logs for details.
Please check the workflow logs for full details and retry with /fs-fix if appropriate.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix Retry the previous fix, but rebase onto latest main — do not git merge main.

Why

The last attempt did the right code work (drop StateProvider, stop suite-level World.Install seeding, keep Factory), but post-fix gitleaks scanned ~70 commits after a merge and blocked the push with leaks found: 1. File-scoped scans were clean. Rebase avoids dragging merge history into the push scan window.

Required

  1. git fetch origin main && git rebase origin/main (resolve conflicts if any; do not merge).
  2. Re-apply / finish the same functional changes if the rebase drops them:
  3. Ensure #6171 / #6168 step changes from main are present after rebase.
  4. Push the rebased branch (force-with-lease is expected after rebase).

Do not expand into #6170.

@ifireball

Copy link
Copy Markdown
Member

/fs-retro Investigate why this PR (agent/6169-unified-driver-lifecycle / #6172) was based on such an old commit — from before a blocking sibling (#6168 / merged #6171) finished — so the branch was far behind main and a fix agent had to bring in a huge history (merge then failed secret scan over ~70 commits).

Please look at:

  • When the branch was created / what SHA it forked from relative to #6168/#6171 and #6135 children
  • Whether the coder agent (or dispatch) checked out a stale base, ignored an open blocking sibling, or skipped rebasing onto current main at start
  • Any process/tooling gaps that let a “depends on / parallel-safe but should stay current” BT refactor PR ship from a pre-blocker base
  • Concrete follow-ups so the next agent run starts from current main (or at least post-blocker SHAs) when related issues have merged

Keep findings actionable for fullsend agent/dispatch behavior, not just this one PR’s content.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ❌ Failure · Started 11:46 AM UTC · Completed 11:57 AM UTC

Commit: fdbf826 · View workflow run →

@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 11:47 AM UTC · Completed 12:07 PM UTC

Commit: fdbf826 · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

⚠️ Post-fix script failed — Secret scan blocked (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Workflow run: https://github.com/fullsend-ai/.fullsend/actions/runs/31945204416

Details:
Secret scan blocked the push. See workflow logs for details.
Please check the workflow logs for full details and retry with /fs-fix if appropriate.

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #6172 — Concurrent sibling code agents and stale-base rework chain

What happened

On Aug 13, ifireball created two sibling issues (#6168, #6169) as children of epic #6135, 2 seconds apart. Both were triaged and dispatched to concurrent code agents within 5 seconds (runs 31687582369 and 31687588304), both forking from the same main SHA (fafb2df). The #6168 agent produced PR #6171 (merged same day at 11:51 UTC), while the #6169 agent produced PR #6172 — still open 3 days later with 5+ fix iterations.

Because PR #6172 was built on a pre-#6171 base, it lacked the step config cleanup from #6168, leading to a chain of rework:

  1. Iterations 1–3 (Aug 13): Fix agent addressed review findings (double-call guard, errors.Join, broken fragment links, test strengthening). Two of these succeeded; one failed on lint-md-links because the fix agent renamed a heading without updating cross-file fragment links.
  2. Iteration 4 (Aug 16, 3 days later): Fix agent restored install.Factory per human instruction but also introduced StateProvider — an abstraction that was unnecessary because test(#6168): stop BT steps from using install.State config indirection #6171 had already removed the config indirection it was working around. The agent couldn't know this because it was 108 commits behind main.
  3. Iteration 5 (Aug 16): Human instructed the fix agent to drop StateProvider, merge latest main, and remove stale code. The fix agent used git merge instead of git rebase, pulling ~70 commits into the gitleaks scan window. Gitleaks found a leak in the merged history and blocked the push.
  4. Iteration 6 (Aug 16): Human had to explicitly instruct "rebase onto latest main — do not git merge."

Additionally, the review agent re-raised the same t.Logf vs t.Errorf finding for GODOG_CONCURRENCY 4 times across review iterations, despite the human dismissing it each time with the explanation that #6135 requires advisory-only behavior.

Root causes

  1. Concurrent dispatch without dependency awareness: Both code agents started simultaneously on the same base. The dispatch layer has no concept of issue relationships or sibling sequencing.
  2. No proactive rebase during fix iterations: Over 3 days and 5 fix iterations, the branch was never rebased onto main (where test(#6168): stop BT steps from using install.State config indirection #6171 had long been merged), causing the agent to work with increasingly stale context and introduce unnecessary abstractions.
  3. Fix agent defaulted to merge instead of rebase: When integrating main, the fix agent chose git merge, inflating the post-fix gitleaks scan range to ~70 commits and causing a false-positive block.
  4. Review agent re-raised dismissed findings: The same low-severity finding was raised 4 consecutive times despite explicit human dismissals with rationale.

Evidence for existing open issues

Assessment

No novel systemic gaps were identified beyond what existing open issues already capture. The workflow's rework chain was caused by a known set of gaps: concurrent dispatch without coordination (fullsend#5795), fix agent not rebasing (agents#565), inflated post-fix scan range (agents#318), and review agent re-raising dismissed findings (agents#106, fullsend#4682). Resolving agents#565 alone would have prevented the most expensive part of the rework (iterations 4–6).

@ifireball

Copy link
Copy Markdown
Member

Closing: fix-agent pushes keep failing post-script gitleaks over a large commit window after rebase/merge onto main (file-scoped scans clean; nothing lands). Restarting from a fresh /fs-code on #6169 based on current main, incorporating the review/fix guidance accumulated on this PR.

@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 1:29 PM UTC · Completed 2:17 PM UTC

Commit: fdbf826 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #6172 (closed without merging)

What happened

PR #6172 implemented issue #6169 (unified install.Driver for behaviour test suite lifecycle), a child of epic #6135. The code agent produced a solid initial implementation (+965/-217, 17 files). The review agent caught a real functional regression — a missing double-call guard in givenEnrolledTestRepository that would leak pool slots — along with valid dead-code, error-handling, and test-adequacy findings.

Four human-triggered /fs-fix iterations over Aug 13 successfully addressed all review findings and strengthened tests. The review agent approved three times. However, the branch was forked from the same main SHA as sibling issue #6168, whose PR #6171 merged the same day, advancing main past #6172's base.

By Aug 16, the branch was 108 commits behind main. Two attempts to rebase failed: the post-fix gitleaks scan used a stale PRE_AGENT_HEAD..HEAD range that expanded to include ~70 upstream commits, tripping on a false positive (a fake RSA PEM test fixture on main). The PR was closed and restarted with a fresh /fs-code on current main.

Total cost: 1 code run, 4 review runs, ~8 fix runs, 3 days of human interaction — all discarded.

Root causes and existing issue coverage

1. Gitleaks SCAN_RANGE breaks after rebase (direct cause of closure)post-fix.sh uses PRE_AGENT_HEAD..HEAD for gitleaks. After rebase, PRE_AGENT_HEAD is orphaned, so the range includes all rebased upstream commits. This is the same structural bug that broke the Signed-off-by check on PR #5391.

2. Stale base from concurrent sibling dispatch — Issues #6168 and #6169 were dispatched to concurrent code agents within 5 seconds from the same SHA. #6168 merged first, leaving #6172 behind with overlapping changes. The fix agent later added an unnecessary StateProvider abstraction because it didn't know sibling PR #6171 had already removed the config indirection.

  • Covered by: agents#235 (detect significant base-branch divergence early) and fullsend#4515 (concurrent PR after branching causing violations).

3. Review agent repeated dismissed findings — The GODOG_CONCURRENCY advisory finding was posted 3 times across reviews 2-4 despite the human explicitly dismissing it each time with the same rationale. The MintDriver naming finding appeared 2-3 times similarly. The review agent regenerated each finding with slightly evolving wording, suggesting stateless regeneration rather than copy-paste.

  • Covered by: fullsend#2816 (persistent finding cache), agents#721 (stable finding IDs for cross-round dedup), fullsend#4682 (dismissal-aware suppression), and 5+ additional dedup issues.

Review quality

The review agent's finding quality was strong. The double-call guard regression was a genuine functional bug that could cause pool slot leaks. Dead-code, error-handling, and test-adequacy findings were all valid and addressed. The agent approved appropriately after each fix iteration.

Human interventions were limited to architectural direction (restoring Factory for the #6135 consolidation path, dropping StateProvider per #6171) and workflow problems (rebase), not missed code quality issues. The primary review gap was statelessness across iterations.

No new proposals

All identified root causes are tracked by existing open issues. This retro provides additional supporting evidence — particularly for fullsend#6259 and agents#318, where PR #6172 represents the second documented instance of the SCAN_RANGE bug causing complete PR abandonment.

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 component/install CLI install and app setup ready-for-merge All reviewers approved — ready to merge ready-for-review Agent PR ready for human review testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(behaviour): suite uses one install Driver for repo allocate/deallocate/finalize

1 participant