Skip to content

feat(e2e): expand org pool from 6 to 12#2766

Merged
ralphbean merged 1 commit into
mainfrom
expand-e2e-org-pool
Jun 30, 2026
Merged

feat(e2e): expand org pool from 6 to 12#2766
ralphbean merged 1 commit into
mainfrom
expand-e2e-org-pool

Conversation

@ralphbean

Copy link
Copy Markdown
Member

Summary

  • Adds halfsend-07 through halfsend-12 to the e2e org pool, doubling capacity from 6 to 12 concurrent runs
  • With only 6 orgs, concurrent PR e2e runs were exhausting the pool and timing out after 10 minutes waiting for a lock

Prerequisites before merging

Each new org must be provisioned using the setup script:

export MINT_PROJECT=it-gcp-konflux-dev-fullsend
export MINT_FUNCTION=fullsend-mint

for n in 07 08 09 10 11 12; do
  hack/setup-new-e2e-org.sh "$n"
done

This creates the org, adds botsend as owner, creates test-repo, installs the GitHub Apps, and checks mint enrollment.

Test plan

  • Provision halfsend-07 through halfsend-12 with hack/setup-new-e2e-org.sh
  • Enroll each in the token mint
  • E2E tests pass on this PR

🤖 Generated with Claude Code

@ralphbean ralphbean enabled auto-merge June 29, 2026 20:08
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Expand e2e org pool and fix URL skill dir naming in resolver

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Expand e2e org pool to 12 GitHub orgs to reduce lock starvation.
• Fix URL-skill resolution to preserve the real skill directory name (not "tree").
• Update resolver tests to assert correct directory basenames on cache hit/miss.
Diagram

