Skip to content

test(#8037): save logs and a failure summary for every failed behaviour scenario - #8040

Merged
rh-hemartin merged 3 commits into
mainfrom
agent/8037-save-failed-run-logs
Oct 8, 2026
Merged

rh-hemartin merged 3 commits into
mainfrom
agent/8037-save-failed-run-logs

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Makes every failed behaviour scenario leave an artifact in BEHAVIOUR_ARTIFACT_DIR. That artifact is the logs of the workflow runs in the scenario's leased repository, or a summary saying why the logs could not be collected. Collection runs in the scenario After hook, before the repository is deleted.

Related Issue

Fixes the uncovered failure paths left after 2a3da80 (#7876). Related: #6115 and #7948 (saved logs stay redacted).

Changes

  • Why logs were missing: saveWorkflowRunLogs only ran when a step got a run object back. Nothing was saved when:
    • a wait timed out, since WaitForHarnessAgent / WaitForWorkflow return no run
    • a step failed before resolving a run
    • a step waited without saving logs (drainIssueOpenWorkflow, waitForDispatchRun)
    • GetRunLogs failed, since the error only went to the test log
  • steps.CollectFailureLogs (new, pkg/behaviourtest/steps/debuglogs.go), called from afterScenario for failed, non-skipped scenarios before CleanupScenario and DeallocateRepo:
    • lists the repo's recent runs and saves logs for every run created during the scenario that wasn't already saved
    • always writes debug-scenario-<name>-<suffix>/failure-summary.txt with the scenario error and, per run, where its logs went or why they couldn't be collected (no repo, no CI driver, no runs, list or fetch errors)
    • capped at 10 runs and a 2 minute budget
  • workflow-logs-unavailable.txt: a failed log fetch now writes this file with the reason, where the logs would have gone.
  • ci.RunLister (new, optional interface, ListRecentRuns): implemented by the GitHub Actions and GitLab CI drivers on top of forge.Client.ListRecentWorkflowRuns. It is optional so external ci.Driver implementations keep compiling.
  • World fields: ScenarioName and ScenarioBegin are set by the Before hook. SavedLogRunIDs prevents fetching the same logs twice.
  • Redaction: everything goes through the secret redactor and is written 0600, as behaviour tests: harden credential redaction in saved failure logs and document the failed-run return #7948 requires.
  • Docs: behaviour-testing.md and behaviour-drivers.md are updated.
  • CI workflow unchanged: the upload step already runs if: always(). Its gate on the redact step succeeding is a deliberate no-unredacted-upload rule, so it stays.

Testing

  • make lint passes (stage changes first, then run). In the sandbox, pre-commit could not fetch remote hook repos (TLS error), so the applicable hooks were run directly: gofmt, go vet, lint-interface-doc-sync, lint-docs-links, lychee, lint-broken-symlinks, and pre-commit-hooks v6.0.0. golangci-lint v1.64.8 reports only existing findings in files this PR doesn't touch.
  • Tests added/updated for new or modified logic. go test -race ./pkg/behaviourtest/... passes. New unit tests cover:
    • timeout with no run: the listed run's logs are saved
    • already-saved runs are skipped, and duplicate runs are fetched once
    • fetch errors produce an explanatory note
    • list errors still collect the step-resolved run
    • a driver without RunLister
    • no repo and no CI driver
    • runs from before the scenario are filtered out
    • redaction and 0600 file mode
    • scenarios with the same name get separate summary dirs
    • ListRecentRuns on both drivers
    • the After hook writes the summary before deallocation, and writes nothing for passing or skipped scenarios
  • Not exercised: a live behaviour run against the pool org.

Checklist

  • PR title follows Conventional Commits (correct type, ! for breaking changes)
  • Commits are signed off (DCO) — human and human-directed agent sessions only (autonomous agent commit; exempt)
  • I wrote this contribution myself and can explain all changes in it

🤖 Generated with Claude Code


Closes #8037

Post-script verification

  • Branch is not main/master (agent/8037-save-failed-run-logs)
  • Secret scan passed (gitleaks — 5563259490807cf923e5b634b182ab2cbbafef1a..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner October 3, 2026 14:05
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Oct 3, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:08 PM UTC · Completed 2:25 PM UTC

Commit: 47c4bbe · View workflow run →

Runtime: pi · Model: openai/gpt-6.1-sol → gpt-6.1-sol · Effort: high · Cost: $2.87

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Site preview

Preview: https://df0681f0-site.fullsend-ai.workers.dev

Commit: c5e04524033b6c726feb52a7a3f93b3f72ff0aa0

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

fullsend-ai-review Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Large diff size and elevated churn in behaviour test infrastructure are balanced by zero protected or security-sensitive paths, bot authorship, additive and revertible failure-logging logic, and complete alignment with issue #8037 criteria.

Previous run

Risk Assessment: moderate (2/5)

Details

Large diff size and elevated churn in behaviour test infrastructure are balanced by zero protected or security-sensitive paths, bot authorship, additive and revertible failure-logging logic, and complete alignment with issue #8037 criteria.

Previous run (2)

Risk Assessment: moderate (2/5)

Details

Large diff and active recent churn in behaviour test infrastructure are balanced by zero protected or security-sensitive paths, complete alignment with issue #8037 criteria, and additive, easily revertible failure-logging logic.

Previous run (3)

Risk Assessment: moderate (2/5)

Details

Moderate risk reflecting test framework failure log collection across 16 files with notable churn and coupling in shared packages, balanced by zero protected paths, no CI workflow modifications, a bot author, and strong alignment with linked issue #8037.

Previous run (4)

Risk Assessment: moderate (2/5)

Details

Moderate risk reflecting test framework failure log collection across 16 files with notable churn and coupling in shared packages, balanced by zero protected paths, no CI workflow modifications, a bot author, and strong alignment with linked issue #8037.

Previous run (5)

Risk Assessment: moderate (2/5)

Details

Moderate risk reflecting test framework failure log collection across 16 files with notable churn and coupling in shared packages, balanced by zero protected paths, no CI workflow modifications, a bot author, and strong alignment with linked issue #8037.

Previous run (6)

Risk Assessment: moderate (2/5)

Details

Moderate risk reflecting test framework failure log collection across 16 files with notable churn and coupling in shared packages, balanced by zero protected paths, no CI workflow modifications, a bot author, and strong alignment with linked issue #8037.

Previous run (7)

Risk Assessment: moderate (2/5)

Details

Moderate risk reflecting test framework log collection changes across 12 files with recent churn and coupling, balanced by zero protected paths, no workflow modifications, and alignment with linked issue #8037.

Previous run (8)

Risk Assessment: moderate (2/5)

Details

Moderate risk reflecting test framework log collection changes across 12 files with recent churn and coupling, balanced by zero protected paths, no workflow modifications, and alignment with linked issue #8037.

Previous run (9)

Risk Assessment: moderate (2/5)

Details

Moderate risk score reflecting test framework log collection changes across 12 files with recent churn and coupling, balanced by zero protected paths, no workflow modifications, and complete alignment with linked issue #8037.

Previous run (10)

Risk Assessment: moderate (2/5)

Details

Moderate risk score reflecting test framework log collection changes across 12 files with recent churn and coupling, balanced by zero protected paths, no workflow modifications, and complete alignment with the linked issue.

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Looks good to me

Previous run

Review

Findings

Low

  • [fail-open] pkg/behaviourtest/steps/debuglogs.go:243 — registerSecretForms ignores the boolean returned by security.RegisterRuntimeSecret. The redactor refuses values shorter than 8 bytes, so a short sensitive value (for example in w.Token or a sensitive-suffixed environment variable) is silently left unredacted. It can then appear in failure summaries, unavailable-log notes, collected logs, console diagnostics and name-derived directory names.
    Remediation: Handle a false return explicitly, for example by masking known short sensitive literals locally or suppressing content-bearing artifacts and diagnostics when a non-empty credential cannot be registered. Add a regression test with a short sensitive value.

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 (2)

Review

Findings

Low

  • [edge-case] pkg/behaviourtest/steps/debuglogs.go:142 — SavedLogRunIDs is only ever set to true and never cleared. If a run is saved complete and a later saveWorkflowRunLogs call with the same label gets an in-band job-fetch failure, workflow-logs.txt is overwritten with partial output while the saved flag stays true; CollectFailureLogs then skips the run as already saved, losing the earlier complete snapshot.
    Remediation: Keep the existing complete snapshot when a later fetch is incomplete, or clear the saved flag when replacing it with partial output. Add a regression test for a complete fetch followed by a partial fetch and then failure collection.
  • [data-exposure] pkg/behaviourtest/steps/debuglogs.go:204 — Literal registration for redaction is name-suffix based. Sensitive infrastructure identifiers such as E2E_GCP_PROJECT_ID, E2E_GCP_WIF_PROVIDER, E2E_GCP_SERVICE_ACCOUNT and CLOUDFLARE_ACCOUNT_ID do not match and have no SecretRedactor patterns, so they can survive in failure summaries and name-derived artifact directory names. GitHub secret masking reduces but does not remove the gap.
    Remediation: Register the explicitly sensitive infrastructure environment variables alongside the suffix matches and test their redaction in summaries and directory names.
  • [injection-vuln] pkg/behaviourtest/steps/debuglogs.go:240 — logRedacted masks credentials but passes newlines and control characters through unchanged. Forge errors can carry remote response text; under GitHub Actions a newline in an interpolated error can start a line the runner parses as a workflow command (e.g. ::warning::). Exploitation needs attacker-influenced forge error text in a test-only path.
    Remediation: Run a console-output sanitizer after formatting and redaction that neutralizes newlines, workflow-command delimiters and control characters; add regression tests with malicious error text.
  • [error-handling-idiom] pkg/behaviourtest/steps/debuglogs.go:104 — In writeWorkflowRunLogs, a prepareDebugDir failure is joined with fetchErr and returned before fetchErr is wrapped with the "fetch logs for %s run %d" context. If GetRunLogs returned empty output with no error, the empty-log diagnostic is dropped in that path.
    Remediation: Wrap fetchErr (including the empty-log check) immediately after GetRunLogs and before prepareDebugDir.

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

Medium

  • [logic-error] pkg/behaviourtest/suite/init_test.go:551 — The new TestBeforeScenario_SkipDoesNotAttachWorld uses the tag @skip:per-org, but SkipErrorForTagNames (pkg/behaviourtest/suite/init.go) only handles skip:per-repo, skip:gitlab, playback and requires:capability:*. No case matches skip:per-org, so beforeScenario returns a nil error and attaches a World. Both assertions fail, so the test fails deterministically.
    Remediation: Use a supported skip condition, e.g. InstallMode: "per-repo" with the tag @skip:per-repo (per-org mode is deprecated, ADR 0044), instead of restoring per-org handling.

Low

  • [edge-case] pkg/behaviourtest/steps/debuglogs.go:128 — A successful retry writes workflow-logs.txt without removing an earlier workflow-logs-unavailable.txt in the same directory. A failed first attempt followed by a successful After-hook collection leaves contradictory artifacts (the summary says logs were saved, the note says they could not be collected).
    Remediation: Remove the obsolete unavailable note (ignore not-exist errors) after a successful write, and add a regression test for failed-then-successful collection into the same directory.
  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:265 — The scenario error is redacted only after its producer has formatted it. An escaped/quoted form of a registered credential (e.g. an error built with %q around a credential containing quotes or backslashes) will not match exact-literal or default patterns, and can reach failure-summary.txt and workflow-logs-unavailable.txt. Typical alphanumeric tokens are unaffected, so this is defense-in-depth for behaviour tests: harden credential redaction in saved failure logs and document the failed-run return #7948.
    Remediation: Make the shared redaction boundary also match escaped forms of registered credentials, and add tests with quoted credentials in scenario errors, list errors and GetRunLogs errors.
  • [data-exposure] pkg/behaviourtest/steps/debuglogs.go:447 — os.WriteFile applies 0600 only on creation; a pre-existing more permissive workflow-logs*.txt keeps its mode when overwritten, and collector directories are created with MkdirAll(..., 0755). The pattern predates this PR and the artifact directory is on an ephemeral CI runner, so impact is limited.
    Remediation: Write via a new 0600 temp file and rename (or chmod explicitly), create collector-owned directories with 0700, and test pre-existing permissive paths.
  • [injection-vuln] pkg/behaviourtest/steps/debuglogs.go:447 — The artifact writer follows pre-existing symlinks (destination files and a symlinked debug directory accepted by MkdirAll). Slugging in this PR prevents lexical traversal, and exploiting this needs prior write access to the artifact directory, so this is hardening only.
    Remediation: Reject symlinks in destination paths and directory components (Lstat / O_NOFOLLOW) and add tests for a symlinked directory and a symlinked destination file.
  • [error-handling-idiom] pkg/behaviourtest/steps/debuglogs.go:104 — writeWorkflowRunLogs wraps the prepareDebugDir error with "create debug dir: %w", but prepareDebugDir already returns "creating debug dir: %w", producing a stuttering message.
    Remediation: Pass err directly to errors.Join(fetchErr, err) without the extra wrapper.

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 (4)

Looks good to me

Previous run (5)

Review

Findings

Medium

  • [logic-error] pkg/behaviourtest/steps/debuglogs.go:163 — Empty logs are classified as complete for a completed run. Both forge clients can return an empty string with no error when no jobs are listed. The collector then writes an empty workflow-logs.txt, marks the run as saved, and reports successful collection without explaining why logs are absent. This misses the explicit unavailable-log explanation required by Behaviour tests: always save logs of failed runs, including runs that currently produce none #8037.
    Remediation: Handle empty log responses in writeWorkflowRunLogs by writing an explanatory unavailable-log artifact and summary outcome instead of marking them complete. Add a regression test for a completed failed run whose driver returns empty logs without an error.

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 (6)

Review

Findings

Medium

  • [logic-error] pkg/behaviourtest/steps/debuglogs.go:307 — The collector appends w.WorkflowRun before the recent listing, then skips duplicate IDs without refreshing their metadata. If the step-resolved snapshot is in_progress but the listing reports the same run as completed/failure, the summary still reports it as running and classifies complete fetched logs as incomplete. This can occur when playback's WaitForHarnessAgentRound returns after the agent job finishes but before the enclosing workflow finishes. The refetch regression test does not cover this because it leaves w.WorkflowRun unset.
    Remediation: Merge runs by ID before fetching, preferring the recent listing's metadata and retaining the step-resolved run as a fallback when listing fails or omits it. Add a regression test with an in_progress step-resolved run and a completed/failure listed counterpart, checking the reported status, conclusion, and completeness classification.

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 (7)

Review

Findings

Medium

  • [edge-case] pkg/behaviourtest/suite/init.go:83 — Non-skip Before-hook errors still leave no failure artifact. Tag validation happens before attaching a World, so a malformed @requires:capability: tag fails the scenario, but the After hook returns at its nil-World guard without collecting a summary.
    Remediation: Attach a reset, named scenario World before tag validation, or provide fallback context for failure summaries. Add a Before-to-After regression test for malformed capability tags.

  • [logic-error] pkg/behaviourtest/steps/debuglogs.go:158 — runLogsComplete treats terminal runs without recognized error markers as complete, but the forge producers silently omit logs: GitHub fetches only the default jobs page and limits each job to 1 MiB; GitLab limits individual traces to 10 MiB. These snapshots can enter SavedLogRunIDs, causing failure collection to skip them and report already-saved logs without explaining missing jobs or log tails.
    Remediation: Expose pagination and size-limit truncation through metadata or recognized markers; do not classify truncated snapshots as complete, and explain remaining truncation in the artifact. Add producer-response regression tests.

  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:101 — In-scenario collection passes label unchanged to prepareDebugDir, which embeds it directly in the directory name. Callers supply feature-provided agent strings, so a registered credential in a label remains in uploaded filenames. Failure-hook names use pathSlug, but this entry point does not.
    Remediation: Redact and constrain labels at the shared directory-creation boundary. Add a saveWorkflowRunLogs test with World.Token and a credential environment value in the label, checking generated paths.

  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:348 — Run names, statuses and conclusions are formatted with %q before redaction. An opaque registered credential containing quotes or backslashes changes representation and can evade exact-string matching, leaving a recoverable escaped credential in summary or unavailable-log artifacts. Scenario names have the same ordering problem in %q console diagnostics.
    Remediation: Redact individual fields before quoting, while retaining the final whole-message scan. Add tests using registered credentials containing quotes or backslashes.


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 (8)

Review

Findings

Medium

  • [edge-case] pkg/behaviourtest/suite/init.go:94 — Log collection checks only the incoming scenario error. When scenario steps pass but deferred DeallocateRepo fails, the hook returns a failure without writing a failure summary. TestAfterScenario_DoubleDeallocateSurfacesError covers the error return but does not assert an artifact.
    Remediation: Write a redacted explanatory artifact when deallocation introduces a failure, while retaining workflow collection before teardown. Add a test for this path.

  • [logic-error] pkg/behaviourtest/steps/debuglogs.go:241 — Only the newest ten runs are listed, with no truncation notice in the summary. An unresolved failed run behind ten newer runs therefore receives neither logs nor an explanation before repository teardown. Already-saved runs also consume the listing limit.
    Remediation: Report possible listing truncation explicitly. Consider a larger bounded listing with the fetch cap applied to unsaved runs, and test an older unsaved failure beyond the first ten results.

  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:155 — The shared redaction helper does not supply known credential literals from World.Token or sensitive runner environment values. An opaque credential without a recognized prefix or assignment/header can survive in the new failure summaries, diagnostics, and name-derived artifact directories. The downstream artifact-redaction script masks selected literals in file contents, but does not rename directories; external consumers may not invoke it.
    Remediation: Supply behaviour-runner credential literals to redaction before formatting transformations and path slugging. Test opaque tokens in scenario errors, scenario/workflow names, diagnostics, and directory names.


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 (9)

Looks good to me

Previous run (10)

Review

Findings

Medium

  • [logic-error] pkg/behaviourtest/steps/debuglogs.go:116 — A successful log fetch marks the run permanently saved even when its logs are incomplete. Both forge implementations can return nil after embedding individual job-log failures, and the GitHub round waiter can return before the overall workflow completes. The After hook then skips the run, preserving only a partial snapshot before repository deletion.
    Remediation: Deduplicate only complete snapshots; account for terminal-run status and partial fetches. Test an early or partial fetch followed by After-hook collection.

  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:219 — Collector errors are forwarded unchanged to World.Logf; scenario names and summary paths are also logged directly. Credentials in these values can reach CI console logs even when artifact contents are redacted. The artifact post-processing step does not sanitize already-emitted console output.
    Remediation: Redact fully formatted diagnostic messages before logging. Add captured-log tests for fetch errors, summary-write errors, and successful summary paths.

  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:298 — Scenario and workflow names enter artifact directory names without redaction. Slugging prevents traversal but can preserve a credential such as sk- followed by 24 lowercase characters. Content redaction, including the CI post-processing script, does not remove secrets from directory names.
    Remediation: Redact names before slugging, or use opaque directory identifiers. Test directory names as well as file contents.

Low

  • [api-contract] pkg/behaviourtest/steps/debuglogs.go:176 — Allocation can fail after repository creation without returning its identity. The summary correctly preserves the allocation error, but claiming there are no workflow runs is stronger than the available evidence; setup runs may exist.
    Remediation: Describe the repository identity as unavailable rather than asserting no runs exist. Preserving setup evidence before slot release can remain follow-up work.

  • [secret-exposure] pkg/behaviourtest/steps/debuglogs.go:314 — The helper inherits the opaque-credential limitation tracked by behaviour tests: harden credential redaction in saved failure logs and document the failed-run return #7948: the runner does not register World.Token with the redactor. Standard CI separately replaces listed environment-secret values in artifact contents, but standalone collection and unregistered values remain affected. This is a pre-existing, nonblocking hardening concern.
    Remediation: In the behaviour tests: harden credential redaction in saved failure logs and document the failed-run return #7948 follow-up, register or replace known opaque credentials and cover the new summary and unavailable-log artifact types.


Labels: The PR changes Go behaviour-test infrastructure and end-to-end failure diagnostics.


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 go Pull requests that update go code labels Oct 3, 2026
@fullsend-ai-coder

fullsend-ai-coder Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 2:27 PM UTC · Completed 2:34 PM UTC

Commit: 47c4bbe · View workflow run →

Runtime: claude · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $0.63

fullsend-ai-coder Bot added a commit that referenced this pull request Oct 3, 2026
- Deduplicate only complete log snapshots: a run that has not reached a
  terminal status, or whose log text embeds a per-job fetch failure, is
  no longer recorded as saved, so the After hook collects it again.
- Redact fully formatted diagnostic messages before logging them.
- Redact scenario and workflow names before slugging them into artifact
  directory names.
- Describe an unavailable repository identity instead of claiming that
  no workflow runs exist.

Not addressed: registering World.Token with the redactor is the
pre-existing limitation tracked by #7948 and is left to that follow-up.

Addresses #8040

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (bot-triggered)

Addressed the three medium findings and the api-contract low finding in debuglogs.go with tests. The low-severity #7948 redactor-registration finding is recorded as a disagreement because it is pre-existing and tracked separately. Go tests and vet pass for pkg/behaviourtest; golangci-lint and pre-commit were unavailable in the sandbox (pre-commit could not fetch hooks), so only gofmt, go vet and the secret scan ran.

Fixed (4):

  1. Successful but partial log fetch marks the run permanently saved (logic-error, debuglogs.go:116) (pkg/behaviourtest/steps/debuglogs.go): A run is now recorded in SavedLogRunIDs only when it has reached the completed status and the returned log text contains none of the in-band per-job fetch-failure notes emitted by the GitHub and GitLab clients. Partial snapshots are still written, but the After hook fetches those runs again and replaces them. Added tests for early/partial fetch followed by CollectFailureLogs.
  2. Collector errors, scenario names and summary paths reach World.Logf unredacted (secret-exposure, debuglogs.go:219) (pkg/behaviourtest/steps/debuglogs.go): All diagnostic messages in this file now go through logRedacted, which formats the message and redacts it before calling World.Logf. Added captured-log tests for fetch errors, scenario names and a nil logger.
  3. Scenario and workflow names enter artifact directory names without redaction (secret-exposure, debuglogs.go:298) (pkg/behaviourtest/steps/debuglogs.go): pathSlug now redacts its input before slugging, which covers both the per-run debug directory labels and the failure-summary directory. Added a test that directory names carry no secret.
  4. Unavailable repository identity reported as no workflow runs (api-contract, debuglogs.go:176) (pkg/behaviourtest/steps/debuglogs.go): The summary line now says the repository identity is unavailable and the runs could not be collected, instead of asserting that no runs exist. Updated the corresponding test.

Disagreed (1):

  1. World.Token is not registered with the redactor (secret-exposure, debuglogs.go:314): The reviewer marked this a pre-existing, nonblocking limitation tracked by behaviour tests: harden credential redaction in saved failure logs and document the failed-run return #7948 and asked for the remediation in that follow-up. Registering opaque credentials in the runner is outside this PR's scope, so it is left unchanged.

Tests: passed

CI inspections (6):

  1. test (CI) (unrelated, failure): A GitLab converge test in internal/repos fails; this PR only touches pkg/behaviourtest and docs, so it is not caused by the change. File an issue with the owners of internal/repos, or rerun the job to check for flakiness.
  2. e2e (passing, success)
  3. behaviour (passing, success)
  4. functional-tests (passing, success)
  5. commit-lint (passing, success)
  6. web (passing, success)

Conflict update: skipped; strategy none; forge MERGEABLE; target main@236fd48421d66f080c29d714bb5070def8bf7198

Decision points
  • How to decide that a log snapshot is complete (alternatives: Change the forge clients to return an error on partial logs, Check run status and detect the embedded failure notes in the returned text; rationale: Changing the forge clients would alter shared behaviour used elsewhere; checking status plus the known notes keeps the change local to the behaviour-test helper.)

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 Oct 3, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:36 PM UTC · Completed 2:47 PM UTC

Commit: 7b0164d · View workflow run →

Runtime: pi · Model: openai/gpt-6.1-sol → gpt-6.1-sol · Effort: high · Cost: $1.35

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 Oct 3, 2026
@rh-hemartin

Copy link
Copy Markdown
Member

/fs-fix rebase and resolve conflicts

@fullsend-ai-coder

fullsend-ai-coder Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

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

Commit: 7b0164d · View workflow run →

Runtime: claude · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $0.30

fullsend-ai-coder Bot added a commit that referenced this pull request Oct 6, 2026
- Deduplicate only complete log snapshots: a run that has not reached a
  terminal status, or whose log text embeds a per-job fetch failure, is
  no longer recorded as saved, so the After hook collects it again.
- Redact fully formatted diagnostic messages before logging them.
- Redact scenario and workflow names before slugging them into artifact
  directory names.
- Describe an unavailable repository identity instead of claiming that
  no workflow runs exist.

Not addressed: registering World.Token with the redactor is the
pre-existing limitation tracked by #7948 and is left to that follow-up.

Addresses #8040

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Rebased the PR branch onto origin/main and resolved one docs conflict; no other changes made.

Fixed (1):

  1. Rebase onto main and resolve merge conflicts (human request) (docs/guides/dev/behaviour-drivers.md): Rebased both PR commits onto origin/main (4bba878). One conflict in the 'Adding a CI driver' list: kept main's updated reference to the GitHub Actions driver package and retained the PR's new RunLister step. Build, vet and pkg/behaviourtest tests pass.

Tests: passed

CI inspections (1):

  1. test (unrelated, failure): Failure was in a converge test in internal/repos, which this PR does not touch; it passes locally after the rebase. Recommend rerunning CI after the rebase is pushed.

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 Oct 6, 2026 •

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 10:27 AM UTC · Ended 10:40 AM UTC

Commit: d30c536 · View workflow run →

@codecov

codecov Bot commented Oct 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:07 AM UTC · Completed 7:21 AM UTC

Commit: b42b842 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $3.29

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 7:23 AM UTC · Completed 7:29 AM UTC

Commit: b42b842 · View workflow run →

Runtime: claude · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $0.60

- Keep an already-saved complete workflow log snapshot when a later fetch
  of the same run is partial, instead of overwriting it.
- Register explicit infrastructure identifiers (E2E_GCP_*, CLOUDFLARE_ACCOUNT_ID)
  with the redactor alongside the name-suffix matches.
- Sanitize console diagnostics: replace control characters and break up
  the workflow-command delimiter so forge error text cannot inject commands.
- Wrap the log fetch error before preparing the debug dir so the empty-log
  diagnostic is not dropped when the directory fails.

Note: pre-commit could not fetch hook repositories in the sandbox; gofmt,
go vet and the behaviourtest tests were run directly. The unrelated
internal/security SSRF test fails in the sandbox (DNS resolution).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (bot-triggered)

Addressed all four low-severity findings in debuglogs.go with regression tests. The behaviourtest tests, go vet and gofmt pass. pre-commit could not fetch hook repos in the sandbox, so gofmt and go vet were run directly. The commit body has one line 1 character over gitlint's 80-column limit; it was not amended because history rewrites are not authorized.

Fixed (4):

  1. Saved-run flag is never cleared, so a later partial fetch can overwrite a complete snapshot while the run stays marked saved (pkg/behaviourtest/steps/debuglogs.go): writeWorkflowRunLogs now keeps the existing complete snapshot when a later fetch of an already-saved run is incomplete. Added a regression test covering a complete fetch, then a partial fetch, then failure collection.
  2. Redaction literal registration is suffix-based and misses sensitive infrastructure identifiers (pkg/behaviourtest/steps/debuglogs.go): Registered the explicitly listed variables (E2E_GCP_PROJECT_ID, E2E_GCP_MINT_PROJECT_ID, E2E_GCP_WIF_PROVIDER, E2E_GCP_SERVICE_ACCOUNT, CLOUDFLARE_ACCOUNT_ID) with the redactor, and added a test checking summaries, file contents and directory names.
  3. Console diagnostics pass newlines and control characters through, allowing workflow-command injection under GitHub Actions (pkg/behaviourtest/steps/debuglogs.go): logRedacted now sanitizes after redaction: control characters become spaces and the double-colon delimiter is broken up. Added a regression test with malicious error text.
  4. A debug-dir failure returns before the fetch error is wrapped, dropping the empty-log diagnostic (pkg/behaviourtest/steps/debuglogs.go): The fetch error and empty-log check now run immediately after GetRunLogs, before prepareDebugDir. Added a test for both the empty-log and fetch-error cases combined with a directory failure.

Tests: passed

CI inspections (6):

  1. CI / test (passing, success)
  2. CI / commit-lint (passing, success)
  3. Functional Tests / functional-tests (passing, success)
  4. E2E Tests / behaviour (passing, success)
  5. E2E Tests / playback (passing, success)
  6. codecov/patch (passing, success)

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 Oct 7, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:31 AM UTC · Completed 7:40 AM UTC

Commit: 8948807 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $2.16

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 7:42 AM UTC · Completed 7:47 AM UTC

Commit: 8948807 · View workflow run →

Runtime: claude · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $0.44

)

registerSecretForms ignored the result of RegisterRuntimeSecret, which
refuses values under 8 bytes, so a short sensitive value (World.Token or
a sensitive environment variable) was left unredacted in summaries,
console diagnostics, collected logs and directory names. Values the
shared redactor declines (4-7 bytes) are now tracked locally and masked
by redactText. Values under 4 bytes are still not masked, since doing
so would erase common fragments of every artifact.

Note: pre-commit hooks were not run via pre-commit (it could not fetch
hook repositories: TLS failure). Secret scan, gofmt, go vet and
go test ./pkg/behaviourtest/... pass; golangci-lint is not installed.
go test ./internal/security fails on this sandbox with or without this
change (DNS resolves a test hostname to a blocked address).

Addresses #8040

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (bot-triggered)

Addressed the single low-severity finding by masking short (4-7 byte) sensitive literals locally in redactText, with a regression test. pre-commit could not fetch hook repos in the sandbox (TLS failure), so secret scan, gofmt, go vet and go test ./pkg/behaviourtest/... were run instead. The internal/security tests fail in this sandbox on the base commit too (DNS), unrelated to this change.

Fixed (1):

  1. registerSecretForms ignores the false return from RegisterRuntimeSecret, so short sensitive values stay unredacted (fail-open) (pkg/behaviourtest/steps/debuglogs.go): Registration now goes through registerSecretLiteral. When the shared redactor declines a value as too short, values of 4-7 bytes are recorded locally and masked by redactText. Values under 4 bytes are deliberately not masked, since that would erase common fragments of every artifact; this is documented in the code. Added a regression test with short World.Token and environment secrets, covering the summary, console log, directory names and saved logs.

Tests: passed

CI inspections (5):

  1. test (passing, success): Unit tests passed.
  2. functional-tests (passing, success): Functional tests passed.
  3. commit-lint (passing, success): Commit lint passed.
  4. behaviour (pending, in_progress): E2E behaviour job still running at inspection time.
  5. playback (pending, in_progress): E2E playback job still running at inspection time.

Conflict update: skipped; strategy none; forge MERGEABLE; target main

Decision points
  • How to handle sensitive values too short for the shared redactor (alternatives: Suppress all content-bearing artifacts when a credential cannot be registered, Mask short literals locally with a minimum length; rationale: Local masking keeps diagnostics useful while closing the fail-open path; a 4-byte floor avoids destroying output for trivially short values.)

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 Oct 7, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:49 AM UTC · Completed 7:56 AM UTC

Commit: c5e0452 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $1.43

@fullsend-ai-review
fullsend-ai-review Bot dismissed stale reviews from themself October 7, 2026 07:56

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Oct 7, 2026
@rh-hemartin rh-hemartin added the ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) label Oct 7, 2026

This branch was successfully deployed

2 active deployments
dev — c5e04524 Deployed Oct 7, 2026 by fullsend-ai-coder[bot] via behaviour #15635
site-preview — c5e04524 Deployed Oct 7, 2026 by github-actions[bot]
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 go Pull requests that update go code needs-human Agent loop needs human intervention ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) ready-for-merge All reviewers approved — ready to merge ready-for-review Triggers review agent dispatch risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Behaviour tests: always save logs of failed runs, including runs that currently produce none

1 participant