Skip to content

fix(#6127): dispatch URL-sourced child harnesses that inherit a URL base - #6367

Merged
ifireball merged 4 commits into
mainfrom
agent/6127-base-dispatch-e2e
Aug 19, 2026
Merged

fix(#6127): dispatch URL-sourced child harnesses that inherit a URL base#6367
ifireball merged 4 commits into
mainfrom
agent/6127-base-dispatch-e2e

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Add behaviour (e2e) test coverage for harness dispatch with base: composition, covering three variants: local base + local child, remote base + local child, and both remote. The code fix for dispatch using LoadWithBase was already completed; this PR addresses the missing test coverage gap identified in the triage.

Related Issue

Related to #6127

Changes

  • Add base-dispatch.feature with a consolidated scenario testing all three base-composition variants in a single CI round
  • Add base_dispatch.go with four new Gherkin step definitions:
    • a custom harness "X" with base "Y" and: — local child referencing local base
    • a URL-sourced base harness "X" with: — commits base to hosting repo (not registered as agent)
    • a custom harness "X" with URL base "Y" and: — local child referencing remote URL base
    • a URL-sourced custom harness "X" with URL base "Y" and: — remote child referencing remote URL base
  • Add URLBaseHarnesses field to world.World for cross-step base URL state
  • Add comprehensive unit tests for all new step definitions (18 test cases)
  • Register new steps in registry.go

Testing

  • All 18 new unit tests pass (go test -race)
  • All existing pkg/behaviourtest/steps tests pass (no regressions)
  • go build -tags behaviour ./e2e/behaviour/... compiles cleanly
  • go vet passes
  • Secret scan passes
  • Behaviour test suite runs in CI (e2e)

Closes #6127

Post-script verification

  • Branch is not main/master (agent/6127-base-dispatch-e2e)
  • Secret scan passed (gitleaks — a7ef381e50b0eb77d3fbedecc9ce88b510642dd0..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Add e2e behaviour test coverage for harness dispatch with base:
composition, testing three variants in a single consolidated
scenario:

1. Local base + local child — both harnesses in the enrolled repo
2. Remote base + local child — base in hosting repo, child local
3. Both remote — base and child both in the hosting repo

New Gherkin step definitions in base_dispatch.go:
- "a custom harness X with base Y and:" — local child with local
  base reference
- "a URL-sourced base harness X with:" — commits base to hosting
  repo without registering as agent
- "a custom harness X with URL base Y and:" — local child with
  URL base reference
- "a URL-sourced custom harness X with URL base Y and:" — remote
  child with URL base reference

The URLBaseHarnesses field on World stores base harness URLs so
child steps can reference them by name.

The code fix (dispatch using LoadWithBase) was completed
previously. This adds the missing behaviour test coverage
identified in the triage.

Note: pre-commit could not run (sandbox network restriction).
golangci-lint not available in sandbox. go vet and go test -race
passed.

Related to #6127
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 19, 2026 08:45
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Aug 19, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:47 AM UTC · Completed 9:02 AM UTC

Commit: b4016cc · View workflow run →

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/behaviourtest/steps/base_dispatch.go 88.81% 15 Missing and 2 partials ⚠️
internal/harness/compose.go 62.50% 1 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [stale-reference] internal/resolve/resolve.go:196 — The isCachePath function in resolve.go is a documented intentional duplicate of compose.isFullsendCachePath (comment: "Duplicates compose.isFullsendCachePath intentionally to avoid an import cycle") but was not updated with the same filepath.Abs resolution fix. It still uses filepath.IsAbs(p) as a guard, which will reject relative cache paths. Currently not reachable via the dispatch path (dispatch uses compose, not resolve), but the stale duplicate will silently re-introduce the bug if the resolve path is ever called with a relative WorkspaceRoot.
    Remediation: Apply the same filepath.Abs resolution to isCachePath in resolve.go, matching the updated logic in compose.go's isFullsendCachePath.

  • [scope-authorization-mismatch] internal/harness/compose.go:673 — The PR title uses the test prefix but includes a production bug fix to isFullsendCachePath (replacing the filepath.IsAbs guard with filepath.Abs resolution). Per COMMITS.md, GoReleaser builds release notes from PR titles. When this PR is squash-merged, the test prefix will suppress the fix from release notes. The individual commit uses the correct fix: prefix, but that is lost on squash merge.
    Remediation: Either (a) change the PR title to fix(#6127): ... to ensure the production fix appears in release notes, or (b) split the isFullsendCachePath fix into its own PR with a fix: title and keep this PR as test-only.

Previous run

Review

Findings

Medium

  • [scope-authorization-mismatch] internal/harness/compose.go:673 — The PR title uses the test prefix but includes a production bug fix to isFullsendCachePath (replacing the filepath.IsAbs guard with filepath.Abs resolution). Per COMMITS.md, GoReleaser builds release notes from PR titles. When this PR is squash-merged, the test prefix will suppress the fix from release notes. The individual commit uses the correct fix: prefix, but that is lost on squash merge.
    Remediation: Either (a) change the PR title to fix(#6127): ... to ensure the production fix appears in release notes, or (b) split the isFullsendCachePath fix into its own PR with a fix: title and keep this PR as test-only.

Low

  • [fail-open-analysis] internal/harness/compose.go:667isFullsendCachePath now accepts relative paths via filepath.Abs resolution instead of rejecting them with filepath.IsAbs. The function remains fail-closed: empty inputs and filepath.Abs errors return false, routing those paths to validateBaseRelPath. The filepath.Rel traversal check is preserved. The content-addressed cache (SHA-256 keyed) prevents new-content injection. Security invariant preserved.

Info

  • [scope-authorization-implicit] internal/harness/compose.go:673 — The isFullsendCachePath fix for relative WorkspaceRoot is not explicitly authorized by issue Harness dispatch should use LoadWithBase for configs with base composition #6127, which proposes using LoadWithBase instead of Load. However, the fix addresses a failure mode in the same dispatch code path and was discovered while testing the primary fix. Reasonable scope extension for a closely related bug in the same subsystem.
Previous run (2)

Review

Findings

Medium

  • [state-leak] pkg/behaviourtest/suite/init.go:76resetScenarioWorld does not reset the new URLBaseHarnesses field added to World. Since URLBaseHarnesses is a map (reference type), Clone produces a shallow copy sharing the same underlying map. If scenario A populates it, scenario B on the same cloned World sees stale entries, violating the isolation contract documented in World.Clone’s docstring.
    Remediation: Add w.URLBaseHarnesses = nil to resetScenarioWorld alongside the existing URLHarnessRepoOwner/URLHarnessRepoName resets.

  • [scope-authorization-mismatch] internal/harness/compose.go:655 — The PR title uses the test prefix but the compose.go change is a production bug fix to isFullsendCachePath — it replaces the filepath.IsAbs guard with filepath.Abs resolution, fixing a dispatch failure for relative WorkspaceRoot values. Per COMMITS.md, a production behavior change should use fix. The test prefix means this fix will not appear in release notes.
    Remediation: Change the PR title to fix(#6127): ... or split the compose.go fix into a separate PR with the correct prefix.

Low

  • [path-handling-inconsistency] pkg/behaviourtest/steps/base_dispatch.go:54givenCustomHarnessWithLocalBase and givenCustomHarnessWithURLBase use filepath.Join for building SCM file paths, while givenURLSourcedBaseHarness and url_dispatch.go use path.Join. On Linux the output is identical, but the convention should be aligned.

  • [scope-authorization-mismatch] — The PR body states the LoadWithBase code fix was already completed but does not document the new isFullsendCachePath fix added in this revision.

  • [duplicate-code] pkg/behaviourtest/steps/base_dispatch.go:44givenCustomHarnessWithLocalBase and givenCustomHarnessWithURLBase share ~30 lines of identical config-update boilerplate. Consider extracting a helper, following url_dispatch.go’s delegation pattern.

  • [fail-open-analysis] internal/harness/compose.go:672isFullsendCachePath now accepts relative paths but remains fail-closed: filepath.Abs errors return false, routing paths to validateBaseRelPath. The content-addressed cache (SHA-256 keyed) prevents new-content injection. Security invariant preserved.


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
Previous run (3)

Review

Findings

Low

  • [scope-authorization-mismatch] — The PR body states the code fix for LoadWithBase was already completed but does not reference the specific commit or PR that implemented it. The fix is verified present in internal/harnessdispatch/enumerate.go on main (line 66, referencing ADR-0045). Consider adding a cross-reference for audit trail completeness.

  • [missing-documentation] docs/guides/dev/behaviour-testing.md — The behaviour-testing guide documents other test patterns with dedicated sections (URL-sourced harness scenarios at line 342, fork PR scenarios at line 245) but does not yet document the new base-composed harness dispatch steps. Consider adding a "Base-composed harness scenarios" section covering the four step definitions, the URLBaseHarnesses World field, and the base-dispatch.feature contract.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Aug 19, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix Behaviour tests failed on this PR because of the new scenario, not a flake. Do not drop the remote-child variant.

What failed

CI: https://github.com/fullsend-ai/fullsend/actions/runs/32234152595
Job: E2E Tests / behaviour

18 scenarios passed, 1 failed:

Scenario: Base-composed harnesses dispatch with local and remote base variants
And the harness "remote-child" workflow completes successfully
Error: harness agent "remote-child" did not complete successfully (waited ~15m)

Variants 1 and 2 succeeded:

  • local-child (local base + local child) — dispatched
  • remote-base-child (URL base + local child) — dispatched

Variant 3 never even scheduled a job:

  • remote-child (URL base + URL-sourced child) — no "Harness run (remote-child)" job

The label-triggered dispatch run that did run local-child + remote-base-child:
https://github.com/halfsend-06/test-repo-05/actions/runs/32234441939

Harness dispatch log (exact skip reason):

harness dispatch: skipping agent remote-child: load failed: resolving URL-sourced resources after base composition: base agent: fetching https://raw.githubusercontent.com/halfsend-06/test-repo-05-base-harness-host/main/.fullsend-cache/resources/sha256/629f2b41c984714e70a75aefd77c5e747377ad4feb5d02698600aa1e308283f6/content: fetch: non-200 status code: got 404

Root cause

This is a product bug in LoadWithBase, not a Gherkin/step-definition bug.

Flow for a URL-sourced child whose base: is also a URL (enumerate.go sets SourceURL, then LoadWithBase):

  1. loadBaseChain fetches the URL base and resolveBaseResources rewrites agent to a cache path:
    .fullsend-cache/resources/sha256//content
  2. mergeBaseIntoChild copies that cache path onto the child
  3. Post-merge, because SourceURL is set, resolveBaseResources runs again on the merged child (compose.go ~209)
  4. isFullsendCachePath is supposed to skip already-cached fields, but it returns false for relative paths:
func isFullsendCachePath(p, workspaceRoot string) bool {
	if !filepath.IsAbs(p) || workspaceRoot == "" {
		return false
	}
	...
}

Dispatch sets WorkspaceRoot to filepath.Dir(configDir). In CI that is filepath.Dir(".fullsend") == ".", so CachePath returns a relative path. isFullsendCachePath then treats .fullsend-cache/.../content as a remote-relative resource and concatenates it onto urlParentDirPrefix(SourceURL), producing the 404 URL above.

Existing unit test TestLoadWithBase_SourceURL_WithBase_AlreadyResolvedSkipped uses t.TempDir() (absolute), so it does not catch production dispatch.

What to change

Fix the product code so post-merge SourceURL resolution skips cache paths even when WorkspaceRoot / cache paths are relative (as dispatch uses). Do not weaken the security property that untrusted absolute host paths are still rejected.

Concrete options (pick the smallest correct one):

  • Abs WorkspaceRoot (and thus cache paths) at the LoadWithBase / ListTriggeredHarnesses boundary, OR
  • Make isFullsendCachePath resolve p against workspaceRoot before the IsAbs check, still requiring the result to live under workspaceRoot/.fullsend-cache

Add a unit test that reproduces the CI failure: URL-sourced child + URL base, ComposeOpts.WorkspaceRoot relative (e.g. "."), agent inherited from the base (child YAML has no agent: field). LoadWithBase must succeed and must not try to fetch .fullsend-cache/... from the SourceURL host. Cover the skip in isFullsendCachePath / LoadWithBase, not by changing the behaviour feature.

Keep the remote-child behaviour scenario. It is the regression test we want.

Do not treat this as review-comment cleanup. The review findings are low; this CI failure is the work.

Follow docs/contributing/go-code.md: stage changes, make go-test / go-vet, and verify ~80% patch coverage on new/changed production lines before committing.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 9:39 AM UTC · Completed 9:51 AM UTC

Commit: b4016cc · View workflow run →

When dispatch sets WorkspaceRoot to filepath.Dir(configDir) and configDir
is ".fullsend", WorkspaceRoot becomes "." and CachePath returns relative
paths like ".fullsend-cache/resources/sha256/<hash>/content". The old
isFullsendCachePath checked filepath.IsAbs(p) first, returning false for
these relative cache paths. Post-merge SourceURL resolution then treated
them as unresolved relative resources and tried to fetch them from the
SourceURL host, producing a 404.

Resolve both p and workspaceRoot to absolute paths before comparison so
cache paths are recognised regardless of whether WorkspaceRoot was
absolute or relative. The security property (untrusted absolute host
paths rejected by validateBaseRelPath) is preserved: the callers check
isFullsendCachePath to skip already-resolved values, and non-cache paths
still flow through validateBaseRelPath which rejects absolute paths,
path traversal, and other unsafe patterns.

Add unit test coverage for relative cache paths in TestIsFullsendCachePath
and a regression test (TestLoadWithBase_SourceURL_WithBase_RelativeWorkspaceRoot)
that reproduces the CI failure: URL-sourced child + URL base with
WorkspaceRoot=".", agent inherited from base.

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Fixed isFullsendCachePath to handle relative cache paths by resolving both p and workspaceRoot to absolute before comparison. Added unit tests for relative WorkspaceRoot in isFullsendCachePath and a regression test reproducing the CI failure (URL-sourced child + URL base, WorkspaceRoot="."). All tests pass with -race.

Fixed (3):

  1. isFullsendCachePath rejects relative cache paths when WorkspaceRoot is relative (internal/harness/compose.go): Replaced filepath.IsAbs(p) guard with filepath.Abs resolution of both p and workspaceRoot before comparison. Cache paths are now recognised regardless of whether WorkspaceRoot is absolute or relative, fixing the remote-child dispatch 404.
  2. missing unit test coverage for relative WorkspaceRoot in isFullsendCachePath (internal/harness/compose_test.go): Added two test cases to TestIsFullsendCachePath: relative cache path with relative workspace root (true), and relative non-cache path with relative workspace root (false).
  3. missing regression test for LoadWithBase with relative WorkspaceRoot (internal/harness/compose_test.go): Added TestLoadWithBase_SourceURL_WithBase_RelativeWorkspaceRoot: URL-sourced child + URL base with WorkspaceRoot=".", agent inherited from base. Verifies LoadWithBase succeeds and no .fullsend-cache path is fetched from the SourceURL host.

Tests: passed

Decision points
  • Chose to fix isFullsendCachePath directly rather than making WorkspaceRoot absolute at the ListTriggeredHarnesses boundary (alternatives: Abs WorkspaceRoot at ListTriggeredHarnesses boundary, Make isFullsendCachePath resolve paths to absolute; rationale: Fixing isFullsendCachePath is more targeted and benefits all callers, not just ListTriggeredHarnesses. Any future caller passing a relative WorkspaceRoot is also protected.)

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:53 AM UTC · Completed 10:13 AM UTC

Commit: 42039bd · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot removed the ready-for-merge All reviewers approved — ready to merge label Aug 19, 2026
@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:15 AM UTC · Completed 10:25 AM UTC

Commit: 42039bd · View workflow run →

- Reset URLBaseHarnesses in resetScenarioWorld to prevent state leaking
  between scenarios via Clone's shallow copy (state-leak finding)
- Use path.Join instead of filepath.Join for SCM repo-relative paths,
  aligning with the convention in url_dispatch.go (path-handling finding)
- Extract registerLocalAgentConfig helper to deduplicate config-update
  boilerplate shared by givenCustomHarnessWithLocalBase and
  givenCustomHarnessWithURLBase (duplicate-code finding)

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (bot-triggered)

Addressed 3 of 6 review findings with code fixes (state-leak reset, path.Join alignment, config helper extraction). Disagreed with 3 findings: 2 are PR metadata concerns outside code scope, 1 is a positive security confirmation requiring no action.

Fixed (3):

  1. state-leak: resetScenarioWorld does not reset URLBaseHarnesses (pkg/behaviourtest/suite/init.go): Added w.URLBaseHarnesses = nil to resetScenarioWorld alongside the existing URLHarnessRepoOwner/URLHarnessRepoName resets. Without this, Clone's shallow copy shares the underlying map, violating scenario isolation.
  2. path-handling-inconsistency: filepath.Join used for SCM repo paths (pkg/behaviourtest/steps/base_dispatch.go): Changed filepath.Join to path.Join for SCM file paths in givenCustomHarnessWithLocalBase and givenCustomHarnessWithURLBase. These are repo-relative paths passed to SCM.CommitFile/GetFileContent, not local filesystem paths. Aligns with the convention in url_dispatch.go and givenURLSourcedBaseHarness.
  3. duplicate-code: config-update boilerplate repeated in two functions (pkg/behaviourtest/steps/base_dispatch.go): Extracted registerLocalAgentConfig helper to deduplicate ~30 lines of identical config read/upsert/marshal/commit logic shared by givenCustomHarnessWithLocalBase and givenCustomHarnessWithURLBase. Follows the delegation pattern suggested by the reviewer.

Disagreed (3):

  1. scope-authorization-mismatch: PR title uses test prefix but includes production fix: Valid observation but changing the PR title is a PR metadata concern, not a code fix. The fix agent's scope is limited to code changes. This should be handled by a human reviewer via gh pr edit --title or at merge time.
  2. scope-authorization-mismatch: PR body does not document the isFullsendCachePath fix: Valid observation but the PR body is metadata, not code. Updating the PR description is outside the fix agent's scope. A human reviewer should update the PR body to document the isFullsendCachePath fix added in iteration 1.
  3. fail-open-analysis: isFullsendCachePath remains fail-closed after relative path change: This is a positive finding confirming the security invariant is preserved, not a request for changes. No action needed.

Tests: passed

Decision points
  • Chose to extract registerLocalAgentConfig as a helper in the same file rather than moving it to a shared utility package (alternatives: Shared utility function in a separate file, Inline deduplication via function variable; rationale: The helper is only used by two functions in the same file. Keeping it local follows the existing pattern where url_dispatch.go keeps its helpers co-located. A separate file would over-engineer the abstraction.)

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:27 AM UTC · Completed 10:42 AM UTC

Commit: 8bed8c8 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 19, 2026 10:41

Superseded by updated review

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 19, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix codecov/patch is still failing after the review/fix loop. Do not change product behaviour, drop the remote-child scenario, or retitle the PR. Only add unit tests so patch coverage reaches ~80%.

Current gap

Codecov on HEAD 8bed8c8: patch 64.375% (need 80%, 5% tolerance). 57 lines missing.

File Patch Gap
pkg/behaviourtest/steps/base_dispatch.go 64.47% 35 missing + 19 partials (this is almost all of the deficit)
internal/harness/compose.go 62.50% 1 missing + 2 partials (isFullsendCachePath Abs/Rel branches)

Happy-path tests already exist. Error-return branches in base_dispatch.go are mostly unhit. Mirror the existing tests in url_dispatch_test.go (same fakeURLSCM / selectiveFailSCM / policyFailSCM helpers).

Tests to add in pkg/behaviourtest/steps/base_dispatch_test.go

Copy the patterns from:

  • TestGivenURLSourcedCustomHarness_CommitHarnessError
  • TestGivenURLSourcedCustomHarness_InvalidConfigYAML
  • TestGivenURLSourcedCustomHarness_FileNotAccessibleAfterCommit
  • TestGivenURLSourcedCustomHarness_GetConfigError
  • TestGivenURLSourcedCustomHarness_GetDefaultBranchError
  • TestGivenURLSourcedCustomHarness_RawURLNotAccessible
  • TestGivenURLSourcedCustomHarness_CommitRelativeResourcesError
  • TestGivenURLSourcedCustomHarness_RelativeResourceNotAccessible
  • TestGivenURLSourcedCustomHarness_UpdatesExistingAgent

givenCustomHarnessWithLocalBase / givenCustomHarnessWithURLBase / registerLocalAgentConfig

These now share registerLocalAgentConfig. Cover both the wrappers and the helper:

  1. CommitFile fails on the child harness YAML (commitFileErr) — "committing harness"
  2. commitLocalHarnessResources fails (invalid YAML / resource commit fail) — same as URL harness resource tests
  3. GetFileContent fails reading .fullsend/config.yaml (getFileContentAlways) — "reading config"
  4. Invalid config YAML"parsing config"
  5. CommitFile fails when writing the updated config (first commit succeeds, config commit fails — policyFailSCM or a counter) — "updating config"
  6. Existing agent upsert (found == true): config already has an agent with the same name (different case); assert the source is replaced, not duplicated

Add the same commit/config error cases for givenCustomHarnessWithURLBase if they do not already flow through the helper tests.

givenURLSourcedBaseHarness

  1. CommitFile fails committing harness/<name>.yaml
  2. commitRelativeResources fails (agent file commit)
  3. waitForFileAccessible fails on the harness YAML (getFileContentAlways after commit, or GetFileContent always errors) — "base harness file not accessible"
  4. waitForFileAccessible fails on a relative resource — use selectiveFailSCM with failPath: "agents/triage.md""base harness resource"
  5. GetDefaultBranch error"getting default branch"
  6. verifyRawURLAccessible fails — do not stub rawHTTPClient / force non-200 (see TestGivenURLSourcedCustomHarness_RawURLNotAccessible) — "base harness raw URL not accessible"
  7. GetFileContent fails reading enrolled-repo config when updating allowlist
  8. Invalid config YAML on the enrolled repo
  9. CommitFile fails when writing the allowlist update (prefix not already present)

givenURLSourcedCustomHarnessWithURLBase

Happy path exists. Add CommitFile/config errors only if they are still unique after the helper tests; otherwise skip — this function mostly delegates to givenURLSourcedCustomHarness.

internal/harness/compose.go (isFullsendCachePath)

Uncovered: the two filepath.Abs error return false branches and the compound filepath.Rel result (rel == ".." vs ../ prefix).

Add cases to TestIsFullsendCachePath:

  • p is exactly filepath.Join(workspaceRoot, ".fullsend-cache") (not a file inside it) so rel == ".." is false and rel == "." / parent-dir handling is exercised — expect false (the cache dir itself is not a cached resource file)
  • Keep the existing sibling / .fullsend-cache-evil cases

Do not refactor production code just to test filepath.Abs errors. Those two lines are acceptable leftovers if the base_dispatch.go gap is closed; patch coverage is dominated by that file.

Verification

After adding tests:

go test -race -count=1 ./pkg/behaviourtest/steps/ ./internal/harness/

Then follow docs/contributing/go-code.md / the check-patch-coverage skill: cover profile on changed packages and confirm new/changed production lines in base_dispatch.go are hit. Target ≥80% patch. Stage before lint.

Do not touch the Gherkin feature, isFullsendCachePath logic, or the PR title.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

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

Commit: 8bed8c8 · View workflow run →

Cover uncovered error-return branches in base_dispatch.go and
compose.go to push codecov/patch coverage from ~64% toward 80%.

22 new tests for base_dispatch.go error paths:
- registerLocalAgentConfig: GetFileContent error, invalid config YAML,
  CommitFile error, existing-agent upsert (found == true branch)
- givenCustomHarnessWithLocalBase: CommitFile error, invalid resource
  YAML, GetConfig error, whitespace validation
- givenURLSourcedBaseHarness: CommitFile error, commitRelativeResources
  error, file not accessible, resource not accessible, GetDefaultBranch
  error, raw URL not accessible, GetConfig error, invalid config YAML,
  commit allowlist error
- givenCustomHarnessWithURLBase: CommitFile error, invalid resource
  YAML, GetConfig error, whitespace validation
- givenURLSourcedCustomHarnessWithURLBase: whitespace validation

3 new test cases for isFullsendCachePath in compose_test.go:
- cache directory itself (rel == ".")
- relative cache directory
- workspace root is not a cache path (rel == "..")

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Added 25 new test cases across 2 files to close the codecov/patch coverage gap. 22 tests cover error-return branches in base_dispatch.go (CommitFile errors, config read/parse/commit errors, resource accessibility failures, GetDefaultBranch errors, raw URL failures, whitespace validation, existing-agent upsert). 3 tests cover edge cases in compose.go's isFullsendCachePath. No production code changed.

Fixed (2):

  1. codecov/patch coverage gap in base_dispatch.go (64% vs 80% target): Added 22 error-path unit tests covering all uncovered error-return branches in registerLocalAgentConfig, givenCustomHarnessWithLocalBase, givenURLSourcedBaseHarness, givenCustomHarnessWithURLBase, and givenURLSourcedCustomHarnessWithURLBase
  2. codecov/patch coverage gap in compose.go isFullsendCachePath: Added 3 test cases to TestIsFullsendCachePath covering cache directory itself (rel == "."), relative cache directory, and workspace root (rel == "..") edge cases

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:02 AM UTC · Completed 11:19 AM UTC

Commit: 31ba8af · View workflow run →

@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/harness/compose.go
@ifireball ifireball changed the title test(#6127): add behaviour tests for base-composed harness dispatch fix(#6127): dispatch URL-sourced child harnesses that inherit a URL base Aug 19, 2026
@ifireball
ifireball added this pull request to the merge queue Aug 19, 2026
Merged via the queue into main with commit 953196f Aug 19, 2026
17 checks passed
@ifireball
ifireball deleted the agent/6127-base-dispatch-e2e branch August 19, 2026 12:09
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ❌ Failure · Started 12:12 PM UTC · Completed 12:12 PM UTC

Commit: 31ba8af · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 19, 2026
Merge origin/main to incorporate recent changes including:
- PER_REPO_WIF_REPOS=* for GCP public mint mode (PR #6377)
- Base dispatch e2e tests (PR #6367)
- CEL trigger jira-poll (PR #6340)
- Other main-branch updates

Resolved conflict in docs/cli/mint.md: adopted main's updated
--public flag description (PER_REPO_WIF_REPOS=* replaces
ALLOWED_ORGS=* for GCP) while preserving the new --status-auth,
--status-github-group, and --status-github-client-id flags from
this PR.

Addresses review feedback on #6365
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review Triggers review agent dispatch requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harness dispatch should use LoadWithBase for configs with base composition

1 participant