graph TD
  A["E2E runner"] --> B["acquireOrg()"] --> C["orgPool (12)"] --> D["GitHub test orgs"]
  E["ResolveHarness"] --> F["resolveSkillDirURL"] --> G[("Fetch cache")] --> H["Sandbox upload/logging"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Change cache layout to store dirs under the skill name (no symlink)
  • ➕ Avoids symlink-related edge cases (Windows, restricted filesystems)
  • ➕ Makes cache contents more directly human-readable
  • ➖ Bigger behavioral change to the cache format and any code relying on current layout
  • ➖ Harder to keep backwards compatibility with existing cached entries
2. Manage e2e org capacity via ephemeral orgs or per-run provisioning
  • ➕ Scales capacity elastically with demand
  • ➕ Reduces long-lived org maintenance overhead
  • ➖ Significantly more complex automation and credentials management
  • ➖ Higher risk/flakiness from org/app provisioning steps in CI runtime

Recommendation: The PR’s approach is a good pragmatic fix: doubling the static org pool immediately reduces CI contention with minimal moving parts, and the resolver change uses an idempotent symlink to preserve stable cache semantics while fixing downstream naming/collision issues. If symlink portability becomes a concern, consider the cache-layout alternative later behind a compatibility layer.

Files changed (3) +33 / -1

Bug fix (1) +17 / -0
resolve.goPreserve URL skill directory name via cache-side symlink +17/-0

Preserve URL skill directory name via cache-side symlink

• When a URL-based skill directory is cached under a hash with a cache-internal "tree/" folder, creates (if missing) a sibling symlink named after the URL’s directory basename. Returns the name-preserving path to prevent skills being reported/uploaded as "tree" and colliding across multiple URL skills.

internal/resolve/resolve.go

Tests (1) +10 / -1
resolve_test.goAssert resolved skill directory basenames match URL path +10/-1

Assert resolved skill directory basenames match URL path

• Updates resolver tests to verify that resolved URL skill directories have basenames matching the URL path (e.g., "review", "cached", "one", "two") rather than the cache-internal "tree" directory. Improves coverage for both cache hit/miss and multi-skill scenarios.

internal/resolve/resolve_test.go

Other (1) +6 / -0
testutil.goDouble e2e GitHub org pool from 6 to 12 +6/-0

Double e2e GitHub org pool from 6 to 12

• Extends the static org pool list to include halfsend-07 through halfsend-12. This increases parallel e2e capacity and reduces lock wait timeouts when many PRs run concurrently.

e2e/admin/testutil.go

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown

Site preview

Preview: https://85cbba5b-site.fullsend-ai.workers.dev

Commit: a6c4bd4ad4af930810fef3c2514548936c603821

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 29, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:12 PM UTC · Completed 8:32 PM UTC
Commit: 8189412 · View workflow run →

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 51 rules

Grey Divider


Action required

1. Skill symlink path traversal 🐞 Bug ⛨ Security
Description
resolveSkillDirURL builds a “named” cache path from filepath.Base(forgeInfo.Path) but does not
reject ".."; a URL ending in "/.." will make namedPath resolve to the cache parent and treePath will
be redirected outside the intended cached tree/ directory. Downstream consumers will then
read/upload the wrong directory.
Code

internal/resolve/resolve.go[R374-387]

+	skillName := filepath.Base(forgeInfo.Path)
+	if skillName == "" || skillName == "." {
+		skillName = "tree"
+	}
+	namedPath := filepath.Join(filepath.Dir(treePath), skillName)
+	if namedPath != treePath {
+		// Idempotent: only create if it doesn't already exist.
+		if _, err := os.Lstat(namedPath); os.IsNotExist(err) {
+			if err := os.Symlink("tree", namedPath); err != nil {
+				return Dependency{}, "", fmt.Errorf("creating named symlink for %s: %w", field, err)
+			}
+		}
+		treePath = namedPath
+	}
Relevance

⭐⭐⭐ High

Repo has accepted prior symlink/path traversal hardening; likely to require rejecting “..” basename
escape in resolver.

PR-#1177
PR-#215

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR adds symlink creation based on the URL-derived repo path basename, but forge URL parsing does
not normalize out .. segments; using that basename in filepath.Join can therefore redirect
treePath to a parent directory. The cache layout explicitly places the skill content under a
tree/ subdirectory, so escaping it is incorrect and broadens what downstream code will operate on.

internal/resolve/resolve.go[309-388]
internal/forge/url.go[48-92]
internal/fetch/cache.go[192-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolveSkillDirURL` derives `skillName := filepath.Base(forgeInfo.Path)` and then sets `treePath = filepath.Join(filepath.Dir(treePath), skillName)`. If `skillName` is `".."` (possible because forge URL paths are not normalized), `filepath.Join` will resolve outside the cache entry directory and the resolver returns a path that is not the cached skill tree.

## Issue Context
- Forge URLs are parsed into `forgeInfo.Path` by joining URL path segments; `..` segments are preserved.
- The cache layout stores the skill directory under `<cacheEntry>/tree/`, so returning a parent/sibling path is incorrect and can expand what gets read/uploaded.

## Fix Focus Areas
- internal/resolve/resolve.go[372-388]

### Suggested fix approach
- Treat URL paths as URL paths: compute the directory name safely (e.g., use `path.Base` on a slash-separated path, or explicitly split on `/`).
- Validate the derived name is a safe single directory name:
 - Reject/replace `""`, `"."`, and **`".."`**.
 - Optionally, restrict to a conservative charset (e.g., `[A-Za-z0-9._-]`) and fall back to `"tree"` if it doesn’t match.
- After computing `namedPath`, ensure it remains within `filepath.Dir(treePath)` (e.g., clean it and verify it has the expected prefix) before assigning to `treePath`.
- If validation fails, skip symlink creation and keep `treePath` as the original `.../tree` path.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Symlink creation TOCTOU 🐞 Bug ☼ Reliability
Description
resolveSkillDirURL does an Lstat(not-exist) check before os.Symlink; concurrent resolves of the same
cached skill can race and make os.Symlink return EEXIST, failing resolution even though the symlink
is already present. This makes cache use brittle under concurrency.
Code

internal/resolve/resolve.go[R380-385]

+		// Idempotent: only create if it doesn't already exist.
+		if _, err := os.Lstat(namedPath); os.IsNotExist(err) {
+			if err := os.Symlink("tree", namedPath); err != nil {
+				return Dependency{}, "", fmt.Errorf("creating named symlink for %s: %w", field, err)
+			}
+		}
Relevance

⭐⭐ Medium

No clear precedent on handling os.Symlink EEXIST races; some similar “already exists” race hardening
was rejected.

PR-#2201

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR’s new logic uses a non-atomic existence check before creating the symlink and treats any
symlink creation error as fatal; with shared content-addressed cache paths, EEXIST can happen due to
concurrent creation and should not be fatal.

internal/resolve/resolve.go[372-387]
internal/fetch/cache.go[192-206]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The current `Lstat` → `Symlink` sequence is a TOCTOU race. If two processes/threads resolve the same cache entry, they can both observe `namedPath` as missing and one will fail with `EEXIST` on `os.Symlink`, causing an avoidable resolution error.

## Issue Context
The cache directory is content-addressed (`<hash>/...`), so it is realistic for multiple resolvers to touch the same entry (especially in shared workspaces).

## Fix Focus Areas
- internal/resolve/resolve.go[378-387]

### Suggested fix approach
- Attempt `os.Symlink("tree", namedPath)` directly and:
 - If it succeeds: proceed.
 - If it fails with `os.IsExist(err)`: treat as success (optionally verify it is a symlink pointing at `tree`).
 - For other errors: return an error.
- Alternatively, keep the `Lstat` fast path but still handle `EEXIST` from `Symlink` as success.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Org pool docs outdated 🐞 Bug ⚙ Maintainability
Description
orgPool now includes halfsend-07 through halfsend-12, but ADR 0040 and the e2e testing guide still
describe the pool as halfsend-01 through halfsend-06, which will mislead maintainers provisioning
capacity and debugging CI behavior. The code and docs now disagree about the pool size.
Code

e2e/admin/testutil.go[R58-63]

+	"halfsend-07",
+	"halfsend-08",
+	"halfsend-09",
+	"halfsend-10",
+	"halfsend-11",
+	"halfsend-12",
Relevance

⭐⭐⭐ High

Team previously required ADR 0040 stay in sync with org-pool implementation; docs/code mismatch is
flagged.

PR-#1215

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR expands the orgPool slice to include 07–12, but the referenced ADR and guide still explicitly
state the pool ends at 06, creating conflicting operational guidance.

e2e/admin/testutil.go[49-64]
docs/ADRs/0040-org-pool-for-parallel-e2e-tests.md[34-39]
docs/guides/dev/e2e-testing.md[23-27]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Documentation still states the e2e org pool is `halfsend-01` through `halfsend-06`, but the code now includes `halfsend-07` through `halfsend-12`.

## Issue Context
This PR’s stated goal is to double pool capacity; docs should reflect the operational reality so maintainers don’t provision incorrectly.

## Fix Focus Areas
- docs/ADRs/0040-org-pool-for-parallel-e2e-tests.md[34-40]
- docs/guides/dev/e2e-testing.md[23-27]
- e2e/admin/testutil.go[49-64]

### Suggested fix approach
- Update both docs to say the pool is currently `halfsend-01` through `halfsend-12` (and/or phrase it generically as “currently 12 orgs”).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Hardcoded halfsend-* org names 📘 Rule violation ⛨ Security
Description
The e2e org pool is expanded by adding hardcoded GitHub organization identifiers (halfsend-07halfsend-12) in source. This introduces environment-specific identifiers in code, increasing risk
of leaking real infrastructure naming and making environments harder to rotate or reconfigure
safely.
Code

e2e/admin/testutil.go[R58-63]

+	"halfsend-07",
+	"halfsend-08",
+	"halfsend-09",
+	"halfsend-10",
+	"halfsend-11",
+	"halfsend-12",
Relevance

⭐ Low

Org pool hardcoded halfsend-01..06 previously merged without objection; pattern appears accepted for
e2e infra.

PR-#1215
PR-#1663

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1062040 disallows hardcoded sensitive or environment-specific identifiers in
source. The change adds multiple literal org names (halfsend-07halfsend-12) directly into
orgPool in e2e/admin/testutil.go.

Rule 1062040: Disallow hardcoded secrets and sensitive environment-specific identifiers in source code
e2e/admin/testutil.go[49-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The e2e org pool currently hardcodes real, environment-specific GitHub org identifiers in source code. Compliance requires these identifiers be loaded from environment/configuration (or replaced with clearly fake placeholders), rather than embedded as literals.

## Issue Context
`orgPool` is used to select GitHub orgs for parallel e2e runs. The newly added org names (`halfsend-07` … `halfsend-12`) are environment-specific resource identifiers.

## Fix Focus Areas
- e2e/admin/testutil.go[49-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread internal/resolve/resolve.go Outdated
Comment on lines +374 to +387
skillName := filepath.Base(forgeInfo.Path)
if skillName == "" || skillName == "." {
skillName = "tree"
}
namedPath := filepath.Join(filepath.Dir(treePath), skillName)
if namedPath != treePath {
// Idempotent: only create if it doesn't already exist.
if _, err := os.Lstat(namedPath); os.IsNotExist(err) {
if err := os.Symlink("tree", namedPath); err != nil {
return Dependency{}, "", fmt.Errorf("creating named symlink for %s: %w", field, err)
}
}
treePath = namedPath
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Skill symlink path traversal 🐞 Bug ⛨ Security

resolveSkillDirURL builds a “named” cache path from filepath.Base(forgeInfo.Path) but does not
reject ".."; a URL ending in "/.." will make namedPath resolve to the cache parent and treePath will
be redirected outside the intended cached tree/ directory. Downstream consumers will then
read/upload the wrong directory.
Agent Prompt
## Issue description
`resolveSkillDirURL` derives `skillName := filepath.Base(forgeInfo.Path)` and then sets `treePath = filepath.Join(filepath.Dir(treePath), skillName)`. If `skillName` is `".."` (possible because forge URL paths are not normalized), `filepath.Join` will resolve outside the cache entry directory and the resolver returns a path that is not the cached skill tree.

## Issue Context
- Forge URLs are parsed into `forgeInfo.Path` by joining URL path segments; `..` segments are preserved.
- The cache layout stores the skill directory under `<cacheEntry>/tree/`, so returning a parent/sibling path is incorrect and can expand what gets read/uploaded.

## Fix Focus Areas
- internal/resolve/resolve.go[372-388]

### Suggested fix approach
- Treat URL paths as URL paths: compute the directory name safely (e.g., use `path.Base` on a slash-separated path, or explicitly split on `/`).
- Validate the derived name is a safe single directory name:
  - Reject/replace `""`, `"."`, and **`".."`**.
  - Optionally, restrict to a conservative charset (e.g., `[A-Za-z0-9._-]`) and fall back to `"tree"` if it doesn’t match.
- After computing `namedPath`, ensure it remains within `filepath.Dir(treePath)` (e.g., clean it and verify it has the expected prefix) before assigning to `treePath`.
- If validation fails, skip symlink creation and keep `treePath` as the original `.../tree` path.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review

Findings

High

  • [incorrect-commit-prefix] PR title — PR title uses feat(e2e): which violates COMMITS.md guidance. The e2e org pool expansion is test infrastructure work (increasing parallel test capacity), not a user-facing feature. Per COMMITS.md, feat is reserved for new user-facing functionality that end users would recognize as new capability. Test infrastructure changes belong under chore or test prefixes.
    Remediation: Change PR title to chore(e2e): expand org pool from 6 to 12 or test(e2e): expand org pool from 6 to 12. Since this increases test parallelism capacity (infrastructure maintenance), chore is more appropriate than test.

Labels: PR modifies e2e test org pool infrastructure in e2e/admin/testutil.go.

Previous run

Review

Findings

Medium

  • [race condition] internal/resolve/resolve.go — TOCTOU race in symlink creation: between the os.Lstat(namedPath) check returning os.IsNotExist and the os.Symlink("tree", namedPath) call, another process sharing the same workspace cache could create the same symlink, causing os.Symlink to fail with EEXIST. This turns a correct cache-hit scenario into a resolution error. Additionally, when os.Lstat returns a non-nil error that is not os.IsNotExist, the code skips symlink creation but still sets treePath = namedPath, which may point to a non-existent path.
    Remediation: After the os.Symlink call fails, check whether the error is os.IsExist — if so, the symlink was created by a concurrent process and is safe to use. For the Lstat error path, propagate the error rather than falling through silently.

  • [scope-mismatch-unauthorized-work] internal/resolve/resolve.go — PR contains two unrelated changes without linking to any authorizing issue. The PR title describes only the e2e test infrastructure change, but the diff also includes a production code change adding named symlink creation for skill directories with corresponding test updates.
    Remediation: Split this PR into two separate PRs or update the PR title and description to cover both changes.

  • [incorrect-commit-prefix] PR title uses feat(e2e): which is incorrect per COMMITS.md. The e2e org pool expansion is test infrastructure work, not a user-facing feature. COMMITS.md explicitly states feat is reserved for new user-facing functionality that end users would recognize as new capability.
    Remediation: Change the PR title prefix from feat(e2e): to test(e2e): or chore(e2e):.

Low

  • [path traversal] internal/resolve/resolve.go:372 — The skillName guard checks for empty string and "." but does not check for "..". If forgeInfo.Path ends with a .. segment, filepath.Base would return "..", causing namedPath to escape the cache entry directory. Exploitation is heavily constrained by MatchingAllowedPrefix and forge URL parsing, and the symlink target is hardcoded to "tree", but the fix is trivial defense-in-depth.
    Remediation: Add ".." to the guard condition: if skillName == "" || skillName == "." || skillName == ".." { skillName = "tree" }

  • [test-coverage-gap] internal/resolve/resolve_test.go — The test updates verify happy-path skill path basenames but do not test symlink creation edge cases: existing symlink with different target, regular file at symlink path, os.Symlink failure, or skillName extraction producing empty string or ".".
    Remediation: Add test cases for idempotent symlink creation and edge cases in filepath.Base extraction.

@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 internal/resolve/resolve.go Outdated
fetchedAt = dirEntry.FetchTime
}

// Create a symlink named after the skill directory so downstream consumers

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] scope-mismatch-unauthorized-work

PR contains two unrelated changes without linking to any authorizing issue. The PR title describes only the e2e test infrastructure change, but the diff also includes a production code change adding named symlink creation for skill directories with corresponding test updates.

Suggested fix: Split this PR into two separate PRs or update the PR title and description to cover both changes.

Comment thread internal/resolve/resolve.go Outdated
fetchedAt = dirEntry.FetchTime
}

// Create a symlink named after the skill directory so downstream consumers

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] path traversal

The skillName guard checks for empty string and '.' but does not check for '..'. If forgeInfo.Path ends with a '..' segment, filepath.Base would return '..', causing namedPath to escape the cache entry directory. Exploitation is heavily constrained but the fix is trivial defense-in-depth.

Suggested fix: Add '..' to the guard condition: if skillName == "" || skillName == "." || skillName == ".." { skillName = "tree" }

// directory name from the URL ("review"), not the cache-internal "tree".
info, err := os.Stat(h.Skills[0])
require.NoError(t, err)
assert.True(t, info.IsDir())

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] test-coverage-gap

The test updates verify happy-path skill path basenames but do not test symlink creation edge cases: existing symlink with different target, regular file at symlink path, os.Symlink failure, or skillName extraction producing empty string or '.'.

Suggested fix: Add test cases for idempotent symlink creation and edge cases in filepath.Base extraction.

With 6 orgs, concurrent e2e runs from multiple PRs starve each other
for test orgs, causing cancellations after the 10-minute lock timeout.
Double the pool to 12 orgs to support higher parallelism.

New orgs (halfsend-07 through halfsend-12) must be provisioned before
merging — run hack/setup-new-e2e-org.sh for each.

Signed-off-by: Robin Bender Ginn <rbenderg@redhat.com>
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jun 30, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:56 AM UTC · Completed 12:05 PM UTC
Commit: a6c4bd4 · View workflow run →

@ralphbean ralphbean added this pull request to the merge queue Jun 30, 2026

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

@fullsend-ai-review fullsend-ai-review Bot added the component/e2e End-to-end tests label Jun 30, 2026
Merged via the queue into main with commit 774cffb Jun 30, 2026
19 checks passed
@ralphbean ralphbean deleted the expand-e2e-org-pool branch June 30, 2026 12:12
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jun 30, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 12:16 PM UTC · Completed 12:24 PM UTC
Commit: a6c4bd4 · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #2766feat(e2e): expand org pool from 6 to 12

Timeline

  1. 2026-06-29 20:08 UTCralphbean opens PR bundling two unrelated changes: e2e org pool expansion (e2e/admin/testutil.go) and a skill symlink fix (internal/resolve/resolve.go).
  2. 2026-06-29 20:12–20:32 UTC — First review agent run (commit 8189412). Correctly identifies scope-mismatch, incorrect commit prefix (featchore/test), TOCTOU race, and path traversal. Issues CHANGES_REQUESTED.
  3. Between reviews — Author force-pushes to a6c4bd4, removing the resolve.go changes. PR now contains only the 6-line org pool expansion.
  4. 2026-06-30 09:05 UTC — Human reviewer rh-hemartin approves the cleaned-up PR.
  5. 2026-06-30 11:56–12:05 UTC — Second review agent run on a6c4bd4 (same SHA the human approved). Correctly drops resolved findings. Only remaining finding: incorrect commit prefix (High). Issues CHANGES_REQUESTED again, overriding the human approval.
  6. 2026-06-30 12:12 UTC — PR merged with human override of bot's objection.

What went well

  • Review agent caught a real scope issue — the scope-mismatch finding prompted the author to split out the resolve.go changes, improving PR hygiene.
  • Second review correctly dropped resolved findings — the agent recognized the resolve.go code was gone and only flagged the remaining commit prefix issue.
  • Qodo bot provided complementary coverage — it caught the path traversal issue with more detail than the review agent.

Friction points (all covered by existing issues)

Friction Existing issue
Second review dispatched on a SHA already approved by a human, creating noise #963
CHANGES_REQUESTED for a single style finding on a human-authored PR #2115
Review bot overriding human approval #1922
"Previous run findings" section displayed findings about code no longer in the diff #2116, #1155
Commit prefix classified as High severity, bypassing verdict dampening for human PRs #2105

Conclusion

No new proposals. All identified improvement opportunities are tracked by existing open issues. The highest-impact fix for this class of friction would be implementing #963 (skip review dispatch when the HEAD SHA already has a human approval) — this single change would have prevented the second review run entirely, avoiding the noisy CHANGES_REQUESTED override and saving token cost.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants