Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Runtime selection is shared with production via `defaults.runtime` in org `confi

- Behaviour tests can pass while prompt quality regresses; LLM evals remain necessary for instruction coverage.
- Behaviour orgs are provisioned at suite start with `--runtime dummy`; production orgs must not use dummy unintentionally.
- **Note (2026-07, #5439 / PR #5489):** Numbered behaviour pool repos (`test-repo-NN`) are lazily created and installed on first scenario use via `RepoEnsurer`; suite-start provisioning still applies to the shared admin/`test-repo` install path where used.
- Adding GitLab or Tekton requires new drivers and runner env values, not feature file rewrites.
- Dummy runtime op vocabulary stays minimal; new ops require runtime + docs updates when scenarios need them.
- Behaviour tests depend on live external infrastructure: GitHub API, GitHub Actions runners, GCP WIF/mint, and the shared halfsend org pool. Transient outages, API rate limits, or pool org state corruption can fail the suite; CI distinguishes infrastructure failures from regressions via workflow logs and artifact inspection, but there is no offline fallback.
Expand Down
1 change: 1 addition & 0 deletions docs/guides/dev/behaviour-drivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Behaviour tests isolate forge-specific code behind drivers so Gherkin scenarios
| `ci.Driver` | `pkg/behaviourtest/drivers/ci` | Workflow polling, logs, artifact download |
| `install.Driver` | `pkg/behaviourtest/drivers/install` | Provision and tear down fullsend in the acquired pool org |
| `install.State` | `pkg/behaviourtest/drivers/install` | Post-install config paths (script commits, workflow polling) |
| `install.RepoEnsurer` | `pkg/behaviourtest/drivers/install` | Lazily create and install numbered pool repos on demand; caches by org/repo key; concurrent-safe via singleflight |

v1 reference implementations:

Expand Down
22 changes: 19 additions & 3 deletions docs/guides/dev/behaviour-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ make behaviour-test

In CI, the test runner mints cross-org `e2e` installation tokens via OIDC (same as admin e2e) for GitHub API operations. Triage workflows on the pool org's `test-repo` mint same-org `triage` tokens from vendored reusable workflows; those require per-repo mint enrollment (`PER_REPO_WIF_REPOS`) on the hosted mint project. Pool `test-repo` repos are enrolled once by a GCP admin — not during CI install. The install driver provisions repo-scoped inference WIF via `fullsend inference provision` before `github setup`. See [e2e-testing.md](e2e-testing.md#behaviour-tests-and-per-repo-mint-enrollment).

### Lazy create+install (`RepoEnsurer`)

The `Given the enrolled test repository` step lazily creates and installs numbered pool repos (`test-repo-NN`) on demand via `RepoEnsurer`. When a scenario leases a repo name from the pool and an ensurer is configured, the step calls `EnsureRepo(ctx, org, repoName)` which:

1. Creates the repo if it does not exist (the forge's `auto_init` provides the initial commit).
2. Validates post-install files; if validation fails, runs `fullsend github setup` (and inference provision when configured).
3. Caches results by `org/repo` key so subsequent scenarios reuse the same State.

Concurrent callers for the same repo are serialized via `singleflight.Group` — only one goroutine runs the create+install flow while others wait. This removes the requirement for numbered `test-repo-NN` repos to be pre-provisioned in the pool org.

**Suite duration:** Because each leased `test-repo-NN` pays create + inference provision + `github setup` on first use in a run, serial godog suites take longer than the old shared-`test-repo` model. CI budgets **45 minutes** for the behaviour job (`timeout-minutes` and `go test -timeout`) to match.

Runner env (defaults shown):

```
Expand All @@ -101,18 +113,22 @@ See [behaviour-drivers.md](behaviour-drivers.md) for driver configuration and [A

Fork dispatch scenarios test `pull_request_target` harness triggering from cross-fork pull requests.

### Logical fork name → leased base

Gherkin keeps a stable logical name (for example `"test-repo-fork"`). At runtime, `Given a fork` remaps that name to **`{World.RepoName}-fork`** when the scenario has leased a numbered base (for example leased `test-repo-07` → actual fork repo `test-repo-07-fork`). Feature files should keep using `"test-repo-fork"`; do not hard-code `test-repo-NN-fork` in Gherkin. Full ephemeral fork deletion remains tracked in #5440.

### Pool-org prerequisites

Fork scenarios require the pool org to have:

- **A long-lived fork repository** of the enrolled `test-repo`. The fork is created once (idempotently) via the `Given a fork` step and persists across test runs. Do not delete the fork repo between scenarios or CI runs.
- **Permission to create forks** of the leased enrolled base (`test-repo-NN`) under the same org. The `Given a fork` step creates `{leased}-fork` idempotently when missing.
- **The same installation token** must have write access to both the base repo and the fork repo within the org, since the e2e bot commits to the fork and opens cross-fork PRs.

### Fork lifecycle

| Resource | Lifecycle | Cleanup |
|----------|-----------|---------|
| Fork repo | Long-lived (created once per pool org) | Never deleted |
| Fork repo | Per leased base (`{RepoName}-fork`); created on demand | Not deleted yet (#5440) |
| Fork branches | Per-scenario | Deleted by `CleanupScenario` |
| Fork PRs | Per-scenario | Closed by `CleanupScenario` |

Expand All @@ -128,7 +144,7 @@ Background:
And a fork "test-repo-fork" of the enrolled test repository
```

The `Given a fork` step is idempotent: if the fork already exists, it reuses it without error. Each scenario then creates its own branch and PR within the fork.
The `Given a fork` step remaps the logical name as above and is idempotent for that actual fork repo. Each scenario then creates its own branch and PR within the fork.

## Version pinning for `fullsend-ai/agents`

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/dev/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Prefer **`wrangler versions upload --name=mint-test --preview-alias=…`** so ru

Behaviour tests install fullsend in **per-repo** mode (`fullsend github setup`). Triage workflows mint same-org `triage` tokens from vendored reusable workflows; that requires per-repo mint enrollment (`PER_REPO_WIF_REPOS`). The install driver does **not** run `mint enroll` — pool org behaviour repos must be enrolled once by a GCP admin on the hosted mint project.

Admin e2e uses the singular `halfsend-NN/test-repo` name. Behaviour parallelization is planned to lease `halfsend-NN/test-repo-01` … `test-repo-12` once [#3454](https://github.com/fullsend-ai/fullsend/issues/3454) / [#5439](https://github.com/fullsend-ai/fullsend/issues/5439) land; mint enrollment for those names is pre-provisioned now so it is not on the critical path later. Today the install driver still uses the singular `test-repo` regardless of concurrency. Enroll base names only — do **not** enroll `*-fork` names (forks are ephemeral PR sources and mint against the enrolled base repo). GitHub repositories need not exist yet — enroll is a mint allowlist / WIF-provider update only.
Admin e2e uses the singular `halfsend-NN/test-repo` name. Behaviour tests lease numbered `halfsend-NN/test-repo-01` … `test-repo-12` names from a `RepoPool`; these repos are **lazily created and installed** on demand by `RepoEnsurer` (see [behaviour-testing.md](behaviour-testing.md#lazy-createinstall-repoensurer)). Pre-provisioning numbered repos in the pool org is no longer required — mint enrollment for those names is still pre-provisioned so it is not on the critical path. Enroll base names only — do **not** enroll `*-fork` names (forks are ephemeral PR sources and mint against the enrolled base repo). GitHub repositories need not exist yet — enroll is a mint allowlist / WIF-provider update only.

Inference (`E2E_GCP_PROJECT_ID`) and mint (`it-gcp-konflux-dev-fullsend` for the hosted mint) may be different GCP projects. The behaviour install driver runs `fullsend inference provision <org>/test-repo` using CI credentials on the inference project (same access model as admin e2e), then passes the repo-scoped WIF provider to `github setup`. `E2E_GCP_WIF_PROVIDER` authenticates the CI job itself; it is not written to pool org repos.

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/infrastructure/mint-administration.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Pass this URL as `--mint-url` when running `fullsend github setup`, or set the `

`roles/owner` covers all of the above for users with broad access.

**Behaviour / e2e pool orgs:** Enroll `halfsend-NN/test-repo` (admin e2e; also what the behaviour install driver uses today) and `halfsend-NN/test-repo-01` … `test-repo-12` (pre-provisioned for planned behaviour parallelization in [#3454](https://github.com/fullsend-ai/fullsend/issues/3454) / [#5439](https://github.com/fullsend-ai/fullsend/issues/5439)) on the hosted mint (`PER_REPO_WIF_REPOS`). Run `fullsend mint enroll owner/repo` once per name — not from CI; do not enroll `*-fork` names. See [e2e-testing.md](../dev/e2e-testing.md#behaviour-tests-and-per-repo-mint-enrollment).
**Behaviour / e2e pool orgs:** Enroll `halfsend-NN/test-repo` (admin e2e) and `halfsend-NN/test-repo-01` … `test-repo-12` (lazily created and installed on demand by `RepoEnsurer` — see [behaviour-testing.md](../dev/behaviour-testing.md#lazy-createinstall-repoensurer)) on the hosted mint (`PER_REPO_WIF_REPOS`). Run `fullsend mint enroll owner/repo` once per name — not from CI; do not enroll `*-fork` names. Repos need not exist at enrollment time — enroll is a mint allowlist / WIF-provider update only; `RepoEnsurer` creates the repos when a behaviour scenario first leases them. See [e2e-testing.md](../dev/e2e-testing.md#behaviour-tests-and-per-repo-mint-enrollment).

An administrator can grant all required roles with a single script:

Expand Down
3 changes: 3 additions & 0 deletions e2e/behaviour/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,15 @@ func TestBehaviourSuite(t *testing.T) {
t.Fatalf("creating repo pool: %v", err)
}

ensurer := install.NewRepoEnsurer(e2eCfg, client, token, binary, t.Logf)

testRepo := installState.TestRepo()
template := &world.World{
Config: cfg,
SCM: scmgh.New(client),
CI: gaci.New(client, token),
Install: installState,
Ensurer: ensurer,
Org: org,
Token: token,
Logf: t.Logf,
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.26.0
require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/cucumber/godog v0.14.1
github.com/cucumber/messages/go/v21 v21.0.1
github.com/google/cel-go v0.29.2
github.com/google/uuid v1.6.0
github.com/knights-analytics/hugot v0.7.5
Expand All @@ -27,7 +28,6 @@ require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect
github.com/cucumber/messages/go/v21 v21.0.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
Expand Down Expand Up @@ -82,7 +82,7 @@ require (
github.com/yalue/onnxruntime_go v1.31.0 // indirect
golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect
golang.org/x/image v0.41.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sync v0.20.0
golang.org/x/sys v0.45.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
Expand Down
Loading
Loading