Skip to content

fix(#8192): read mint_url from config.yaml/config.base.yaml in fullsend run - #8400

Open
mfrancisc wants to merge 19 commits into
fullsend-ai:mainfrom
mfrancisc:fix/8192-mint-url-config-layering
Open

mfrancisc wants to merge 19 commits into
fullsend-ai:mainfrom
mfrancisc:fix/8192-mint-url-config-layering

Conversation

@mfrancisc

@mfrancisc mfrancisc commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

fullsend run and the status-comment notifier never consulted config.yaml/config.base.yaml for mint_url — only --mint-url and FULLSEND_MINT_URL were read. This adds the file as a layer between them, so a repo can configure its mint endpoint via the layered config instead of only through the repository variable.

Related Issue

Fixes #8192

Changes

  • Add ConfigMintURLFromFile() to PerRepoConfigReader (internal/config/interfaces.go, internal/config/defaults.go): walks the overlay → base chain like ConfigMintURL(), but stops short of the compiled default (DefaultPerRepoMintURL). This lets callers tell "the file is silent" apart from "the file resolves to the hosted default."
  • Add resolveMintURL() in internal/cli/run.go with precedence: --mint-url flag > config.yaml/config.base.yaml mint_url > FULLSEND_MINT_URL > "".
  • Wire resolveMintURL() into runAgent() and setupStatusNotifierGitHub(), replacing the old flag-then-env-only resolution.
  • Deliberately preserve "narrow" behavior: when nothing sets mint_url anywhere, the resolved value stays "" and minting is skipped with a warning, exactly as before this change — it does not fall through to the compiled default https://mint.fullsend.sh. That default continues to apply only at install time (fullsend github setup/converge, via effectiveSetupMintURL()), which is unchanged.
  • Add an OIDC-availability downgrade in runAgent: a file-sourced mint_url with no FULLSEND_MINT_URL set, outside GitHub Actions (or with Actions OIDC env absent), downgrades to warn-and-skip instead of a hard failure, since minting would otherwise attempt and fail a GitHub Actions OIDC exchange that is unavailable locally.

Scope / follow-up

This PR's live-dispatch effect is not uniformly zero, and is narrower than "none" — see review discussion:

  • Most dispatched stages (triage, code, review, fix, retro, harness-run) still pass mint-url built from the FULLSEND_MINT_URL repository variable, which wins over the file via the flag. No observable change there until the route job is taught to compute an effective mint URL from a trusted config checkout (follow-up PR, needs workflows write permission a code agent doesn't have).
  • The prioritize paths (reusable-prioritize.yml and the inlined prioritize job in reusable-dispatch.yml) have no upfront mint-token step gating on that variable, and action.yml omits --mint-url entirely when its input is empty (rather than passing an empty value). So when FULLSEND_MINT_URL is unset but mint_url is set in config.yaml/config.base.yaml, fullsend run (and the status notifier, since these jobs set status-repo/status-number) now mint for real from the file's endpoint, inside a real Actions run with working OIDC — a live change today, on this one path. The same applies to any other composite-action caller that leaves mint-url empty. This is very likely the intended outcome of mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192 for that path, and is not a trust concern (.fullsend is checked out at a pinned base SHA), but it was undocumented. If the endpoint configured via the file does not serve the prioritize role, a prioritize run that previously skipped minting and used the workflow token will now fail instead.

Confirming that the mint endpoint serves the prioritize role wherever mint_url is file-only, and adding dedicated coverage for that path, is tracked as follow-up — not yet done in this PR. The docs/contributing/config-source-inventory.md mint_url row has been updated to describe this accurately.

Testing

  • make lint passes (stage changes first, then run)
  • Tests added/updated for new or modified logic
    • internal/config: ConfigMintURLFromFile overlay-wins, base-only fallback, and neither-set-divergence-from-ConfigMintURL cases
    • internal/cli: file-wins-over-env, base-file-wins-over-env, and flag-still-wins-over-file cases for both runAgent and setupStatusNotifierGitHub; confirmed existing TestRunAgent_WarnsWhenNoMintURL, TestRunAgent_FallsBackToFULLSEND_MINT_URL, TestMintAgentToken_SkipsWhenNoMintURL, TestSetupStatusNotifier_NoMintURL, TestRunAgent_MintsRuntimePrivilegeLevel remain green and unmodified

Context

  • partof the broader AISDLC-144 configuration-alignment effort

…l in fullsend run

Signed-off-by: fmuntean <fmuntean@redhat.com>
@mfrancisc
mfrancisc requested a review from a team as a code owner October 13, 2026 15:39
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

E2E tests did not run

E2E tests run automatically for pull requests authored by users with write access or higher.

For other contributors, someone with write access must add the ok-to-test label after the latest push.

Reason: unauthorized

See E2E testing guide for details.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Read layered mint_url configuration during fullsend run

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Read mint_url from layered config before FULLSEND_MINT_URL for agent runs and GitHub status
 comments.
• Preserve flag precedence and skip minting when no runtime URL is configured.
• Add precedence and fallback tests; dispatched workflows still require a follow-up change.
Diagram

graph TD
  Flag["--mint-url flag"] --> Resolver["URL resolver"] --> Agent["Agent run"] --> Mint["Mint service"]
  File["Overlay / base"] --> Resolver --> Notifier["GitHub notifier"] --> Mint
  Env["FULLSEND_MINT_URL"] --> Resolver
Loading
High-Level Assessment

Keep the shared CLI resolver and file-only accessor. Reusing ConfigMintURL would silently introduce the compiled default into runtime minting; resolving only in workflows would not fix local CLI runs. The separate workflow change remains necessary because dispatched stages currently pass an overriding flag.

Files changed (7) +333 / -11

Bug fix (3) +59 / -11
run.goResolve runtime mint URLs from flag, layered config, then environment +32/-11

Resolve runtime mint URLs from flag, layered config, then environment

• Adds a shared resolver for agent runs and GitHub status notifications. It preserves the empty-URL behavior when no runtime source supplies a value.

internal/cli/run.go

defaults.goStop file-only mint URL lookup at compiled defaults +5/-0

Stop file-only mint URL lookup at compiled defaults

• Implements ConfigMintURLFromFile on the defaults layer as an empty value, distinct from the hosted URL returned by ConfigMintURL.

internal/config/defaults.go

interfaces.goExpose file-only layered mint URL lookup +22/-0

Expose file-only layered mint URL lookup

• Extends PerRepoConfigReader with ConfigMintURLFromFile. The implementation searches the overlay and parent chain without substituting the compiled default.

internal/config/interfaces.go

Tests (4) +274 / -0
run_test.goTest runtime mint URL precedence +196/-0

Test runtime mint URL precedence

• Adds agent-run coverage for overlay and base-file values outranking the environment, and for the flag outranking the file. Adds GitHub status-notifier coverage for a file value outranking the environment.

internal/cli/run_test.go

defaults_test.goVerify defaults do not supply a file-only mint URL +2/-0

Verify defaults do not supply a file-only mint URL

• Asserts that direct defaults and an empty per-repo config return no file-only mint URL while retaining the compiled ConfigMintURL default.

internal/config/defaults_test.go

interfaces_test.goTest overlay precedence and file-only fallback +34/-0

Test overlay precedence and file-only fallback

• Covers parent fallback, local override, and the distinction between an unset file value and the compiled ConfigMintURL default.

internal/config/interfaces_test.go

load_test.goTest file-only mint URLs through config loading +42/-0

Test file-only mint URLs through config loading

• Checks overlay and base-file lookup after loading layered config, plus the empty result when neither file sets mint_url.

internal/config/load_test.go

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:41 PM UTC · Completed 3:51 PM UTC

Commit: 295b10b · View workflow run →

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

@qodo-code-review

qodo-code-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Run docs omit file mint settings ✓ Resolved
Description
resolveMintURL changes fullsend run and the status notifier to consult layered mint_url before
FULLSEND_MINT_URL, but the PR updates no affected documentation. The config-source inventory still
says runs use only the flag and environment variable, so users and maintainers cannot discover or
verify the new file-based precedence.
Code

internal/cli/run.go[R6549-6558]

+func resolveMintURL(flagValue string, orgCfg config.ConfigReader, getenv func(string) string) string {
+	if flagValue != "" {
+		return flagValue
+	}
+	if prc, ok := orgCfg.(config.PerRepoConfigReader); ok {
+		if u := prc.ConfigMintURLFromFile(); u != "" {
+			return u
+		}
+	}
+	return getenv("FULLSEND_MINT_URL")
Relevance

●●● Strong

Recent reviews accept updating user-facing docs when CLI configuration precedence or layered config
behavior changes.

PR-#7476
PR-#5976

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The documentation checklist requires every referenced configuration or environment-variable document
to be updated when behavior changes, and specifically requires the canonical config reference plus
layered-config documentation when layered resolution changes. The PR adds the file layer in
resolveMintURL, while the source inventory still documents flag-then-environment precedence and
the related configuration references retain the old default-only description.

Rule 2748504: Update docs/ references when changing user-facing behavior
Rule 2806787: Update CLI-related docs according to docs/contributing/documentation.md when changing CLI surface
internal/cli/run.go[6549-6558]
docs/contributing/documentation.md[20-27]
docs/contributing/config-source-inventory.md[113-113]

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 PR changes the precedence and source of the `mint_url` configuration used by `fullsend run` and the status notifier, but the related documentation still describes the previous flag-and-environment-only behavior.

## Fix Focus Areas
- docs/contributing/config-source-inventory.md[113-113]
- docs/reference/config-reference.md[267-275]
- docs/guides/infrastructure/layered-config-reference.md[230-245]

## Recommended Fix
Update the mint URL documentation to state the actual precedence: `--mint-url`, layered `config.yaml`/`config.base.yaml` `mint_url`, `FULLSEND_MINT_URL`, then no URL for the run path. Clarify that the compiled hosted default remains an install-time default and is not used by `fullsend run` when no runtime source sets the value.

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


2. CLI changes lack a required end-to-end gate 📘 Rule violation ▣ Testability
Description
The internal/cli/run.go changes have no documented make e2e-test execution or passing result in
the PR description. Although CI defines a related behaviour suite, the available evidence does not
show that the required end-to-end check ran successfully for this commit before merge.
Code

internal/cli/run.go[R872-875]

+	// Precedence: flag > config.yaml/config.base.yaml mint_url > env (#8192).
+	mintURL := resolveMintURL(sOpts.mintURL, orgCfg, os.Getenv)
	runtimeLevel := h.PrivilegeLevelForStage(harness.PrivilegeStageRuntime)
	var minted bool
Relevance

●●● Strong

Recent history accepts explicit E2E gating and documentation for CLI paths covered by the behavior
workflow.

PR-#2793
PR-#2277

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR modifies internal/cli/run.go, which is within the paths covered by the end-to-end testing
rule. The PR description lists lint and unit-test-related checks but does not state that `make
e2e-test passed, while the repository workflow invokes make behaviour-test` instead; no passing
result for the latest commit is available.

Rule 1062051: Run end-to-end tests for critical internal modules before merge
internal/cli/run.go[872-875]
.github/workflows/e2e.yml[186-194]

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

## Issue description
Changes under `internal/cli/` require evidence that the end-to-end test suite ran successfully before merge, but the PR description does not record a passing `make e2e-test` run and no latest-commit result is provided.

## Fix Focus Areas
- internal/cli/run.go[872-875]
- .github/workflows/e2e.yml[186-194]

## Recommended Fix
Run the repository's current end-to-end target for this change and record the successful result in the PR description, or ensure the equivalent CI behaviour-test job is a required status check that runs for `internal/cli/` changes and passes before merge.

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

Dismiss ↗ | View ↗


3. Base-only repos lose status notifications ✓ Resolved
Description
setupStatusNotifier passes a config loaded by tryLoadFullsendConfig to the new mint URL
resolver, but that loader returns nil when config.yaml is absent even if config.base.yaml
exists. For a base-only repository with mint_url in the base file and no flag or environment
value, the agent can load the base URL while notifier setup reports that no mint URL is available
and disables status comments.
Code

internal/cli/run.go[R6117-6118]

+func setupStatusNotifierGitHub(notifyCfg config.StatusNotificationConfig, orgCfg config.ConfigReader, owner, repo, role string, sOpts statusOpts, printer *ui.Printer) (*statuscomment.Notifier, error) {
+	mintURL := resolveMintURL(sOpts.mintURL, orgCfg, os.Getenv)
Relevance

●●● Strong

Recent precedent accepts fixing loaders that skip base-only configuration, matching this notifier
failure mode.

PR-#8088

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The notifier passes the result of tryLoadFullsendConfig into resolveMintURL, but that loader
returns nil on a missing overlay before it can read the base file. The runtime loader accepts a
base-only configuration; without a notifier config, the resolver falls back to the environment and
an empty result causes notifier setup to fail.

internal/cli/run.go[6082-6118]
internal/cli/orgconfig.go[16-29]
internal/cli/lock.go[151-178]
internal/cli/run.go[6549-6558]
internal/cli/run.go[1270-1278]

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 status notifier cannot read `mint_url` from a base-only configuration because its config loader requires `config.yaml` to exist. This disables status comments when the URL is set only in `config.base.yaml` and no flag or environment value is available.

## Fix Focus Areas
- internal/cli/run.go[6082-6118]
- internal/cli/orgconfig.go[16-29]
- internal/cli/lock.go[151-178]

## Recommended Fix
Load the notifier's configuration through a path that accepts either config layer, as `loadLockConfig` does, while retaining the notifier's best-effort warning behavior for malformed configuration. Add a notifier test with only `config.base.yaml` present and its `mint_url` set.

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


Grey Divider

Context sources
✅ Compliance rules (platform): 89 rules
✅ Skills: 5 invoked
  writing-user-docs
  writing-adrs
  check-patch-coverage
  cutting-releases
  adr-corner
✅ Cross-repo context — repo relationships
  Explored: repo: fullsend-ai/.fullsend (sha: 7c163cad) — View relationship
  Explored: repo: fullsend-ai/agents (sha: e0166257) — View relationship
Review mode: Auto: ⚖️ Balanced: Behavioral mint URL precedence affects token minting across multiple CLI paths.

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread internal/cli/run.go
Comment on lines +872 to 875
// Precedence: flag > config.yaml/config.base.yaml mint_url > env (#8192).
mintURL := resolveMintURL(sOpts.mintURL, orgCfg, os.Getenv)
runtimeLevel := h.PrivilegeLevelForStage(harness.PrivilegeStageRuntime)
var minted bool

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Cli changes lack a required end-to-end gate 📘 Rule violation ▣ Testability

The internal/cli/run.go changes have no documented make e2e-test execution or passing result in
the PR description. Although CI defines a related behaviour suite, the available evidence does not
show that the required end-to-end check ran successfully for this commit before merge.
Agent Prompt
## Issue description
Changes under `internal/cli/` require evidence that the end-to-end test suite ran successfully before merge, but the PR description does not record a passing `make e2e-test` run and no latest-commit result is provided.

## Fix Focus Areas
- internal/cli/run.go[872-875]
- .github/workflows/e2e.yml[186-194]

## Recommended Fix
Run the repository's current end-to-end target for this change and record the successful result in the PR description, or ensure the equivalent CI behaviour-test job is a required status check that runs for `internal/cli/` changes and passes before merge.

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

Dismiss ↗ | View ↗

Comment thread internal/cli/run.go Outdated
Comment thread internal/cli/run.go Outdated
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.29730% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/config/interfaces.go 83.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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

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

Copy link
Copy Markdown

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths or dependency changes and solid test coverage offsets elevated commit churn and coupling in runner CLI files, with implementation scope closely aligning with issue #8192.

Previous run

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

High churn and file coupling across runner CLI files are balanced by zero protected or security-sensitive paths, strong test coverage, and clear issue alignment.

Previous run (2)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Low metadata risk with substantial test additions and no protected paths is balanced by high recent churn and multi-author contention on internal/cli/run.go.

Previous run (3)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Low metadata risk with substantial test additions and no protected paths is balanced by high recent churn and multi-author contention on internal/cli/run.go.

Previous run (4)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths or dependency changes and solid test coverage offsets elevated commit churn and coupling in runner CLI files, with implementation scope closely aligning with issue #8192.

Previous run (5)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths or dependency changes and solid test coverage offsets elevated commit churn and coupling in runner CLI files, with implementation scope closely aligning with issue #8192.

Previous run (6)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths or dependency changes and solid test coverage offsets elevated commit churn and coupling in runner CLI files, with implementation scope closely aligning with issue #8192.

Previous run (7)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths and high test coverage offsets elevated commit churn and coupling in internal/cli/run.go, with the implementation closely aligning with linked issue #8192.

Previous run (8)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk reflects solid test coverage and zero protected paths balanced by high recent churn and multi-author contention on internal/cli/run.go, with scope well aligned to linked issue #8192.

Previous run (9)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk reflects solid test coverage (42%) and zero protected paths balanced by high recent churn and multi-author contention on internal/cli/run.go, with scope well aligned to linked issue #8192.

Previous run (10)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk reflects solid test coverage (42%) and zero protected paths balanced by high recent churn and multi-author contention on internal/cli/run.go, with scope well aligned to linked issue #8192.

Previous run (11)

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths and strong test coverage offsets elevated commit churn and coupling in internal/cli/run.go, with the implementation closely aligning with linked issue #8192.

Previous run (12)

Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths and strong test coverage offsets elevated commit churn and coupling in internal/cli/run.go, with the implementation closely aligning with linked issue #8192.

Previous run (13)

Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths and high test coverage offsets elevated commit churn and coupling in internal/cli/run.go, with the implementation closely aligning with linked issue #8192.

Previous run (14)

Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths and high test coverage offsets elevated commit churn and coupling in internal/cli/run.go, with the implementation closely aligning with linked issue #8192.

Previous run (15)

Risk Assessment: moderate (2/5)

Details

Moderate risk: low-risk metadata with no protected paths and high test coverage offsets elevated commit churn and coupling in internal/cli/run.go, with the implementation closely aligning with linked issue #8192.

Previous run (16)

Risk Assessment: moderate (2/5)

Details

Low metadata risk with strong test coverage (57%) and no protected paths is balanced by high recent churn and multi-author contention on internal/cli/run.go.

@fullsend-ai-review

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

Copy link
Copy Markdown

Review

Findings

Medium

  • [logic-error] internal/cli/run.go:1402 — The cancelled-review finalizer passes the raw sOpts.mintURL to newStatusGitHubForgeClient, which falls back only to FULLSEND_MINT_URL and never consults the layered config. setupStatusNotifierGitHub resolves the endpoint through resolveMintURL (file wins), so with a file-only mint_url the start comment is posted but the cancellation finalizer mints against an empty URL and returns without finalizing. With a competing env value it mints against the env endpoint instead of the file-selected one. The reconcile-status always() step partly covers this, hence medium.
    Remediation: Use the resolved mint endpoint (resolveMintURL with the loaded layered config) for cancellation finalization, or reuse the notifier's configured client factory. Add cancellation regression tests for a file-only mint_url and for a file value competing with a different FULLSEND_MINT_URL.

Low

  • [api-contract] docs/guides/user/bring-your-own-agent.md:185 — The doc states that a run where GitHub Actions OIDC is unavailable skips minting and uses GH_TOKEN, with no qualification. runAgent only downgrades when the URL came solely from the config file, FULLSEND_MINT_URL is unset and GITHUB_ACTIONS is not true. An explicit --mint-url or FULLSEND_MINT_URL (even one shadowed by the file) still attempts minting and fails closed without OIDC.
    Remediation: Restrict the OIDC-unavailable fallback wording to a file-only mint_url outside GitHub Actions, and state that an explicit flag or environment value stays fail-closed.
  • [design-direction] internal/cli/run.go:467 — The fullsend run --mint-url flag help still reads (default: $FULLSEND_MINT_URL) and describes status tokens only. The flag now also governs agent token minting, and precedence is flag > layered config file > FULLSEND_MINT_URL. The reconcile-status flag help was updated in this PR.
    Remediation: Update the --mint-url help text in run.go to describe agent and status token minting and the flag > config file > FULLSEND_MINT_URL precedence.
  • [error-handling-idiom] internal/cli/run.go:6474 — The error returned by setupStatusNotifierGitHub when no mint URL resolves still says (set --mint-url or FULLSEND_MINT_URL). It omits the config.yaml/config.base.yaml source now consulted by resolveMintURL, while reconcileGitHubClient lists all three sources.
    Remediation: Update the message to name the layered mint_url alongside --mint-url and FULLSEND_MINT_URL, matching reconcileGitHubClient.
  • [under-engineering] internal/cli/run.go:900 — Mint URL resolution and the local-run OIDC downgrade in runAgent are inline. setupStatusNotifierGitHub re-resolves the URL independently after a separate config load, and the cancellation path at run.go:1402 resolves a third way. This divergence produces the cancellation finding above, and config-source-inventory.md acknowledges the OIDC downgrade is not yet extended to status comments.
  • [fail-open] internal/cli/run.go:923 — When mint_url comes only from the config file, FULLSEND_MINT_URL is empty, GITHUB_ACTIONS is not true and the OIDC request variables are absent, runAgent clears mintURL and continues with the ambient GH_TOKEN, which also skips pre/post-script privilege reminting. This matches the base-branch behavior for the same inputs, and flag/env/Actions cases still fail closed, so it is not a regression. Design note: a file-set mint_url is silently ignored outside Actions, and the local-run test depends on GITHUB_ACTIONS, which the caller's environment controls.
  • [doc-style] internal/cli/run_test.go:8457 — The doc comment for TestRunAgent_MintURLFlagFailsClosedWithoutOIDC (about lines 8455-8459) sits directly above TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet's own comment, leaving the function at about line 8555 without a doc comment.
  • [backward-incompatible] internal/cli/run.go:6945 — resolveMintURL now ranks a config-file mint_url above FULLSEND_MINT_URL in fullsend run, where the env var was previously the only source. A repo with both set to different values now mints against the file's URL. This is the behavior issue mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192 requests and is documented, but there is no release-note callout.
    Remediation: Mark the change as a behavior change in the release notes.

Earlier findings

  • internal/cli/run.go:918, design-smell — reclassified (below posting threshold): single inline condition, helpers already factored out (run.go:901-924).
  • internal/cli/orgconfig.go:48, pattern-inconsistency — reclassified (below posting threshold): warning text still accurate and matches sibling loader (orgconfig.go:25, 48-53).
  • internal/cli/orgconfig.go:44, misplaced-abstraction — reclassified (below posting threshold): file already holds generic per-repo loaders (orgconfig.go:16, 59).
  • internal/cli/run.go:6955, architectural-conflict — reclassified (below posting threshold): same-package call, type assertion is deliberate and documented in resolveMintURL.

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

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfZjk1ZWQ3NTc2MzI4ZDZmMiIsImxpbmUiOjE0MDIsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImFwaS1jb250cmFjdCIsImZpbGUiOiJkb2NzL2d1aWRlcy91c2VyL2JyaW5nLXlvdXItb3duLWFnZW50Lm1kIiwiaWQiOiJmX2YxYzFjNjU2OTEzM2VmNGYiLCJsaW5lIjoxODUsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6InVuZGVyLWVuZ2luZWVyaW5nIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNWRhZjg0OGY1YWEwZGQ1OCIsImxpbmUiOjkwMCwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImFyY2hpdGVjdHVyYWwtY29uZmxpY3QiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl8wNTNiNGFjOWU4OThkNTBhIiwibGluZSI6Njk1NSwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZGVzaWduLXNtZWxsIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfMzM2Mzg0OTNiNGRiMjljZiIsImxpbmUiOjkxOCwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoibWlzcGxhY2VkLWFic3RyYWN0aW9uIiwiZmlsZSI6ImludGVybmFsL2NsaS9vcmdjb25maWcuZ28iLCJpZCI6ImZfMjc2MGYwY2FkZmJlNmYwYiIsImxpbmUiOjQ0LCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJkZXNpZ24tZGlyZWN0aW9uIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfYjM2OGIwNzA3ODQyOGY2NCIsImxpbmUiOjQ2NywiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZXJyb3ItaGFuZGxpbmctaWRpb20iLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl8wOWMyZmViZTU3NGRlZWU0IiwibGluZSI6NjQ3NCwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoicGF0dGVybi1pbmNvbnNpc3RlbmN5IiwiZmlsZSI6ImludGVybmFsL2NsaS9vcmdjb25maWcuZ28iLCJpZCI6ImZfNDFjYjQwMWMxZDNiYWJjZSIsImxpbmUiOjQ4LCJhY3Rpb25hYmxlIjp0cnVlfV19 -->

Review

Findings

Medium

  • [logic-error] internal/cli/run.go:1402 — The cancelled built-in review path still passes sOpts.mintURL directly to newStatusGitHubForgeClient, which falls back only to FULLSEND_MINT_URL. It bypasses the new file-layer resolution used for agent minting and ordinary status notifications. With a file-only mint_url, cancellation fails with an empty mint URL and returns without finalizing the status comment. With a stale environment URL, it contacts the wrong endpoint and, on success, replaces the notifier's correctly configured client factory with that client.
    Remediation: Resolve the mint URL through resolveMintURL (flag > file > env) on this cancellation path as well, and add cancellation tests for file-only configuration and file-over-environment precedence.

Low

  • [api-contract] docs/guides/user/bring-your-own-agent.md:185 — The new text says a local run skips minting whenever GitHub Actions OIDC is unavailable. The implementation only downgrades when mint_url came from the file, FULLSEND_MINT_URL is unset, and GITHUB_ACTIONS is not true; an explicit --mint-url or FULLSEND_MINT_URL stays fail-closed.
    Remediation: Limit the description to a file-sourced mint_url with no flag/env, outside GitHub Actions, and state that explicit configuration remains fail-closed.
  • [under-engineering] internal/cli/run.go:900 — PR metadata concern: the PR body says Fixes #8192, but most dispatched stages still pass --mint-url from the repository variable (flag beats file) until the follow-up route-job change lands. The PR discloses this, but merge will auto-close mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192 while that part is pending.
    Remediation: Consider Relates to #8192, or reopen/track mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192 after merge.
  • [architectural-conflict] internal/cli/run.go:6955 — Per AGENTS.md/ADR 0044, org-mode references should be flagged. New resolveMintURL, reconcileGitHubClient and setupStatusNotifierGitHub signatures use orgCfg naming (and an org-mode comment) for a per-repo layered config. Naming/comment only; no org-mode behaviour added.
    Remediation: Rename to cfg/repoCfg and soften the comment.
  • [design-smell] internal/cli/run.go:918 — The OIDC-availability downgrade is asymmetric: only runAgent uses mintURLSource; the status notifier and reconcile-status discard it and still attempt OIDC exchange on a local run with a file-sourced mint_url. The inventory doc records this as tracked, not done.
    Remediation: Apply the check uniformly across mint paths or encapsulate it in token resolution.
  • [misplaced-abstraction] internal/cli/orgconfig.go:44 — tryLoadLayeredFullsendConfig adds a second best-effort loader; tryLoadFullsendConfig is now referenced only from tests, and reconcilestatus.go uses a third variant directly.
    Remediation: Consolidate .fullsend config loading into one helper and remove the unused one.
  • [design-direction] internal/cli/run.go:467 — The fullsend run --mint-url help still says (default: $FULLSEND_MINT_URL); docs/reference/config-reference.md also does not describe the flag > file > env precedence or that the compiled default is not used at run time.
    Remediation: Update the flag text and document the precedence in the config reference.
  • [error-handling-idiom] internal/cli/run.go:6474 — setupStatusNotifierGitHub now resolves via resolveMintURL, but its error does not mention config.yaml/config.base.yaml, unlike the updated reconcileGitHubClient message.
    Remediation: Mention mint_url in config.yaml/config.base.yaml in the message.
  • [pattern-inconsistency] internal/cli/orgconfig.go:48 — tryLoadLayeredFullsendConfig warns Config malformed (remote resource allowlist unavailable), copied from the old loader; its caller uses the config for status notifications and mint_url, so the context is misleading.
    Remediation: Use a general Config malformed: <err> message.

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

Reason: stale-head

The review agent reviewed commit ff16a0bc39c7ecde6aabb1a0f47de45a26555231 but the PR HEAD is now 29713b072ee30d153654dec13dbaa3067a7db480. This review was discarded to avoid approving unreviewed code.

Previous run (3)

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJoaWdoIiwiY2F0ZWdvcnkiOiJhcGktY29udHJhY3QiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3JlY29uY2lsZXN0YXR1cy5nbyIsImlkIjoiZl8yODVlYmE5ZDJhNDdhODVhIiwibGluZSI6MTgwLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJsb2dpYy1lcnJvciIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzdjYWI2ZjA5MWQyMmUyNjMiLCJsaW5lIjoxMzk5LCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJpbmNvbXBsZXRlLWRvYyIsImZpbGUiOiJkb2NzL3JlZmVyZW5jZS9jb25maWctcmVmZXJlbmNlLm1kIiwiaWQiOiJmXzgwMjU0OGUwNzAyMTZkNjkiLCJsaW5lIjozMDgsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzc3ZjJkM2IwMmYyYWNhNDAiLCJsaW5lIjo5MjMsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJkb2Mtc3R5bGUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bl90ZXN0LmdvIiwiaWQiOiJmXzM3ODdjMWY4ZjljMTdhZjMiLCJsaW5lIjo4MzM4LCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiYnJlYWtpbmctY2xpIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfMzkxZmQ4ZTNmNzg2ZjljMiIsImxpbmUiOjY5NzgsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJicmVha2luZy1jbGkiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3JlY29uY2lsZXN0YXR1cy5nbyIsImlkIjoiZl9hYjk2MzZmNzRmNzk5MjlkIiwibGluZSI6MTk5LCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoibWlzc2luZy1kb2MiLCJmaWxlIjoiZG9jcy9jbGkvcnVuLm1kIiwiaWQiOiJmXzUyNGE3ZmMwNTJiMDcyYjQiLCJsaW5lIjoxNywiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfOThhYTM4MzkwNjEyZDFlYiIsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJzZWNyZXQtZXhwb3N1cmUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85ODkwOTliZDU1ZGYxZGVmIiwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNzEyYjYxMGI3NzQzYmRhZSIsImxpbmUiOjYxMDcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2NvbnRyaWJ1dGluZy9jb25maWctc291cmNlLWludmVudG9yeS5tZCIsImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwibGluZSI6MTEzLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJsb2dpYy1lcnJvciIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzgzMGU5ZjhlMzQwNzMxY2MiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImRvYy1zdHlsZSIsImZpbGUiOiJpbnRlcm5hbC9jb25maWcvaW50ZXJmYWNlc190ZXN0LmdvIiwiaWQiOiJmXzY4OGQwNDBjNmY0NThjNzIiLCJsaW5lIjozNTcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2d1aWRlcy91c2VyL3J1bm5pbmctYWdlbnRzLWxvY2FsbHkubWQiLCJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsImxpbmUiOjQwMCwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvZ3VpZGVzL3VzZXIvYnJpbmcteW91ci1vd24tYWdlbnQubWQiLCJpZCI6ImZfODJjZjNlYWZkOTFjMThkYyIsImxpbmUiOjE4MiwiYWN0aW9uYWJsZSI6dHJ1ZX1dLCJkaXNwb3NpdGlvbnMiOlt7ImlkIjoiZl84MDI1NDhlMDcwMjE2ZDY5Iiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzk4YWEzODM5MDYxMmQxZWIiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfOTg5MDk5YmQ1NWRmMWRlZiIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl83N2YyZDNiMDJmMmFjYTQwIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzUyNGE3ZmMwNTJiMDcyYjQiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfYWI5NjM2Zjc0Zjc5OTI5ZCIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl8zNzg3YzFmOGY5YzE3YWYzIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzcxMmI2MTBiNzc0M2JkYWUiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl82ODhkMDQwYzZmNDU4YzcyIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzgyY2YzZWFmZDkxYzE4ZGMiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifV19 -->

Review

Findings

High

  • [api-contract] internal/cli/reconcilestatus.go:180 — The call to resolveBuiltInGitHubReviewCancellationGuidance has lost its statusForgeClient argument. The PR head has cmd.Context(), followed by three whitespace-only lines and then , "review", forgePlatform, trackerSource, (lines 175–180). The bare comma where an expression should be is a Go syntax error, so the internal/cli package, the fullsend binary and the CLI tests cannot compile.
    Remediation: Restore statusForgeClient as the second argument (cmd.Context(), statusForgeClient, "review", forgePlatform, trackerSource,), remove the stray whitespace lines, then run gofmt and go test ./internal/cli/....

Medium

  • [logic-error] internal/cli/run.go:1399 — The cancelled-review finalizer still calls newStatusGitHubForgeClient(dCtx, sOpts.mintURL, h.Role, parts[1]), which falls back only to FULLSEND_MINT_URL. It does not use the resolveMintURL precedence (flag > file > env) that the PR applies to agent minting and to the start-notification client. If mint_url is set only in a config file, cancellation finalization mints with an empty URL. If FULLSEND_MINT_URL also holds a different value, it uses that endpoint instead of the file value. The in-process cancellation completion path therefore disagrees with the rest of the run and depends on the later reconcile-status step.
    Remediation: Pass the resolved mint URL into the cancellation finalizer, for example resolveMintURL(sOpts.mintURL, orgCfg, os.Getenv), the same way setupStatusNotifierGitHub does. Add regression tests for a file-only mint_url and for a file value competing with an env value.

  • [incomplete-doc] docs/reference/config-reference.md:308 — The mint_url section still describes only the install-time behavior (default value and the FULLSEND_MINT_URL repository variable). It does not say that fullsend run, the status-comment notifier and reconcile-status --fullsend-dir now read mint_url from config.yaml/config.base.yaml with precedence flag > file > FULLSEND_MINT_URL, or that a file-only value downgrades to warn-and-skip on a local run without Actions OIDC. This doc is not modified by the PR.
    Remediation: Add the runtime precedence and the local-run downgrade to the mint_url section, with a link to docs/contributing/config-source-inventory.md.

Low

  • [fail-open] internal/cli/run.go:923 — When mint_url comes only from config.yaml/config.base.yaml, FULLSEND_MINT_URL is unset, GITHUB_ACTIONS is not true and the Actions OIDC env vars are missing, runAgent clears mintURL, warns and continues with the ambient GH_TOKEN. Pre/post-script reminting is skipped. Before this PR fullsend run ignored the file mint_url, so these local runs already used the ambient token without minting, and explicit flag/env values and all runs inside Actions stay fail-closed (covered by tests). The remaining concern is that a configured mint endpoint degrades with a warning instead of failing closed.
    Remediation: Optionally require an explicit opt-in for the local ambient-token fallback, or make the warning more prominent.

  • [doc-style] internal/cli/run_test.go:8338 — The doc comment above TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet begins with a paragraph about TestRunAgent_MintURLFlagFailsClosedWithoutOIDC (lines 8338–8342). That function is defined later (around line 8438) with no doc comment of its own.
    Remediation: Move lines 8338–8342 so they sit directly above func TestRunAgent_MintURLFlagFailsClosedWithoutOIDC.

  • [breaking-cli] internal/cli/run.go:6978 — The fullsend run mint_url precedence changes from flag > env to flag > file > env. Setups that set both FULLSEND_MINT_URL and mint_url in config.yaml/config.base.yaml now use the file value. This is what mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192 asks for, but it changes the effective mint for existing setups.
    Remediation: Mention the precedence change in the release notes.

  • [breaking-cli] internal/cli/reconcilestatus.go:199 — reconcile-status now falls back to the layered mint_url from --fullsend-dir before $FULLSEND_MINT_URL. The error messages for a missing mint URL and for a missing --role also changed. Scripts that match those strings, or that rely on the env var taking precedence, will behave differently.
    Remediation: Mention the change in the release notes.

  • [missing-doc] docs/cli/run.md:17 — The fullsend run flags table and the rest of the page do not document the mint URL resolution (--mint-url > config.yaml/config.base.yaml mint_url > FULLSEND_MINT_URL) or the local-run warn-and-skip downgrade. This carried-over finding is unchanged because the PR does not touch this page.
    Remediation: Document the mint URL precedence and the downgrade on this page.

  • [logic-error] internal/cli/run.go — Carried-over finding: a low-severity logic-error finding in this file from the prior review is not resolved by the current diff. The prior projection carries no description, so it is kept at its prior severity until a code change addresses it.

  • [secret-exposure] internal/cli/run.go — Carried-over finding: a low-severity secret-exposure finding in this file from the prior review is not resolved by the current diff. The prior projection carries no description, so it is kept at its prior severity until a code change addresses it.

  • [sub-agent-failure] N/A — The docs-currency sub-agent did not return findings: it timed out after 900s. The two open docs findings above were re-checked directly against the unmodified docs; no further documentation review was done for this run.


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)

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImluY29tcGxldGUtZG9jIiwiZmlsZSI6ImRvY3MvcmVmZXJlbmNlL2NvbmZpZy1yZWZlcmVuY2UubWQiLCJpZCI6ImZfODAyNTQ4ZTA3MDIxNmQ2OSIsImxpbmUiOjI5MiwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoibG9naWMtZXJyb3IiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85OGFhMzgzOTA2MTJkMWViIiwibGluZSI6NjM3MiwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6InNlY3JldC1leHBvc3VyZSIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzk4OTA5OWJkNTVkZjFkZWYiLCJsaW5lIjo2ODMwLCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZmFpbC1vcGVuIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNzdmMmQzYjAyZjJhY2E0MCIsImxpbmUiOjkxNiwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6Im1pc3NpbmctZG9jIiwiZmlsZSI6ImRvY3MvY2xpL3J1bi5tZCIsImlkIjoiZl81MjRhN2ZjMDUyYjA3MmI0IiwibGluZSI6MTcsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJicmVha2luZy1jbGkiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3JlY29uY2lsZXN0YXR1cy5nbyIsImlkIjoiZl9hYjk2MzZmNzRmNzk5MjlkIiwibGluZSI6MTk5LCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZG9jLXN0eWxlIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW5fdGVzdC5nbyIsImlkIjoiZl8zNzg3YzFmOGY5YzE3YWYzIiwibGluZSI6ODI4NSwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNzEyYjYxMGI3NzQzYmRhZSIsImxpbmUiOjYxMDcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2NvbnRyaWJ1dGluZy9jb25maWctc291cmNlLWludmVudG9yeS5tZCIsImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwibGluZSI6MTEzLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJsb2dpYy1lcnJvciIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzgzMGU5ZjhlMzQwNzMxY2MiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImRvYy1zdHlsZSIsImZpbGUiOiJpbnRlcm5hbC9jb25maWcvaW50ZXJmYWNlc190ZXN0LmdvIiwiaWQiOiJmXzY4OGQwNDBjNmY0NThjNzIiLCJsaW5lIjozNTcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2d1aWRlcy91c2VyL3J1bm5pbmctYWdlbnRzLWxvY2FsbHkubWQiLCJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsImxpbmUiOjQwMCwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvZ3VpZGVzL3VzZXIvYnJpbmcteW91ci1vd24tYWdlbnQubWQiLCJpZCI6ImZfODJjZjNlYWZkOTFjMThkYyIsImxpbmUiOjE4MiwiYWN0aW9uYWJsZSI6dHJ1ZX1dLCJkaXNwb3NpdGlvbnMiOlt7ImlkIjoiZl84MDI1NDhlMDcwMjE2ZDY5Iiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzk4YWEzODM5MDYxMmQxZWIiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfNTI0YTdmYzA1MmIwNzJiNCIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl85ODkwOTliZDU1ZGYxZGVmIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzc3ZjJkM2IwMmYyYWNhNDAiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfYWI5NjM2Zjc0Zjc5OTI5ZCIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl8zNzg3YzFmOGY5YzE3YWYzIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzcxMmI2MTBiNzc0M2JkYWUiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl82ODhkMDQwYzZmNDU4YzcyIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzgyY2YzZWFmZDkxYzE4ZGMiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifV19 -->

Review

Findings

Medium

  • [incomplete-doc] docs/reference/config-reference.md:292 — The ### mint_url section only describes install-time setting of the FULLSEND_MINT_URL repository variable. After this PR, fullsend run and fullsend reconcile-status read mint_url directly from config.yaml/config.base.yaml with precedence over FULLSEND_MINT_URL (overridden only by --mint-url), and a file-only value warns and skips minting on local runs without GitHub Actions OIDC. None of this is documented in the canonical config reference.
    Remediation: Update the ### mint_url section to describe the flag > file > env precedence for fullsend run/reconcile-status and the local-run warn-and-skip downgrade.

Low

  • [logic-error] internal/cli/run.go:6372 — The status notifier resolves the file-sourced mint URL without applying runAgent's local/OIDC downgrade. On a local run with mint_url only in the config file and no Actions OIDC variables, agent minting is skipped, but --status-repo/--status-number still installs a mint-backed notifier whose start/completion calls fail and emit extra warnings.
    Remediation: Apply the same file-source/local/OIDC eligibility check before installing the notifier, preserving fail-closed behavior for explicit flag/env configuration and Actions runs.
  • [secret-exposure] internal/cli/run.go:6830 — The file-sourced mint URL now takes precedence over FULLSEND_MINT_URL and determines where the mint-audience OIDC JWT is sent. mintclient enforces HTTPS (except localhost) but no endpoint allowlist applies, so a compromised or mistaken config could redirect the JWT. This requires control of the loaded configuration.
    Remediation: Validate file-sourced mint destinations against an operator-controlled allowlist, retaining explicit operator overrides for custom mints.
  • [fail-open] internal/cli/run.go:916 — The local-run downgrade clears a configured mint URL when FULLSEND_MINT_URL is empty, GITHUB_ACTIONS is not true, and OIDC request variables are absent, so the run proceeds with ambient credentials and skips token downscoping. Flag/env-requested and in-Actions file-sourced mints remain fail-closed, and this matches prior local behavior when no mint URL was set.
    Remediation: Require explicit operator opt-in before substituting ambient credentials for configured role-scoped minting.
  • [missing-doc] docs/cli/run.md:17 — The Flags table omits --mint-url, which now takes precedence over layered config mint_url and $FULLSEND_MINT_URL.
    Remediation: Add --mint-url to the Flags table with its precedence.
  • [breaking-cli] internal/cli/reconcilestatus.go:199 — reconcile-status now resolves mint_url as flag > layered config file > FULLSEND_MINT_URL, so a file value overrides the env var for callers that set both --fullsend-dir and the env var. Intentional and documented in help text.
    Remediation: Note the precedence change in release notes or the PR description.
  • [doc-style] internal/cli/run_test.go:8285 — The doc comment for TestRunAgent_MintURLFlagFailsClosedWithoutOIDC sits above TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet (line 8297), leaving the function at line 8385 without a doc comment.
    Remediation: Move the comment block at lines 8285-8289 to directly precede the function at line 8385.

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

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImluY29tcGxldGUtZG9jIiwiZmlsZSI6ImRvY3MvcmVmZXJlbmNlL2NvbmZpZy1yZWZlcmVuY2UubWQiLCJpZCI6ImZfODAyNTQ4ZTA3MDIxNmQ2OSIsImxpbmUiOjI5MiwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoibG9naWMtZXJyb3IiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85OGFhMzgzOTA2MTJkMWViIiwibGluZSI6NjM3MiwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6Im1pc3NpbmctZG9jIiwiZmlsZSI6ImRvY3MvY2xpL3J1bi5tZCIsImlkIjoiZl81MjRhN2ZjMDUyYjA3MmI0IiwibGluZSI6MTcsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJzZWNyZXQtZXhwb3N1cmUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85ODkwOTliZDU1ZGYxZGVmIiwibGluZSI6NjgzMCwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzc3ZjJkM2IwMmYyYWNhNDAiLCJsaW5lIjo5MTYsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJicmVha2luZy1jbGkiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3JlY29uY2lsZXN0YXR1cy5nbyIsImlkIjoiZl9hYjk2MzZmNzRmNzk5MjlkIiwibGluZSI6MTkxLCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZG9jLXN0eWxlIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW5fdGVzdC5nbyIsImlkIjoiZl8zNzg3YzFmOGY5YzE3YWYzIiwibGluZSI6ODI4NSwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNzEyYjYxMGI3NzQzYmRhZSIsImxpbmUiOjYxMDcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2NvbnRyaWJ1dGluZy9jb25maWctc291cmNlLWludmVudG9yeS5tZCIsImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwibGluZSI6MTEzLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJsb2dpYy1lcnJvciIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzgzMGU5ZjhlMzQwNzMxY2MiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImRvYy1zdHlsZSIsImZpbGUiOiJpbnRlcm5hbC9jb25maWcvaW50ZXJmYWNlc190ZXN0LmdvIiwiaWQiOiJmXzY4OGQwNDBjNmY0NThjNzIiLCJsaW5lIjozNTcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2d1aWRlcy91c2VyL3J1bm5pbmctYWdlbnRzLWxvY2FsbHkubWQiLCJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsImxpbmUiOjQwMCwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvZ3VpZGVzL3VzZXIvYnJpbmcteW91ci1vd24tYWdlbnQubWQiLCJpZCI6ImZfODJjZjNlYWZkOTFjMThkYyIsImxpbmUiOjE4MiwiYWN0aW9uYWJsZSI6dHJ1ZX1dLCJkaXNwb3NpdGlvbnMiOlt7ImlkIjoiZl84MDI1NDhlMDcwMjE2ZDY5Iiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzk4YWEzODM5MDYxMmQxZWIiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfNTI0YTdmYzA1MmIwNzJiNCIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl85ODkwOTliZDU1ZGYxZGVmIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmX2FiOTYzNmY3NGY3OTkyOWQiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfMzc4N2MxZjhmOWMxN2FmMyIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl83N2YyZDNiMDJmMmFjYTQwIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzcxMmI2MTBiNzc0M2JkYWUiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl82ODhkMDQwYzZmNDU4YzcyIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzgyY2YzZWFmZDkxYzE4ZGMiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifV19 -->

Review

Findings

Medium

  • [incomplete-doc] docs/reference/config-reference.md:292 — The mint_url entry does not describe the runtime behavior this PR adds: precedence --mint-url flag > config.yaml/config.base.yaml > FULLSEND_MINT_URL for fullsend run, the status notifier and reconcile-status, or the local-run warn-and-skip when GitHub Actions OIDC is unavailable. AGENTS.md names this page the canonical config reference; the PR updates four other docs but not this one.
    Remediation: Document the runtime precedence and the local-run OIDC downgrade in the mint_url entry.

Low

  • [logic-error] internal/cli/run.go:6372 — The local-run OIDC downgrade is not propagated to status notifications. setupStatusNotifierGitHub re-resolves the file-sourced URL and installs a client factory that attempts the unavailable OIDC exchange, so local runs with --status-repo/--status-number emit a status-mint failure per call instead of disabling notifications alongside the skipped agent mint. The PR's own inventory row records this as tracked, not yet done.
    Remediation: Apply the same file-source/local-run downgrade when setting up GitHub status notifications, and add a test.
  • [missing-doc] docs/cli/run.md:17 — The Flags table omits --mint-url, whose default resolution this PR changes.
    Remediation: Add --mint-url to the Flags table with its layered default resolution.
  • [secret-exposure] internal/cli/run.go:6830 — A file-sourced mint_url now overrides FULLSEND_MINT_URL, and the destination receives the Actions OIDC JWT as a bearer credential; no endpoint-trust check applies beyond HTTPS. Exploiting it requires control of the trusted config checkout, so impact is limited. Advisory.
  • [fail-open] internal/cli/run.go:916 — In a non-Actions environment with a file-only mint_url, no FULLSEND_MINT_URL and no OIDC variables, minting is skipped and the ambient GH_TOKEN is used, and stage reminting becomes a no-op. This matches the previous behavior for that case and is guarded by three conditions plus a warning. Advisory.
  • [breaking-cli] internal/cli/reconcilestatus.go:191 — reconcile-status error texts changed, and a file mint_url now beats FULLSEND_MINT_URL in run, the status notifier and reconcile-status. A repo that sets both to different values will switch endpoints. This matches the issue's intent. Worth a release note.
  • [doc-style] internal/cli/run_test.go:8285 — The doc comment for TestRunAgent_MintURLFlagFailsClosedWithoutOIDC is stacked onto the comment of TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet, so the former test (line 8385) has no comment and the latter is misdocumented.

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)

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImluY29tcGxldGUtZG9jIiwiZmlsZSI6ImRvY3MvcmVmZXJlbmNlL2NvbmZpZy1yZWZlcmVuY2UubWQiLCJpZCI6ImZfODAyNTQ4ZTA3MDIxNmQ2OSIsImxpbmUiOjI5MiwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoibG9naWMtZXJyb3IiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85OGFhMzgzOTA2MTJkMWViIiwibGluZSI6NjM3MiwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6Im1pc3NpbmctZG9jIiwiZmlsZSI6ImRvY3MvY2xpL3J1bi5tZCIsImlkIjoiZl81MjRhN2ZjMDUyYjA3MmI0IiwibGluZSI6MTcsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJzZWNyZXQtZXhwb3N1cmUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85ODkwOTliZDU1ZGYxZGVmIiwibGluZSI6NjgzMCwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImJyZWFraW5nLWNsaSIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcmVjb25jaWxlc3RhdHVzLmdvIiwiaWQiOiJmX2FiOTYzNmY3NGY3OTkyOWQiLCJsaW5lIjoyMDIsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJkb2Mtc3R5bGUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bl90ZXN0LmdvIiwiaWQiOiJmXzM3ODdjMWY4ZjljMTdhZjMiLCJsaW5lIjo4Mjg1LCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZmFpbC1vcGVuIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNzdmMmQzYjAyZjJhY2E0MCIsImxpbmUiOjkxNywiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfNzEyYjYxMGI3NzQzYmRhZSIsImxpbmUiOjYxMDcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2NvbnRyaWJ1dGluZy9jb25maWctc291cmNlLWludmVudG9yeS5tZCIsImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwibGluZSI6MTEzLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJsb2dpYy1lcnJvciIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzgzMGU5ZjhlMzQwNzMxY2MiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcnVuLmdvIiwiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJsaW5lIjo5MDEsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImRvYy1zdHlsZSIsImZpbGUiOiJpbnRlcm5hbC9jb25maWcvaW50ZXJmYWNlc190ZXN0LmdvIiwiaWQiOiJmXzY4OGQwNDBjNmY0NThjNzIiLCJsaW5lIjozNTcsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6InN0YWxlLWRvYyIsImZpbGUiOiJkb2NzL2d1aWRlcy91c2VyL3J1bm5pbmctYWdlbnRzLWxvY2FsbHkubWQiLCJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsImxpbmUiOjQwMCwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvZ3VpZGVzL3VzZXIvYnJpbmcteW91ci1vd24tYWdlbnQubWQiLCJpZCI6ImZfODJjZjNlYWZkOTFjMThkYyIsImxpbmUiOjE4MiwiYWN0aW9uYWJsZSI6dHJ1ZX1dLCJkaXNwb3NpdGlvbnMiOlt7ImlkIjoiZl85OGFhMzgzOTA2MTJkMWViIiwic3RhdHVzIjoicmVjbGFzc2lmaWVkIn0seyJpZCI6ImZfODAyNTQ4ZTA3MDIxNmQ2OSIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl81MjRhN2ZjMDUyYjA3MmI0Iiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzk4OTA5OWJkNTVkZjFkZWYiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfYWI5NjM2Zjc0Zjc5OTI5ZCIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl8zNzg3YzFmOGY5YzE3YWYzIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzcxMmI2MTBiNzc0M2JkYWUiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl82ODhkMDQwYzZmNDU4YzcyIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzgyY2YzZWFmZDkxYzE4ZGMiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifV19 -->

Review

Findings

Medium

  • [incomplete-doc] docs/reference/config-reference.md:292 — The ### mint_url section still gives only the compiled default and install-time effect. It omits the runtime resolution this PR adds for fullsend run and reconcile-status: flag, then layered config.yaml/config.base.yaml, then FULLSEND_MINT_URL; no compiled-default fallback at runtime; warn-and-skip minting for local runs without Actions OIDC.
    Remediation: Extend the ### mint_url section with the runtime precedence, the absence of a compiled-default fallback at runtime, and the local warn-and-skip behavior (or link to config-source-inventory.md / running-agents-locally.md).

Low

  • [missing-doc] docs/cli/run.md:17 — The Flags table does not list --mint-url or its resolution order.
    Remediation: Add --mint-url to the Flags table with its purpose and resolution order.
  • [secret-exposure] internal/cli/run.go:6830 — resolveMintURL lets a file-sourced mint_url choose the endpoint that receives the Actions OIDC token; this relies on .fullsend being read from a trusted, pinned checkout. Unchanged since the prior review.
    Remediation: Keep reading .fullsend only from a trusted checkout; consider validating scheme/host of mint_url.
  • [breaking-cli] internal/cli/reconcilestatus.go:202 — The missing-mint-URL error text changed and layered config mint_url now outranks FULLSEND_MINT_URL. Intended by mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192, but consumers parsing the old message or setting both may see different behavior.
    Remediation: Mention the precedence change in release notes.
  • [doc-style] internal/cli/run_test.go:8285 — The doc comment for TestRunAgent_MintURLFlagFailsClosedWithoutOIDC sits above the comment for TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet, leaving the former undocumented.
    Remediation: Move lines 8285-8289 directly above func TestRunAgent_MintURLFlagFailsClosedWithoutOIDC.
  • [logic-error] internal/cli/run.go:6372 — setupStatusNotifierGitHub does not apply runAgent's local OIDC downgrade, so in a local file-only mint_url run without OIDC, status-comment mint attempts fail and warn. Tracked as a known limitation in config-source-inventory.md; reclassified from medium to low (warning noise only).
    Remediation: Share the effective minting decision between agent-token and status-token setup and add a test with status options set.
  • [fail-open] internal/cli/run.go:917 — For a file-only mint_url outside Actions with no OIDC env, the run warns and keeps the ambient GH_TOKEN. Explicit flag/env and Actions runs stay fail-closed, and this matches pre-PR local behavior; noted as a hardening opportunity.
    Remediation: Optionally require an explicit opt-in before falling back to the ambient token.

Earlier findings

  • PR-level, docs/guides/user/building-custom-agents.md — the only change since the prior review arrived via a merge of main (file is not in this PR's own file list); no new concerns.
  • internal/cli/run.go:6372, logic-error, reclassified medium to low — failed status mint is only a warning and the PR's docs record the limitation (config-source-inventory.md).

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)

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfOThhYTM4MzkwNjEyZDFlYiIsImxpbmUiOjYzNzIsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibWVkaXVtIiwiY2F0ZWdvcnkiOiJpbmNvbXBsZXRlLWRvYyIsImZpbGUiOiJkb2NzL3JlZmVyZW5jZS9jb25maWctcmVmZXJlbmNlLm1kIiwiaWQiOiJmXzgwMjU0OGUwNzAyMTZkNjkiLCJsaW5lIjoyOTIsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6Im1pc3NpbmctZG9jIiwiZmlsZSI6ImRvY3MvY2xpL3J1bi5tZCIsImlkIjoiZl81MjRhN2ZjMDUyYjA3MmI0IiwibGluZSI6MTcsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJzZWNyZXQtZXhwb3N1cmUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl85ODkwOTliZDU1ZGYxZGVmIiwibGluZSI6NjgzMCwiYWN0aW9uYWJsZSI6ZmFsc2V9LHsic2V2ZXJpdHkiOiJsb3ciLCJjYXRlZ29yeSI6ImJyZWFraW5nLWNsaSIsImZpbGUiOiJpbnRlcm5hbC9jbGkvcmVjb25jaWxlc3RhdHVzLmdvIiwiaWQiOiJmX2FiOTYzNmY3NGY3OTkyOWQiLCJsaW5lIjoyMDIsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJkb2Mtc3R5bGUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bl90ZXN0LmdvIiwiaWQiOiJmXzM3ODdjMWY4ZjljMTdhZjMiLCJsaW5lIjo4Mjg1LCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6Im1lZGl1bSIsImNhdGVnb3J5IjoibG9naWMtZXJyb3IiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl83MTJiNjEwYjc3NDNiZGFlIiwibGluZSI6NjEwNywiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6Im1lZGl1bSIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvY29udHJpYnV0aW5nL2NvbmZpZy1zb3VyY2UtaW52ZW50b3J5Lm1kIiwiaWQiOiJmXzVjZGE1YThhNTE1NjMzZDAiLCJsaW5lIjoxMTMsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsImxpbmUiOjkwMSwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6Im1lZGl1bSIsImNhdGVnb3J5IjoiZmFpbC1vcGVuIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfOTAwMzE1NDc1YmU3ZDE3OCIsImxpbmUiOjkwMSwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZG9jLXN0eWxlIiwiZmlsZSI6ImludGVybmFsL2NvbmZpZy9pbnRlcmZhY2VzX3Rlc3QuZ28iLCJpZCI6ImZfNjg4ZDA0MGM2ZjQ1OGM3MiIsImxpbmUiOjM1NywiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvZ3VpZGVzL3VzZXIvcnVubmluZy1hZ2VudHMtbG9jYWxseS5tZCIsImlkIjoiZl8zZGFhYTExYzk4NGRiNzQ4IiwibGluZSI6NDAwLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJzdGFsZS1kb2MiLCJmaWxlIjoiZG9jcy9ndWlkZXMvdXNlci9icmluZy15b3VyLW93bi1hZ2VudC5tZCIsImlkIjoiZl84MmNmM2VhZmQ5MWMxOGRjIiwibGluZSI6MTgyLCJhY3Rpb25hYmxlIjp0cnVlfV0sImRpc3Bvc2l0aW9ucyI6W3siaWQiOiJmXzM3ODdjMWY4ZjljMTdhZjMiLCJzdGF0dXMiOiJvcGVuIn0seyJpZCI6ImZfOTg5MDk5YmQ1NWRmMWRlZiIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl9hYjk2MzZmNzRmNzk5MjlkIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzcxMmI2MTBiNzc0M2JkYWUiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzkwMDMxNTQ3NWJlN2QxNzgiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl82ODhkMDQwYzZmNDU4YzcyIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzgyY2YzZWFmZDkxYzE4ZGMiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifV19 -->

Review

Findings

Medium

  • [logic-error] internal/cli/run.go:6372 — File-only mint configuration now enables status comments (setupStatusNotifierGitHub resolves the mint URL via resolveMintURL), but the composite action's "Finalize orphaned status comment" step is still gated on a nonempty inputs.mint-url, and the "Run fullsend" step omits --mint-url when that input is empty. The PR's own config-source-inventory.md row says empty-mint-url callers (the prioritize paths) already mint and notify from a file-only mint_url, but does not mention that the finalizer is skipped for them. A Started comment created through a file-only mint_url is never finalized after SIGKILL/OOM, and adding file resolution to reconcile-status does not reach this caller path.
    Remediation: Preferred: in action.yml drop the inputs.mint-url != '' condition from the finalizer (keep the status-repo/status-number checks), add --mint-url to the reconcile flags only when nonempty, and keep passing --fullsend-dir (action.yml is a protected path and needs human/workflows-capable follow-up). Within this PR, record the gap as tracked follow-up in the mint_url row of docs/contributing/config-source-inventory.md.
  • [incomplete-doc] docs/reference/config-reference.md:292 — The mint_url section lists Default: https://mint.fullsend.sh and describes only install-time behavior. This PR makes fullsend run and reconcile-status read mint_url from config.yaml/config.base.yaml at runtime with precedence flag > file > FULLSEND_MINT_URL, so a file value now overrides the repository variable, and when mint_url is omitted the runtime does not use the compiled default (warn-and-skip). The canonical config reference does not describe this and the PR does not touch it.
    Remediation: Update the mint_url section to state the runtime precedence and that omitting mint_url does not fall back to the hosted default at run time.

Low

  • [secret-exposure] internal/cli/run.go:6830 — The file-sourced mint endpoint is accepted without a trusted-destination check and overrides FULLSEND_MINT_URL when --mint-url is absent. A malicious config.yaml/config.base.yaml reaching an OIDC-enabled run can send the GitHub Actions OIDC JWT to an attacker-controlled HTTPS endpoint. Exploiting this requires control of config consumed by the privileged run, hence low.
    Remediation: Validate file-sourced mint destinations against a trusted policy or require explicit approval before sending OIDC credentials to a repository-config-introduced destination.
  • [breaking-cli] internal/cli/reconcilestatus.go:202 — reconcileGitHubClient's error messages changed (--mint-url or FULLSEND_MINT_URL required now has new text; --role is required when using --mint-url became --role is required to mint a token) and flag help changed. Mint precedence is now flag > layered config > env, so reconcile-status can pick a different endpoint when --fullsend-dir holds a mint_url. Impact is small and intentional.
    Remediation: Optional: note the change in release notes.
  • [missing-doc] docs/cli/run.md:17 — The Flags table does not list --mint-url. The omission predates this PR, but the PR changes how the flag resolves (flag > config file > FULLSEND_MINT_URL), so the gap now hides more behavior.
    Remediation: Add --mint-url to the Flags table with the precedence flag > config file > FULLSEND_MINT_URL.

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)

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJhcHByb3ZlIiwiZmluZGluZ3MiOlt7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJkb2Mtc3R5bGUiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bl90ZXN0LmdvIiwiaWQiOiJmXzM3ODdjMWY4ZjljMTdhZjMiLCJsaW5lIjo4Mjg1LCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic2VjcmV0LWV4cG9zdXJlIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfOTg5MDk5YmQ1NWRmMWRlZiIsImxpbmUiOjY4MDUsImFjdGlvbmFibGUiOmZhbHNlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJicmVha2luZy1jbGkiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3JlY29uY2lsZXN0YXR1cy5nbyIsImlkIjoiZl9hYjk2MzZmNzRmNzk5MjlkIiwibGluZSI6MjAyLCJhY3Rpb25hYmxlIjpmYWxzZX0seyJzZXZlcml0eSI6Im1lZGl1bSIsImNhdGVnb3J5IjoibG9naWMtZXJyb3IiLCJmaWxlIjoiaW50ZXJuYWwvY2xpL3J1bi5nbyIsImlkIjoiZl83MTJiNjEwYjc3NDNiZGFlIiwibGluZSI6NjEwNywiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6Im1lZGl1bSIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvY29udHJpYnV0aW5nL2NvbmZpZy1zb3VyY2UtaW52ZW50b3J5Lm1kIiwiaWQiOiJmXzVjZGE1YThhNTE1NjMzZDAiLCJsaW5lIjoxMTMsImFjdGlvbmFibGUiOnRydWV9LHsic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImxvZ2ljLWVycm9yIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfODMwZTlmOGUzNDA3MzFjYyIsImxpbmUiOjkwMSwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6Im1lZGl1bSIsImNhdGVnb3J5IjoiZmFpbC1vcGVuIiwiZmlsZSI6ImludGVybmFsL2NsaS9ydW4uZ28iLCJpZCI6ImZfOTAwMzE1NDc1YmU3ZDE3OCIsImxpbmUiOjkwMSwiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5IjoiZG9jLXN0eWxlIiwiZmlsZSI6ImludGVybmFsL2NvbmZpZy9pbnRlcmZhY2VzX3Rlc3QuZ28iLCJpZCI6ImZfNjg4ZDA0MGM2ZjQ1OGM3MiIsImxpbmUiOjM1NywiYWN0aW9uYWJsZSI6dHJ1ZX0seyJzZXZlcml0eSI6ImxvdyIsImNhdGVnb3J5Ijoic3RhbGUtZG9jIiwiZmlsZSI6ImRvY3MvZ3VpZGVzL3VzZXIvcnVubmluZy1hZ2VudHMtbG9jYWxseS5tZCIsImlkIjoiZl8zZGFhYTExYzk4NGRiNzQ4IiwibGluZSI6NDAwLCJhY3Rpb25hYmxlIjp0cnVlfSx7InNldmVyaXR5IjoibG93IiwiY2F0ZWdvcnkiOiJzdGFsZS1kb2MiLCJmaWxlIjoiZG9jcy9ndWlkZXMvdXNlci9icmluZy15b3VyLW93bi1hZ2VudC5tZCIsImlkIjoiZl84MmNmM2VhZmQ5MWMxOGRjIiwibGluZSI6MTgyLCJhY3Rpb25hYmxlIjp0cnVlfV0sImRpc3Bvc2l0aW9ucyI6W3siaWQiOiJmXzgzMGU5ZjhlMzQwNzMxY2MiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl85MDAzMTU0NzViZTdkMTc4Iiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfOTg5MDk5YmQ1NWRmMWRlZiIsInN0YXR1cyI6Im9wZW4ifSx7ImlkIjoiZl82ODhkMDQwYzZmNDU4YzcyIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn0seyJpZCI6ImZfM2RhYWExMWM5ODRkYjc0OCIsInN0YXR1cyI6InJlc29sdmVkX2J5X2NoYW5nZSJ9LHsiaWQiOiJmXzgyY2YzZWFmZDkxYzE4ZGMiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl9hYjk2MzZmNzRmNzk5MjlkIiwic3RhdHVzIjoib3BlbiJ9LHsiaWQiOiJmXzcxMmI2MTBiNzc0M2JkYWUiLCJzdGF0dXMiOiJyZXNvbHZlZF9ieV9jaGFuZ2UifSx7ImlkIjoiZl81Y2RhNWE4YTUxNTYzM2QwIiwic3RhdHVzIjoicmVzb2x2ZWRfYnlfY2hhbmdlIn1dfQ== -->

Review

Findings

Low

  • [doc-style] internal/cli/run_test.go:8285 — The doc comment for TestRunAgent_MintURLFlagFailsClosedWithoutOIDC (lines 8285-8289) was separated from its function when the two new TestRunAgent_MintURLFromFileFailsClosed* tests were inserted directly after it. Those lines now run into the doc comment of TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet, and TestRunAgent_MintURLFlagFailsClosedWithoutOIDC (line 8385) has no doc comment.
    Remediation: Move lines 8285-8289 to sit directly above func TestRunAgent_MintURLFlagFailsClosedWithoutOIDC.
  • [secret-exposure] internal/cli/run.go:6805 — (f_989099bd55df1def) A file-configured mint_url overrides FULLSEND_MINT_URL and receives the Actions OIDC bearer JWT through mintclient.MintToken. The client enforces HTTPS except for localhost but does not restrict destination hosts. No direct URL or token logging occurs in resolveMintURL or the new downgrade warning. This is a non-actionable trust-boundary observation, not a demonstrated leak.
    Remediation: Keep both configuration layers on the trusted configuration ref. Any future support for untrusted config must independently constrain credential destinations.
  • [breaking-cli] internal/cli/reconcilestatus.go:202 — (f_ab9636f74f79929d) reconcile-status now resolves the mint URL as --mint-url, then layered config.yaml/config.base.yaml mint_url from --fullsend-dir, then FULLSEND_MINT_URL. It previously used only the flag and the env var. The missing-URL error text also changed. This is intended per mint_url in .fullsend/config.yaml does not reach workflows or fullsend run #8192. action.yml always passes --fullsend-dir and --mint-url "${MINT_URL}", so an empty MINT_URL now lets the file layer apply ahead of the env var.

Earlier findings

  • f_830e9f8e340731cc — resolved_by_change: the runAgent downgrade now also requires FULLSEND_MINT_URL to be empty and GITHUB_ACTIONS != "true", with two new regression tests.
  • f_900315475be7d178 — resolved_by_change: the same narrowed condition keeps a shadowed env URL and Actions jobs missing OIDC fail-closed.
  • f_688d040c6f458c72 — resolved_by_change: the doc comment above TestPerRepoConfig_MintURLFromFile_Fallback was reworded and the typo fixed.
  • f_3daaa11c984db748 — resolved_by_change: docs/guides/user/running-agents-locally.md now describes the layered --mint-url default.
  • f_82cf3eafd91c18dc — resolved_by_change: docs/guides/user/bring-your-own-agent.md now lists the config file as a mint URL source and the OIDC-unavailable skip.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:23 AM UTC · Completed 9:35 AM UTC

Commit: 273f9aa · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

… notifier

setupStatusNotifier loaded config via tryLoadFullsendConfig, which
stats config.yaml and returns nil when that overlay is absent,
never consulting config.base.yaml. runAgent instead resolves
mint_url through loadLockConfig, which honors a base-only layout.
In a base-only repo the agent would mint with the base file's
mint_url while the notifier fell back to FULLSEND_MINT_URL (a
possibly different mint) or failed with 'no mint URL available'.

Add tryLoadLayeredFullsendConfig, a best-effort loader that calls
config.LoadConfigWriter directly (like loadLockConfig) instead of
gating on config.yaml's existence, and wire it into
setupStatusNotifier.

Add tests: tryLoadLayeredFullsendConfig unit tests (base-only,
neither file, overlay-wins, malformed base) and two
setupStatusNotifier tests covering a config.base.yaml-only
mint_url winning over FULLSEND_MINT_URL, with and without that
env var set. Verified both new notifier tests fail without the
fix and pass with it.

Signed-off-by: fmuntean <fmuntean@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:23 AM UTC · Completed 10:33 AM UTC

Commit: 4fcebc0 · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

Comment thread internal/cli/run.go Outdated
Comment thread internal/cli/run.go Outdated
@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:21 PM UTC · Completed 12:38 PM UTC

Commit: 19776ff · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

… and reconcile-status

- runAgent: a mint_url sourced only from config.yaml/config.base.yaml
  now warns and skips minting (falling back to the ambient GH_TOKEN)
  when GitHub Actions OIDC env is unavailable, instead of hard-failing
  local runs. An explicit --mint-url flag or FULLSEND_MINT_URL still
  fails closed.
- reconcile-status now resolves mint_url through the same
  resolveMintURL precedence as fullsend run (flag > layered file >
  env) when --fullsend-dir is given, so the two agree on a mint
  endpoint within the same CI job.
- Update config-source-inventory.md and cli-internals.md to match.

Signed-off-by: fmuntean <fmuntean@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 1:33 PM UTC · Ended 1:47 PM UTC

Commit: 1a2084b · View workflow run →

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Site preview

Preview: https://5052117b-site.fullsend-ai.workers.dev

Commit: 29713b072ee30d153654dec13dbaa3067a7db480

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:33 PM UTC · Completed 1:47 PM UTC

Commit: 1a2084b · View workflow run →

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

Comment thread docs/contributing/config-source-inventory.md Outdated
@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:59 PM UTC · Completed 4:09 PM UTC

Commit: ca8412c · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:35 PM UTC · Completed 4:45 PM UTC

Commit: 122e21d · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[MEDIUM] Fixes #8192 will auto-close an issue whose reported repro still fails (PR description)

The PR body says Fixes #8192. The repro in #8192 is: leave FULLSEND_MINT_URL unset, set mint_url in .fullsend/config.yaml, then trigger /fs-triage. That still fails at head 122e21d.

In reusable-dispatch.yml, the triage job (line ~707) and the code/review/fix/retro/harness-run jobs (lines 818, 949, 1080, 1342, 1738) run .github/actions/mint-token with mint_url: ${{ inputs.mint_url }} before the run step. When that input is empty, mint-token/action.yml exits 1 with ::error::FULLSEND_MINT_URL is not set. The PR's own Scope section says these stages stay as they are until a follow-up teaches the route job to compute an effective mint URL.

#8192's "What should happen" asks for workflow dispatch to work, as well as fullsend run, and #8192 blocks #4977. Merging as written closes the issue while its main path is still broken, and unblocks #4977 too early.

Suggestion: Change Fixes #8192 to Part of #8192 (or Refs #8192). Keep #8192 open, or file the route-job/workflow follow-up as a sub-issue that #8192 depends on.

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 2:14 PM UTC · Ended 2:39 PM UTC

Commit: 20786da · View workflow run →

@mfrancisc

Copy link
Copy Markdown
Contributor Author

[MEDIUM] Fixes #8192 will auto-close an issue whose reported repro still fails (PR description)

@waynesun09 I've added a blocked by #8672 link in #8192 so it doesn't autoclose when this pr is merged. but only after we address #8672 ( which was next on my list ).

fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:14 PM UTC · Completed 2:39 PM UTC

Commit: 20786da · View workflow run →

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

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 7:36 PM UTC · Ended 7:48 PM UTC

Commit: ff16a0b · View workflow run →

This review was superseded by a newer commit. Check the current pull request HEAD's review status before merging.

… conflict

A merge from main left resolveBuiltInGitHubReviewCancellationGuidance's
call missing its statusForgeClient argument (stray blank lines and a
bare comma instead), breaking the build. Restores the argument.

Signed-off-by: fmuntean <fmuntean@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

Copy link
Copy Markdown

/fs-review

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:50 PM UTC · Completed 8:04 PM UTC

Commit: 29713b0 · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

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

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:59 AM UTC · Completed 10:19 AM UTC

Commit: f5ccbb9 · View workflow run →

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

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

Note: The following review comments could not be posted on the diff (GitHub returned 422) and are included here instead:

  • internal/cli/run.go (file-level): Line 1402 ·
    [medium] logic-error

The cancelled-review finalizer passes the raw sOpts.mintURL to newStatusGitHubForgeClient, which falls back only to FULLSEND_MINT_URL and never consults the layered config. setupStatusNotifierGitHub resolves the endpoint through resolveMintURL (file wins), so with a file-only mint_url the start comment is posted but the cancellation finalizer mints against an empty URL and returns without finalizing; with a competing env value it mints against the env endpoint instead of the file-selected one. The reconcile-status always() step partly covers this, hence medium.

Suggested fix: Use the resolved mint endpoint (resolveMintURL with the loaded layered config) for cancellation finalization, or reuse the notifier's configured client factory. Add cancellation regression tests for a file-only mint_url and for a file value competing with a different FULLSEND_MINT_URL.

  • internal/cli/run.go (file-level): Line 467 ·
    [low] design-direction

The fullsend run --mint-url flag help still reads (default: $FULLSEND_MINT_URL) and describes status tokens only, while the flag now also governs agent token minting and precedence is flag > layered config file > FULLSEND_MINT_URL. The reconcile-status flag help was updated in this PR.

Suggested fix: Update the --mint-url help text in run.go to describe agent and status token minting and the flag > config file > FULLSEND_MINT_URL precedence.

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

your `GH_TOKEN`), sets up the providers on the OpenShell gateway, and starts
the sandbox. For GitHub and OpenAI the agent never holds a real credential:
local run with no mint URL from `--mint-url`, `config.yaml`/`config.base.yaml`,
or `FULLSEND_MINT_URL` — or where GitHub Actions OIDC isn't available —

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] api-contract

The doc states that a run where GitHub Actions OIDC is unavailable skips minting and uses GH_TOKEN, with no qualification. runAgent only downgrades when the URL came solely from the config file, FULLSEND_MINT_URL is unset and GITHUB_ACTIONS is not true; an explicit --mint-url or FULLSEND_MINT_URL (even one shadowed by the file) still attempts minting and fails closed without OIDC.

Suggested fix: Restrict the OIDC-unavailable fallback wording to a file-only mint_url outside GitHub Actions, and state that an explicit flag or environment value stays fail-closed.

Comment thread internal/cli/run.go
if mintURL == "" {
mintURL = os.Getenv("FULLSEND_MINT_URL")
// Precedence: flag > config.yaml/config.base.yaml mint_url > env (#8192).
mintURL, mintSource := resolveMintURL(sOpts.mintURL, orgCfg, os.Getenv)

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] under-engineering

Mint URL resolution and the local-run OIDC downgrade in runAgent are inline, while setupStatusNotifierGitHub re-resolves the URL independently after a separate config load, and the cancellation path at run.go:1402 resolves a third way. The divergence is what produces the cancellation finding above; config-source-inventory.md acknowledges the OIDC downgrade is not yet extended to status comments.

Comment thread internal/cli/run.go
func setupStatusNotifierGitHub(notifyCfg config.StatusNotificationConfig, orgCfg config.ConfigReader, owner, repo, role string, sOpts statusOpts, printer *ui.Printer) (*statuscomment.Notifier, error) {
mintURL, _ := resolveMintURL(sOpts.mintURL, orgCfg, os.Getenv)
if mintURL == "" {
return nil, fmt.Errorf("no mint URL available (set --mint-url or FULLSEND_MINT_URL)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] error-handling-idiom

The error returned by setupStatusNotifierGitHub when no mint URL resolves still says (set --mint-url or FULLSEND_MINT_URL), omitting the config.yaml/config.base.yaml source now consulted by resolveMintURL. reconcileGitHubClient lists all three sources.

Suggested fix: Update the message to name the layered mint_url in config.yaml/config.base.yaml alongside --mint-url and FULLSEND_MINT_URL, matching reconcileGitHubClient.

Comment thread internal/cli/run.go
os.Getenv("GITHUB_ACTIONS") != "true" &&
!hasActionsOIDCEnv(os.Getenv) {
mintSkipWarning = "config.yaml/config.base.yaml sets mint_url for a local run with no FULLSEND_MINT_URL, but GitHub Actions OIDC is unavailable; skipping token minting for role " + h.Role
mintURL = ""

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] fail-open

When mint_url comes only from the config file, FULLSEND_MINT_URL is empty, GITHUB_ACTIONS is not true and the OIDC request variables are absent, runAgent clears mintURL and continues with the ambient GH_TOKEN, which also skips pre/post-script privilege reminting. This matches the base-branch behavior for the same inputs (the file was never consulted, so the run warned and skipped minting), and flag/env/Actions cases still fail closed, so it is not a regression. Design note: a file-set mint_url is silently ignored outside Actions, and the local-run test depends on GITHUB_ACTIONS, which the caller's environment controls.

Comment thread internal/cli/run_test.go

// TestRunAgent_MintURLFlagFailsClosedWithoutOIDC confirms the other half of
// the #8400 follow-up fix: unlike a file-sourced mint_url, an explicit
// --mint-url flag (or FULLSEND_MINT_URL) is a deliberate signal from the

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] doc-style

The doc comment for TestRunAgent_MintURLFlagFailsClosedWithoutOIDC (about lines 8455-8459) sits directly above TestRunAgent_MintURLFromFileFailsClosedWhenEnvAlsoSet's own comment, leaving the function at about line 8555 without a doc comment.

Comment thread internal/cli/run.go
// resolveMintURL resolves the token mint URL with the precedence order
// documented in docs/contributing/config-source-inventory.md's mint_url
// row: an explicit flag value wins outright (one-run override); then
// config.yaml/config.base.yaml's mint_url ("file should win" over the

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] backward-incompatible

resolveMintURL now ranks a config-file mint_url above FULLSEND_MINT_URL in fullsend run, where the env var was previously the only source. A repo with both set to different values now mints against the file's URL. This is the behavior issue #8192 requests and is documented, but there is no release-note callout.

Suggested fix: Mark the change as a behavior change in the release notes.

This branch was successfully deployed

1 active (outdated) deployment
site-preview — 29713b07 Deployed Oct 26, 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/mint Token mint and cross-boundary credentials component/runner Agent runner behavior and lifecycle go Pull requests that update go code risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mint_url in .fullsend/config.yaml does not reach workflows or fullsend run

3 participants