Skip to content

feat(#5633): add --platform=cloudflare to mint enroll/unenroll - #6137

Open
fullsend-ai-coder[bot] wants to merge 3 commits into
mainfrom
agent/5633-cf-enroll-unenroll
Open

feat(#5633): add --platform=cloudflare to mint enroll/unenroll#6137
fullsend-ai-coder[bot] wants to merge 3 commits into
mainfrom
agent/5633-cf-enroll-unenroll

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Adds --platform=cloudflare support to mint enroll and mint unenroll commands, enabling org and per-repo enrollment against durable Cloudflare Workers. This mirrors the existing platform flag on mint deploy and mint delete.

Related Issue

Closes #5633

Changes

  • WranglerRunner interface (internal/dispatch/cf/provisioner.go): Added GetVars (reads Worker vars via CF API) and HasPreviewVersions (detects preview aliases via wrangler versions list)
  • CF Provisioner (internal/dispatch/cf/provisioner.go): Added EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, RemoveRepoFromWorker methods that read-modify-write Worker vars via a durable redeploy with --keep-vars
  • CLI enroll command (internal/cli/mint.go): Added --platform (gcp|cloudflare), --worker-name, and --preview (rejected) flags with platform routing
  • CLI unenroll command (internal/cli/mint.go): Same flag additions with platform routing, including confirmation prompts for CF path
  • Help text: Documents CF credentials, the mutable-vs-preview model, and --preview rejection semantics
  • WIF/STS steps remain GCP-only; CF path does not invent WIF
  • --preview on enroll/unenroll is rejected with a clear error pointing to mint deploy
  • Preview version detection warns (does not fail) when preview versions exist on the durable Worker

Testing

  • Unit tests for CF provisioner methods (EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, RemoveRepoFromWorker, ParseWorkerSettingsVars, ParseHasPreviewVersions)
  • CLI tests for --platform=cloudflare on enroll/unenroll (dry-run, success, preview rejection, worker-not-found, preview warning, unsupported platform)
  • All existing GCP enroll/unenroll tests pass unchanged
  • go vet, go build ./... pass
  • Secret scan clean

Closes #5633

Post-script verification

  • Branch is not main/master (agent/5633-cf-enroll-unenroll)
  • Secret scan passed (gitleaks — 55bd9f61ce57f9f2151a29d59a04d42b664110cf..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 12, 2026 09:19
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Agent PR ready for human review label Aug 12, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:20 AM UTC · Completed 9:40 AM UTC

Commit: 070aff4 · View workflow run →

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.20516% with 131 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/cli/mint.go 77.77% 45 Missing and 29 partials ⚠️
internal/dispatch/cf/provisioner.go 84.34% 31 Missing and 26 partials ⚠️

📢 Thoughts on this report? Let us know!

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review

Findings

Medium

Low

  • [race-condition] internal/dispatch/cf/provisioner.go:416 — The enroll/unenroll Cloudflare path uses a read-modify-write cycle (GetVars → merge → UpdateVars) without concurrency control. Two concurrent commands targeting the same Worker will race, and one change may be silently lost. This is an accepted CLI limitation matching the GCP path's behavior, documented in the CLI help text, docs/guides/infrastructure/mint-administration.md ("Enroll serially" callout), and the "Enrollment ordering" section. See also: security dimension identified this as an authorization-list mutation without atomicity; severity is proportionate to the documented, accepted limitation.

  • [scope-creep] docs/guides/getting-started/operations.md:109 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows (deploy, delete, add-role, remove-role, status) still use "GCP Admin (Mint)", creating a visual inconsistency within the same table.
    Remediation: Either rename all multi-platform mint commands to "Mint Admin" for consistency, or keep the original label.

  • [naming-consistency] internal/cli/mint.go:625 — Function name warnIrrelevantEnrollFlags follows a slightly different pattern than the existing warnIrrelevantFlags. These are separate functions because they have different flag sets (deploy/delete vs enroll/unenroll), so the naming is defensible, but the suffix style differs from the original.
    Remediation: Consider consolidating into a single parameterized helper, or accept the current naming as reflecting the distinct flag sets.


Resolved prior findings
  • [error-handling] (previously low) — Resolved. The deploy error in UpdateVars now includes the version ID in the error message: fmt.Errorf("deploying version %s: %%w", versionID, err).
Previous run

Review

Findings

Medium

Low

  • [race-condition] internal/dispatch/cf/provisioner.go:397 — The enroll/unenroll Cloudflare path uses a read-modify-write cycle (GetVars → merge → UpdateVars) without concurrency control. Two concurrent commands targeting the same Worker will race, and one change may be silently lost. This is an accepted CLI limitation matching the GCP path's behavior, documented in the CLI help text, docs/guides/infrastructure/mint-administration.md ("Enroll serially" callout), and the "Enrollment ordering" section.

  • [scope-creep] docs/guides/getting-started/operations.md:117 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows (deploy, delete, add-role, remove-role, status) still use "GCP Admin (Mint)", creating a visual inconsistency within the same table.
    Remediation: Either rename all mint-related rows to "Mint Admin" for consistency, or keep the original label.

  • [error-handling] internal/dispatch/cf/provisioner.go:1720 — In UpdateVars, if createVersionWithVars succeeds but deployVersionFn fails, the error message does not include the version ID of the orphaned version. Including the version ID would aid debugging.
    Remediation: Change the error format to include the version ID: fmt.Errorf("deploying version %s: %%w", versionID, err).


Resolved prior findings
  • [input-validation-gap] (previously low) — Resolved. runMintEnrollRepoCloudflare and runMintUnenrollRepoCloudflare now validate the repo slug via gcf.ValidateRepoSlug(repo) and check gcf.PlaceholderOrg.

  • [input-validation] (previously low) — Resolved. ResolveCloudflareAuth now validates CLOUDFLARE_ACCOUNT_ID via ValidateAccountID() (32 lowercase hex chars) on all three return paths.

  • [nondeterministic-output] (previously low) — Resolved. createVersionWithVars now sorts binding keys via sort.Strings(sortedKeys) before building the bindings array.

  • [code-duplication] (previously low) — Resolved. The shared prepareCFEnrollContext helper extracts the common preamble (worker name defaulting, provisioner construction, WorkerExists verification, and preview-version warning).

  • [stale-doc] (previously low, skills/mint-enroll/SKILL.md) — Resolved. The skill runbook now includes platform triage and Cloudflare enrollment sections.

  • [stale-doc] (previously low, docs/guides/dev/cli-internals.md) — Resolved. A footnote documents Cloudflare credential requirements for --platform=cloudflare.

Previous run (2)

Review

Findings

Low

  • [input-validation-gap] internal/cli/mint.go:1839 — The Cloudflare repo enroll/unenroll paths (runMintEnrollRepoCloudflare, runMintUnenrollRepoCloudflare) validate the owner via validateOrgName() but do not validate the repo slug portion via gcf.ValidateRepoSlug(repo). The GCP equivalents validate both. The repo slug is only written to a comma-separated env var string, so this is a defense-in-depth gap rather than an exploitable vulnerability.
    Remediation: Add gcf.ValidateRepoSlug(repo) validation in runMintEnrollRepoCloudflare and runMintUnenrollRepoCloudflare, matching the GCP path's validation pattern.

  • [race-condition] internal/dispatch/cf/provisioner.go:352 — The enroll/unenroll Cloudflare path uses a read-modify-write cycle (GetVars → merge → UpdateVars) without concurrency control. Two concurrent commands targeting the same Worker will race, and one change will be silently lost. The documentation acknowledges this as an accepted CLI limitation and instructs operators to run commands serially. The existing GCP codepath has the same accepted limitation with equivalent documentation mitigation.
    Remediation: Consider using Cloudflare's If-Match / ETag headers for optimistic concurrency control.

  • [input-validation] internal/dispatch/cf/provisioner.go:1241 — The accountID sourced from CLOUDFLARE_ACCOUNT_ID env var is interpolated into API URLs without format validation. The wrangler whoami path validates the 32-char hex format, but the env var path in ResolveCloudflareAuth returns the raw value without any format check. In practice, the Cloudflare API would reject malformed requests.
    Remediation: Add format validation on accountID (e.g., ^[a-f0-9]{32}$) in ResolveCloudflareAuth.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows (deploy, delete, add-role, remove-role, status) still use "GCP Admin (Mint)", creating a visual inconsistency within the same table.
    Remediation: Either rename all mint-related rows to "Mint Admin" for consistency, or keep the original label.

  • [code-duplication] internal/cli/mint.go — The four Cloudflare enroll/unenroll functions (runMintEnrollOrgCloudflare, runMintEnrollRepoCloudflare, runMintUnenrollOrgCloudflare, runMintUnenrollRepoCloudflare) repeat an identical ~20-line preamble: computing effectiveName, constructing wrangler + provisioner, verifying Worker existence, and checking preview versions.
    Remediation: Extract a shared helper that handles effectiveName defaulting, provisioner construction, WorkerExists verification, and the preview-versions warning.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused. An operator following this runbook for Cloudflare enrollment would get no guidance on platform selection, Worker name configuration, or Cloudflare-specific requirements.
    Remediation: Add a platform triage step and parallel Cloudflare enrollment sections, or note the runbook is GCP-only and direct users to CLI documentation.

  • [stale-doc] docs/guides/dev/cli-internals.md:134 — The Command Decomposition table describes mint enroll required access as GCP IAM roles only. With --platform=cloudflare, the required access is a Cloudflare API token + account ID, not GCP IAM roles.
    Remediation: Add a footnote noting that listed GCP IAM roles apply to --platform=gcp (default), and that Cloudflare enrollment requires CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID instead.

  • [nondeterministic-output] internal/dispatch/cf/provisioner.go:1357 — In createVersionWithVars, the bindings list is built by iterating over a map (vars), which produces nondeterministic ordering across Go map iterations. While the Cloudflare API accepts bindings in any order, nondeterministic output makes debugging harder.


Resolved prior findings
  • [race-condition] (previously medium) — Resolved. Serial-enrollment guidance has been added to docs/guides/infrastructure/mint-administration.md with an "Enroll serially" callout for the Cloudflare path and an updated "Enrollment ordering" section covering both GCP and Cloudflare.

  • [content-injection] (previously low) — Resolved. createVersionWithVars now validates module names against moduleNamePattern (^[a-zA-Z0-9._-]+$) before embedding them in Content-Disposition headers, rejecting names with invalid characters. A test (TestCreateVersionWithVars_InvalidModuleName) covers the injection case.

  • [silent-truncation] (previously low) — Resolved. Both the multipart and single-module code paths now probe for one additional byte after LimitReader yields maxWorkerModuleBytes of data, and return an explicit error if the module was truncated. Tests cover both truncation paths.

  • [stale-doc] (previously low, org-mode.md) — Resolved. The label has been updated from "GCP Admin (Mint)" to "Mint Admin".

  • [stale-doc] (previously low, repo-management.md) — Resolved. The label has been updated from "GCP Admin (Mint)" to "Mint Admin".

Previous run (3)

Review

Findings

Low

  • [input-validation-gap] internal/cli/mint.go — The Cloudflare enroll/unenroll repo paths (runMintEnrollRepoCloudflare, runMintUnenrollRepoCloudflare) validate the owner via validateOrgName() but do not validate the repo slug portion via gcf.ValidateRepoSlug(repo) or check gcf.PlaceholderOrg. The GCP equivalents validate both. The repo slug is only written to a comma-separated env var string (not used in URL paths or shell commands), so this is a defense-in-depth gap rather than an exploitable vulnerability.
    Remediation: Add gcf.ValidateRepoSlug(repo) and gcf.PlaceholderOrg checks in runMintEnrollRepoCloudflare and runMintUnenrollRepoCloudflare, matching the GCP path's validation.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows (deploy, delete, add-role, remove-role, status) still use "GCP Admin (Mint)", creating a visual inconsistency within the same table.
    Remediation: Either rename all mint-related rows to "Mint Admin" for consistency, or keep the original label.

  • [code-duplication] internal/cli/mint.go — The four Cloudflare enroll/unenroll functions (runMintEnrollOrgCloudflare, runMintEnrollRepoCloudflare, runMintUnenrollOrgCloudflare, runMintUnenrollRepoCloudflare) repeat an identical ~20-line preamble: computing effectiveName, constructing wrangler + provisioner, verifying Worker existence, and checking preview versions.
    Remediation: Extract a shared helper that handles effectiveName defaulting, provisioner construction, WorkerExists verification, and the preview-versions warning.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused. An operator following this runbook for Cloudflare enrollment would get no guidance on platform selection, Worker name configuration, or Cloudflare-specific requirements.
    Remediation: Add a platform triage step and parallel Cloudflare enrollment sections, or note the runbook is GCP-only and direct users to CLI documentation.


Resolved prior findings
  • [race-condition] (previously medium) — Resolved. Serial-enrollment guidance has been added to docs/guides/infrastructure/mint-administration.md with an "Enroll serially" callout for the Cloudflare path and an updated "Enrollment ordering" section covering both GCP and Cloudflare.

  • [content-injection] (previously low) — Resolved. createVersionWithVars now validates module names against moduleNamePattern (^[a-zA-Z0-9._-]+$) before embedding them in Content-Disposition headers, rejecting names with invalid characters. A test (TestCreateVersionWithVars_InvalidModuleName) covers the injection case.

  • [silent-truncation] (previously low) — Resolved. Both the multipart and single-module code paths now probe for one additional byte after LimitReader yields maxWorkerModuleBytes of data, and return an explicit error if the module was truncated. Tests cover both truncation paths.

  • [stale-doc] (previously low, org-mode.md) — Resolved. The label has been updated from "GCP Admin (Mint)" to "Mint Admin".

  • [stale-doc] (previously low, repo-management.md) — Resolved. The label has been updated from "GCP Admin (Mint)" to "Mint Admin".

Previous run (4)

Review

Findings

Medium

  • [race-condition] internal/dispatch/cf/provisioner.go:348 — EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, and RemoveRepoFromWorker each perform a non-atomic read-modify-write cycle on ALLOWED_ORGS or PER_REPO_WIF_REPOS. The value is read via GetVars, modified in memory, then written via UpdateVars (which itself reads vars again and merges). Between the initial read and the final API write, a concurrent operation could modify the same var, and the second operation’s changes would be silently overwritten. The GCP path has the same accepted limitation with documentation mitigation (“Enroll organizations serially”), but no equivalent guidance exists for the Cloudflare path.
    Remediation: Add serial-enrollment guidance for the Cloudflare path in docs/guides/infrastructure/mint-administration.md (matching the existing GCP guidance), and mention it in CLI help text.

Low

  • [content-injection] internal/dispatch/cf/provisioner.go:1367 — createVersionWithVars constructs Content-Disposition headers for re-uploaded modules using the module name from the CF API response (mod.name) via fmt.Sprintf without sanitization. A compromised API response containing embedded double quotes or newlines in a module name could corrupt the multipart form structure. Risk is very low (requires compromised CF API or MITM past TLS).
    Remediation: Sanitize mod.name (reject or escape characters outside [a-zA-Z0-9._-]) before embedding in MIME headers.

  • [silent-truncation] internal/dispatch/cf/provisioner.go:1261io.LimitReader(part, maxWorkerModuleBytes) silently truncates module data exceeding 50 MB. If a Worker module exceeds this limit, fetchWorkerContent returns a truncated module without error, and createVersionWithVars re-uploads the truncated data, resulting in a corrupted deployment. Cloudflare’s own Worker size limits (typically 10–25 MB) make this unlikely in practice.
    Remediation: After ReadAll, check if the number of bytes read equals maxWorkerModuleBytes and return an error on truncation.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from “GCP Admin (Mint)” to “Mint Admin” while adjacent rows (deploy, delete, add-role, remove-role, status) still use “GCP Admin (Mint)”, creating a visual inconsistency within the same table.
    Remediation: Either rename all mint-related rows to “Mint Admin” for consistency, or keep the original label.

  • [code-duplication] internal/cli/mint.go — The four Cloudflare enroll/unenroll functions (runMintEnrollOrgCloudflare, runMintEnrollRepoCloudflare, runMintUnenrollOrgCloudflare, runMintUnenrollRepoCloudflare) repeat an identical ~20-line preamble: computing effectiveName, constructing wrangler + provisioner, verifying Worker existence, and checking preview versions.
    Remediation: Extract a shared helper that handles effectiveName defaulting, provisioner construction, WorkerExists verification, and the preview-versions warning.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused. An operator following this runbook for Cloudflare enrollment would get no guidance on platform selection, Worker name configuration, or Cloudflare-specific requirements.
    Remediation: Add a platform triage step and parallel Cloudflare enrollment sections, or note the runbook is GCP-only and direct users to CLI documentation.

  • [stale-doc] docs/guides/getting-started/org-mode.md:201 — The full uninstall table labels the mint unenroll step as “GCP Admin (Mint)”, inconsistent with the updated “Mint Admin” label in operations.md.
    Remediation: Update to “Mint Admin” for consistency.

  • [stale-doc] docs/guides/getting-started/repo-management.md:424 — Same role label inconsistency as org-mode.md.
    Remediation: Update to “Mint Admin” for consistency.


Resolved prior findings
  • [logic-error] (previously high) — Resolved. All four Cloudflare enroll/unenroll functions now pass effectiveName (which defaults to "fullsend-mint") to the Provisioner Config instead of the raw workerName flag value. Tests now assert the correct worker name is passed to UpdateVars.

  • [test-integrity] (previously low, 2 findings) — Resolved. Tests now assert fake.updateVarsCalls[0].workerName equals "fullsend-mint", and no-op tests correctly assert fake.updateVarsCalls is empty instead of fake.deployCalls.

  • [unbounded-read] (previously low) — Resolved. All io.ReadAll calls now use io.LimitReader with appropriate caps (maxAPIResponseBytes, maxWorkerModuleBytes, maxErrorResponseBytes). The existing resolveSubdomainViaAPI call was also updated.


Labels: PR primarily modifies Go production code and tests in the mint and CF provisioner packages.

Previous run (5)

Review

Findings

High

  • [logic-error] internal/cli/mint.go:1764 — In all four Cloudflare enroll/unenroll functions (runMintEnrollOrgCloudflare, runMintEnrollRepoCloudflare, runMintUnenrollOrgCloudflare, runMintUnenrollRepoCloudflare), the Provisioner is constructed with WorkerName: workerName where workerName is the raw flag value, which defaults to "" when the user omits --worker-name. The effectiveName variable (defaulting to "fullsend-mint") is used for the WorkerExists check and display text, but the Provisioner receives the empty string. All Provisioner methods (GetVars, UpdateVars) use p.cfg.WorkerName, so API calls target an empty worker name at runtime.
    Remediation: Pass effectiveName instead of workerName when constructing the Provisioner Config in all four functions.

Medium

  • [race-condition] internal/dispatch/cf/provisioner.go:364 — EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, and RemoveRepoFromWorker each perform a read-modify-write cycle on ALLOWED_ORGS or PER_REPO_WIF_REPOS without concurrency control. Each method reads vars via GetVars, computes a new value locally, then passes it to updateDurableVars → UpdateVars. If two concurrent operations run, the second to complete will overwrite the first's changes. The existing GCP codepath has the same limitation, and mint-administration.md already warns about serial enrollment. This is a known trade-off for a CLI tool, not a blocking issue.
    Remediation: Consider atomic read-modify-write within UpdateVars, or Cloudflare ETag-based optimistic concurrency, or explicitly document the concurrent-unenroll risk.

Low

  • [test-integrity] internal/cli/mint_test.go:996 — TestRunMintEnrollOrgCloudflare_Success and similar tests call the enroll/unenroll functions with workerName="" and do not assert what worker name was passed to UpdateVars. The fake records workerName but tests only check var contents, masking the empty-WorkerName bug above.
    Remediation: Add assertions like assert.Equal(t, "fullsend-mint", fake.updateVarsCalls[0].workerName).

  • [test-integrity] internal/dispatch/cf/provisioner_test.go:1913 — TestEnsureOrgInWorker_AlreadyEnrolled and similar no-op tests assert fake.deployCalls is empty, but enroll uses UpdateVars, not Deploy. The assertion is vacuously true and does not verify that no mutation occurred. Same pattern in TestRegisterRepoInWorker_AlreadyEnrolled, TestRemoveOrgFromWorker_NotEnrolled, TestRemoveRepoFromWorker_NotEnrolled.
    Remediation: Change assert.Empty(t, fake.deployCalls) to assert.Empty(t, fake.updateVarsCalls).

  • [unbounded-read] internal/dispatch/cf/provisioner.go:1248 — Multiple io.ReadAll calls on Cloudflare API response bodies have no size limit. While these are authenticated API calls over TLS to a trusted provider, a buggy response could cause unbounded memory allocation.
    Remediation: Use io.LimitReader to cap response body reads at reasonable maximums.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows (deploy, delete, add-role, remove-role, status) still use "GCP Admin (Mint)", creating a visual inconsistency within the same table.
    Remediation: Either rename all mint-related rows to "Mint Admin" for consistency, or keep the original label.

  • [code-duplication] internal/cli/mint.go — The four new Cloudflare enroll/unenroll functions each repeat an identical ~20-line preamble: computing effectiveName, constructing wrangler + provisioner, verifying Worker existence, and checking preview versions.
    Remediation: Extract a shared helper that handles effectiveName defaulting, provisioner construction, WorkerExists verification, and the preview-versions warning.

  • [naming-consistency] internal/dispatch/cf/provisioner.go — The new test-overridable function vars mix export styles: GetWorkerVarsFn and ResolveCloudflareAPITokenFn are exported, while deployVersionFn is unexported. The established pattern in this file (BuildWASMFn, CopyWASMExecFn, WranglerWhoamiFn) exports all test seams.
    Remediation: Export deployVersionFn (→ DeployVersionFn) for consistency, or add a comment explaining the intentional difference.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused. An operator following this runbook for Cloudflare enrollment would get no guidance on platform selection, Worker name configuration, or Cloudflare-specific requirements.
    Remediation: Add a platform triage step and parallel Cloudflare enrollment sections, or note the runbook is GCP-only and direct users to CLI documentation.

  • [stale-doc] docs/guides/getting-started/org-mode.md:201 — The full uninstall table labels the mint unenroll step as "GCP Admin (Mint)", inconsistent with the updated "Mint Admin" label in operations.md.
    Remediation: Update to "Mint Admin" for consistency.

  • [stale-doc] docs/guides/getting-started/repo-management.md:424 — Same role label inconsistency as org-mode.md.
    Remediation: Update to "Mint Admin" for consistency.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (6)

Review

Findings

Medium

  • [race-condition] internal/dispatch/cf/provisioner.go:335 — EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, and RemoveRepoFromWorker each perform a read-modify-write cycle on ALLOWED_ORGS or PER_REPO_WIF_REPOS without concurrency control. Each method reads vars via GetVars, computes a new value locally, then passes it to updateDurableVars → UpdateVars. If two concurrent operations run, the second to complete will overwrite the first's changes. The existing GCP codepath has the same limitation, and mint-administration.md already warns about serial enrollment. This is a known trade-off for a CLI tool, not a blocking issue.
    Remediation: Consider atomic read-modify-write within UpdateVars, or Cloudflare ETag-based optimistic concurrency, or explicitly document the concurrent-unenroll risk (where a removed org could remain authorized if a concurrent enroll overwrites the removal).

Low

  • [HTTP-header-injection] internal/dispatch/cf/provisioner.go:1354 — In createVersionWithVars, module names from the Cloudflare content API (mod.name) are interpolated unescaped into Content-Disposition headers via fmt.Sprintf. A compromised CF API response with double quotes or newlines in a module name could break the multipart form structure. Risk is very low (requires compromised API or MITM past TLS).
    Remediation: Sanitize mod.name (reject or escape characters outside [a-zA-Z0-9._-]) before header interpolation.

  • [unbounded-read] internal/dispatch/cf/provisioner.go:1248 — Multiple io.ReadAll calls on Cloudflare API response bodies have no size limit. While these are authenticated API calls over TLS to a trusted provider, a buggy response could cause unbounded memory allocation. The multipart module read is notable since WASM modules can be large.
    Remediation: Use io.LimitReader to cap response body reads at reasonable maximums.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows (deploy, delete, add-role, remove-role, status) still use "GCP Admin (Mint)", creating a visual inconsistency within the same table.
    Remediation: Either rename all mint-related rows to "Mint Admin" for consistency, or keep the original label.

  • [code-duplication] internal/cli/mint.go — The four new Cloudflare enroll/unenroll functions (runMintEnrollOrgCloudflare, runMintEnrollRepoCloudflare, runMintUnenrollOrgCloudflare, runMintUnenrollRepoCloudflare) each repeat an identical ~20-line preamble: computing effectiveName, constructing wrangler + provisioner, verifying Worker existence, and checking preview versions. The existing GCP codepaths follow a similar pattern, so this is consistent but represents a maintenance concern.
    Remediation: Extract a shared helper that resolves effectiveName, builds the wrangler/provisioner, verifies the Worker exists, and checks preview versions.

  • [naming-consistency] internal/dispatch/cf/provisioner.go — The new test-overridable function vars mix export styles: GetWorkerVarsFn and ResolveCloudflareAPITokenFn are exported, while deployVersionFn is unexported. All serve the same purpose (test seams). The difference is likely because deployVersionFn is only used within the cf package tests, while the other two are used from internal/cli tests.
    Remediation: Consider exporting deployVersionFn (→ DeployVersionFn) for consistency, or add a comment explaining the intentional difference.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused (asks for GCP_PROJECT/MINT_REGION, runs gcloud commands, describes Cloud Run verification). An operator following this runbook for Cloudflare enrollment would get no guidance on platform selection, Worker name configuration, or Cloudflare-specific requirements.
    Remediation: Add a platform triage step and parallel Cloudflare enrollment sections, or note the runbook is GCP-only and direct users to CLI documentation.

  • [stale-doc] docs/guides/getting-started/org-mode.md:201 — The full uninstall table labels the mint unenroll step as "GCP Admin (Mint)", inconsistent with the updated "Mint Admin" label in operations.md.
    Remediation: Update to "Mint Admin" for consistency.

  • [stale-doc] docs/guides/getting-started/repo-management.md:424 — Same role label inconsistency as org-mode.md.
    Remediation: Update to "Mint Admin" for consistency.

Resolved prior findings
  • [api-contract-violation] (previously medium) — Resolved. getWorkerVars and UpdateVars now use ResolveCloudflareAPITokenFn which falls back to wrangler auth token when CLOUDFLARE_API_TOKEN is unset. Documentation claims about Wrangler OAuth support are now accurate.

  • [race-condition serial enrollment warning] (previously low) — Contextually resolved. The existing "Enrollment ordering" section in mint-administration.md is generic enough to cover both GCP and Cloudflare enrollment.

Previous run (7)

Review

Findings

Medium

  • [api-contract-violation] docs/cli/mint.md:246 — Documentation claims enroll/unenroll supports "Wrangler OAuth session via wrangler login" as an alternative to CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID. However, getWorkerVars (provisioner.go) unconditionally requires CLOUDFLARE_API_TOKEN and explicitly rejects OAuth: "wrangler OAuth sessions are not supported for this operation." A user authenticating via wrangler login alone would pass ResolveCloudflareAuth but fail at the first provisioner operation (GetVars). The same inaccurate claim appears in docs/guides/infrastructure/mint-administration.md (line 286) and the CLI long help text (internal/cli/mint.go, under the Cloudflare mode sections for both newMintEnrollCmd and newMintUnenrollCmd).
    Remediation: Either update docs and CLI help to state CLOUDFLARE_API_TOKEN is required for enroll/unenroll (OAuth is only supported for deploy/delete), or add OAuth support to getWorkerVars.

Low

  • [race-condition] internal/dispatch/cf/provisioner.go:324 — The read-modify-write pattern in EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, and RemoveRepoFromWorker (GetVars → modify → Deploy) has no concurrency control. This matches the GCP path's accepted limitation, but the Cloudflare enrollment documentation does not include the serial enrollment warning that exists for GCP enrollment (see "Enrollment ordering" section).
    Remediation: Add an "Enrollment ordering" note to the Cloudflare enrollment section.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows still use "GCP Admin (Mint)", creating an inconsistency within the same table.
    Remediation: Either keep the original label for consistency or rename all mint-related rows.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused. An operator following this runbook for Cloudflare enrollment would get no guidance on platform selection, Worker name configuration, or Cloudflare-specific requirements.
    Remediation: Add a Cloudflare enrollment section or note the runbook is GCP-only and direct users to CLI documentation.

  • [stale-doc] docs/guides/getting-started/org-mode.md:201 — The full uninstall table labels the mint unenroll step as "GCP Admin (Mint)", inconsistent with the updated "Mint Admin" label in operations.md.
    Remediation: Change "GCP Admin (Mint)" to "Mint Admin".

  • [stale-doc] docs/guides/getting-started/repo-management.md:424 — Same role label inconsistency as above.
    Remediation: Change "GCP Admin (Mint)" to "Mint Admin".

Previous run (8)

Review

Findings

Medium

  • [api-contract-violation] docs/cli/mint.md:35 — Documentation claims enroll/unenroll supports "Wrangler OAuth session via wrangler login" as an alternative to CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID. However, getWorkerVars (provisioner.go) unconditionally requires CLOUDFLARE_API_TOKEN and explicitly rejects OAuth: "CLOUDFLARE_API_TOKEN is required to read Worker vars (wrangler OAuth sessions are not supported for this operation)." A user authenticating via wrangler login alone would pass ResolveCloudflareAuth but fail at the first provisioner operation. The same inaccurate claim appears in docs/guides/infrastructure/mint-administration.md (line 138) and the CLI long help text (internal/cli/mint.go newMintEnrollCmd and newMintUnenrollCmd).
    Remediation: Either update docs and CLI help to state CLOUDFLARE_API_TOKEN is required for enroll/unenroll, or add OAuth support to getWorkerVars.

Low

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused. An operator following this runbook for Cloudflare enrollment would get no guidance. The CLI --help and mint-administration.md cover the CF path, but the skill runbook does not.
    Remediation: Add a platform triage step and parallel Cloudflare enrollment sections.

  • [race-condition] internal/dispatch/cf/provisioner.go:322 — The read-modify-write pattern (GetVars → modify → Deploy) has no concurrency control. This matches the GCP path's accepted limitation. The GCP enrollment docs include a "Enroll organizations serially" warning but the Cloudflare enrollment docs do not.
    Remediation: Add the "Enroll organizations serially" warning to the Cloudflare enrollment docs.

  • [scope-creep] docs/guides/getting-started/operations.md:99 — The PR renames the persona for mint enroll/unenroll from "GCP Admin (Mint)" to "Mint Admin" while adjacent rows still use "GCP Admin (Mint)", creating an inconsistency within the same table.
    Remediation: Either keep the original label for consistency or rename all mint-related rows.

  • [stale-doc] docs/guides/getting-started/org-mode.md:201 — The full uninstall table labels the mint unenroll step as "GCP Admin (Mint)", now inconsistent with the updated "Mint Admin" label in operations.md.

  • [stale-doc] docs/guides/getting-started/repo-management.md:424 — Same role label inconsistency as above.

Previous run (9)

Review

Findings

Medium

  • [logic-error] internal/dispatch/cf/provisioner.go:478updateDurableVars stamps empty version metadata on every enroll/unenroll. The CLI creates the Provisioner without setting cfg.Version or cfg.Commit (they default to empty strings). writeVersionTS writes empty FULLSEND_VERSION and FULLSEND_COMMIT, silently wiping the Worker's version metadata set during the original deploy.
    Remediation: Either pass Version/Commit from the CLI into the Provisioner config, or skip writeVersionTS when both are empty.

  • [stale-doc] docs/cli/mint.md:207 — The mint enroll section only documents GCP mode (--project and --region flags, GCP usage examples). The PR adds --platform, --worker-name, and --preview flags. The deploy and delete sections in the same file already document both GCP and Cloudflare modes.
    Remediation: Add a Cloudflare mode subsection mirroring the structure used by deploy/delete.

  • [stale-doc] docs/cli/mint.md:227 — The mint unenroll section shows only GCP usage with no flags table. Missing --platform, --worker-name, and --preview documentation.
    Remediation: Add a Cloudflare mode subsection and flags table.

  • [stale-doc] docs/guides/infrastructure/mint-administration.md — The enrollment/unenrollment sections only describe GCP mode. Flags tables and "What enrollment does" subsection are GCP-specific but not labeled as such.
    Remediation: Add Cloudflare sections or label existing content as GCP-specific.

  • [stale-doc] skills/mint-enroll/SKILL.md — The mint-enroll skill runbook is exclusively GCP-focused (asks for GCP_PROJECT/MINT_REGION, runs gcloud commands, describes Cloud Run verification). An agent following this runbook for Cloudflare enrollment would execute GCP-specific steps.
    Remediation: Add a parallel Cloudflare enrollment path covering CLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_ID, --worker-name, and CF-specific verification.

Low

  • [logic-error] internal/dispatch/cf/provisioner.go:381RegisterRepoInWorker adds the repo owner to ALLOWED_ORGS, diverging from GCP per-repo enrollment which explicitly does NOT add the owner. The linked issue authorizes this behavior, but the semantic difference (triggering dual-enrollment logic in the handler) is not documented.

  • [silent-misbehavior] internal/dispatch/cf/provisioner.go:352RemoveOrgFromWorker and RemoveRepoFromWorker perform a full redeploy (WASM build + deploy) even when the target entity is not present in the list. Compare filtered list length to original and return early if unchanged.

  • [race-condition] internal/dispatch/cf/provisioner.go:322 — The read-modify-write pattern (GetVars -> modify -> Deploy) has no concurrency control. Concurrent operations could cause lost updates. This matches the GCP path's accepted limitation for CLI admin tools.

  • [naming-convention] internal/cli/mint.go — Dispatcher functions use full Cloudflare suffix (runMintEnrollCloudflare) while leaf helpers use abbreviation CF (runMintEnrollOrgCF). The existing deploy uses Cloudflare consistently.

  • [test-coverage-gap] internal/dispatch/cf/provisioner_test.go — No test covers RemoveOrgFromWorker or RemoveRepoFromWorker when the target entity is not enrolled.

  • [architectural-consistency] internal/cli/mint.go — The deploy command calls warnIrrelevantFlags() for wrong-platform flags. Enroll/unenroll register both GCP and CF flags but do not call warnIrrelevantFlags, so --platform=cloudflare --project foo silently ignores --project.

  • [stale-doc] docs/guides/getting-started/operations.md — The standalone commands table labels mint enroll/unenroll with role "GCP Admin (Mint)". These commands now support both platforms.


Labels: PR modifies mint CLI enrollment commands and CF provisioner


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the component/mint Token mint and cross-boundary credentials label Aug 12, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 9:59 AM UTC · Completed 10:13 AM UTC

Commit: 070aff4 · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed 10 of 12 review findings. Fixed the version metadata stamping bug, added early-return for no-op remove operations, renamed CF-suffix functions for consistency, added warnIrrelevantEnrollFlags, added missing tests, and updated three documentation files with Cloudflare mode content. Disagreed with 2 findings: skills/mint-enroll/SKILL.md is a protected path, and the race condition is an accepted limitation matching GCP.

Fixed (10):

  1. updateDurableVars stamps empty version metadata on every enroll/unenroll (internal/dispatch/cf/provisioner.go): Skip writeVersionTS when both cfg.Version and cfg.Commit are empty, preventing overwrite of existing version metadata during enroll/unenroll
  2. mint enroll section only documents GCP mode (docs/cli/mint.md): Added Cloudflare mode subsection with usage examples, and a combined flags table covering both platforms
  3. mint unenroll section only documents GCP mode (docs/cli/mint.md): Added Cloudflare mode subsection and flags table for unenroll
  4. mint-administration.md enrollment sections only describe GCP mode (docs/guides/infrastructure/mint-administration.md): Added GCP/Cloudflare subsections for both enrollment and unenrollment, with Cloudflare-specific usage examples, credential requirements, and updated flags tables
  5. RegisterRepoInWorker adds owner to ALLOWED_ORGS diverging from GCP (internal/dispatch/cf/provisioner.go): Added code comment documenting why CF per-repo enrollment adds the owner to ALLOWED_ORGS (CF mint handler requires it, unlike GCP which has a dedicated WIF provider)
  6. RemoveOrgFromWorker and RemoveRepoFromWorker perform full redeploy when entity not present (internal/dispatch/cf/provisioner.go): Added early return when filtered list length equals original list length, skipping unnecessary redeploy
  7. Inconsistent naming: Cloudflare suffix vs CF abbreviation (internal/cli/mint.go): Renamed runMintEnrollOrgCF/RepoCF and runMintUnenrollOrgCF/RepoCF to use Cloudflare suffix consistently, matching the dispatch functions
  8. No test covers RemoveOrgFromWorker or RemoveRepoFromWorker when entity not enrolled (internal/dispatch/cf/provisioner_test.go): Added TestRemoveOrgFromWorker_NotEnrolled and TestRemoveRepoFromWorker_NotEnrolled verifying no redeploy when entity is absent
  9. Enroll/unenroll do not call warnIrrelevantFlags for wrong-platform flags (internal/cli/mint.go): Added warnIrrelevantEnrollFlags function and calls in both enroll and unenroll RunE handlers, warning when GCP flags are used with --platform=cloudflare and vice versa
  10. operations.md commands table labels mint enroll/unenroll as GCP Admin only (docs/guides/getting-started/operations.md): Changed role from 'GCP Admin (Mint)' to 'Mint Admin' and updated descriptions to note both --platform=gcp and --platform=cloudflare support

Disagreed (2):

  1. skills/mint-enroll/SKILL.md is exclusively GCP-focused: skills/ is a protected path and cannot be modified by the fix agent without explicit human instruction
  2. Read-modify-write pattern has no concurrency control: The reviewer explicitly notes this matches the GCP path's accepted limitation for CLI admin tools. No change needed.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 12, 2026
- Skip writeVersionTS when Version/Commit are empty to avoid wiping
  Worker version metadata during enroll/unenroll
- Return early from RemoveOrgFromWorker/RemoveRepoFromWorker when
  the target entity is not present, avoiding unnecessary redeploy
- Document why RegisterRepoInWorker adds owner to ALLOWED_ORGS
  (diverges from GCP path by design)
- Rename CF-suffix helpers to Cloudflare for naming consistency
  with the dispatch functions (runMintEnrollCloudflare, etc.)
- Add warnIrrelevantEnrollFlags to enroll/unenroll commands
- Add tests for remove operations when entity is not enrolled
- Update docs/cli/mint.md with Cloudflare mode sections and flags
- Update mint-administration.md with CF enrollment/unenrollment docs
- Update operations.md to reflect cross-platform enroll/unenroll

Addresses review feedback on #6137
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 10:14 AM UTC · Ended 10:30 AM UTC

Commit: a81344e · View workflow run →

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Site preview

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

Commit: c1facfe044b9aade2936721db39551ab67d5d225

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 12, 2026 10:29

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 12, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:14 AM UTC · Completed 10:30 AM UTC

Commit: a81344e · View workflow run →

@ifireball

Copy link
Copy Markdown
Member

/fs-fix when enrolling a repo in per-repo mode is should nit be added the ALLOWED_ORGS only to PER_REPO_WIF_REPOS , this has notihg to do with the dedicated WIF privider.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 11:21 AM UTC · Completed 11:31 AM UTC

Commit: a81344e · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Per-repo Cloudflare enrollment no longer adds the owner org to ALLOWED_ORGS. Only PER_REPO_WIF_REPOS is updated, matching the GCP per-repo enrollment behavior. Updated provisioner logic, CLI help text, dry-run output, documentation, and tests.

Fixed (5):

  1. per-repo CF enrollment should not add owner to ALLOWED_ORGS (internal/dispatch/cf/provisioner.go): Removed ALLOWED_ORGS logic from RegisterRepoInWorker. Per-repo enrollment now only updates PER_REPO_WIF_REPOS, matching the behavior on GCP. Updated method comment to explain the correct semantics.
  2. dry-run output incorrectly mentions ALLOWED_ORGS for per-repo enrollment (internal/cli/mint.go): Removed 'Would ensure owner is in ALLOWED_ORGS' line from dry-run output in runMintEnrollRepoCloudflare. Updated CLI help text to clarify that per-repo enrollment only modifies PER_REPO_WIF_REPOS.
  3. docs incorrectly state per-repo enrollment updates ALLOWED_ORGS (docs/cli/mint.md): Updated Cloudflare enroll documentation to clarify that org enrollment updates ALLOWED_ORGS while per-repo enrollment updates PER_REPO_WIF_REPOS only.
  4. docs incorrectly state per-repo enrollment updates ALLOWED_ORGS (docs/guides/infrastructure/mint-administration.md): Updated admin guide to clarify that per-repo enrollment does not modify ALLOWED_ORGS.
  5. tests assert incorrect ALLOWED_ORGS behavior for per-repo enrollment (internal/dispatch/cf/provisioner_test.go): Updated TestRegisterRepoInWorker_AddsRepo to assert ALLOWED_ORGS is NOT modified. Renamed TestRegisterRepoInWorker_OwnerAlreadyInAllowedOrgs to TestRegisterRepoInWorker_DoesNotModifyAllowedOrgs with updated assertions.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 12, 2026
RegisterRepoInWorker no longer adds the owner org to ALLOWED_ORGS.
Per-repo enrollment is independent of org-level enrollment on both
GCP and Cloudflare — only PER_REPO_WIF_REPOS is updated.

Updated CLI help text, dry-run output, docs, and tests to reflect
that per-repo enrollment does not touch ALLOWED_ORGS.

Addresses review feedback on #6137
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:33 AM UTC · Completed 11:50 AM UTC

Commit: e3fdaaa · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix

Replace the enroll/unenroll durable-var update path so it does not require local Worker sources or wrangler deploy.

Problem

updateDurableVars currently calls resolveSourceDir + ensureWASMArtifacts + wrangler deploy --keep-vars. That re-uploads Worker code from embedded TS + auto-built WASM and assumes access to the same build inputs used at deploy time. Enroll/unenroll must work from environments that only have Cloudflare credentials (no fullsend checkout, no cmd/mint-wasm, no make wasm-stage).

Required approach: version clone + binding update (no local sources)

Follow the same pattern as wrangler versions secret put / copyWorkerVersionWithNewSecrets in workers-sdk — not wrangler deploy and not wrangler versions upload from a local project.

  1. Read current vars via Cloudflare API (GET /accounts/{account}/workers/scripts/{name}/settings), parsing plain_text bindings. Keep existing parseWorkerSettingsVars logic.

  2. Patch locallyALLOWED_ORGS (org enroll/unenroll) or PER_REPO_WIF_REPOS (per-repo enroll/unenroll only; do not touch ALLOWED_ORGS on per-repo path).

  3. Create a new Worker version via the Versions Upload API without local source:

    • Fetch base version modules + bindings from Cloudflare (prefer the currently deployed version when gradual rollouts may make "latest uploaded" ≠ live; fall back to latest uploaded).
    • Re-upload module bytes from the API response, not from disk.
    • Mark unchanged bindings as type: "inherit" (modules, PEM secrets, KV/DO/service bindings, etc.).
    • Set updated vars as type: "plain_text".
    • Use keep_bindings so secret_text and other binding types are preserved (same semantics as wrangler's secret-put clone).
    • Do not call resolveSourceDir, ensureWASMArtifacts, writeVersionTS, or buildWASM on the enroll/unenroll path.
  4. Deploy the new version to 100% durable traffic:

    • wrangler versions deploy <version-id>@100 --yes (non-interactive), or equivalent deployments API call.
    • This replaces the immediate deploy behavior of today's wrangler deploy --keep-vars.
  5. Preview versions — unchanged: enroll/unenroll still rejects --preview; warn (don't fail) when preview aliases exist. This path updates the durable Worker only.

Auth: support wrangler OAuth, not only CLOUDFLARE_API_TOKEN

getWorkerVars currently hard-requires CLOUDFLARE_API_TOKEN. Extend auth so API calls work when the operator uses wrangler login:

  • Prefer resolving a Bearer token via npx wrangler auth token (wrangler ≥ 4.57) when CLOUDFLARE_API_TOKEN is unset — OAuth tokens (cfoat_…) work for Workers API calls.
  • Alternatively extend ResolveCloudflareAuth (or add ResolveCloudflareAPIToken) to return (accountID, token, err) using the same precedence as wrangler: env API token → wrangler OAuth via auth token.
  • Use that token for both settings GET and version create/deploy API calls.
  • Keep CLOUDFLARE_API_TOKEN as the CI path; document that wrangler login is sufficient for interactive use.

WranglerRunner / tests

  • Add runner methods for version-clone + deploy (or a single UpdateDurableVars that hides the API multipart upload), with fakes in unit tests.
  • Remove or bypass Deploy() from the enroll/unenroll var-update path; Deploy() remains for mint deploy.
  • Update tests: enroll/unenroll should not invoke Deploy with a source dir; assert version-create + versions-deploy (or API equivalent) instead.
  • Update docs (docs/cli/mint.md, mint-administration.md) to state enroll/unenroll do not require local Worker sources or WASM build artifacts.

Reference

Wrangler precedent: packages/wrangler/src/versions/secrets/index.ts (copyWorkerVersionWithNewSecrets) — clones latest version bindings with inherit, updates only the target binding(s), re-uploads modules fetched from Cloudflare.

Also fix any remaining review-agent findings if still open.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:26 AM UTC · Completed 6:45 AM UTC

Commit: e3fdaaa · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Replaced the enroll/unenroll var-update path to use the Cloudflare Versions API instead of wrangler deploy. The new UpdateVars method on WranglerRunner clones the deployed version's modules from the API, creates a new version with updated plain_text bindings (preserving secrets and other bindings via keep_bindings), and deploys to 100% traffic. Also added OAuth auth support via wrangler auth token fallback. All 8 findings from the human instruction addressed.

Fixed (8):

  1. Replace updateDurableVars to not require local Worker sources or wrangler deploy (internal/dispatch/cf/provisioner.go): Replaced updateDurableVars body: instead of resolveSourceDir + ensureWASMArtifacts + wrangler deploy, it now delegates to WranglerRunner.UpdateVars which uses the Cloudflare Versions API to clone the deployed version's modules, update plain_text bindings with keep_bindings for secrets/KV/DO, and deploy to 100% traffic
  2. Add UpdateVars to WranglerRunner interface (internal/dispatch/cf/provisioner.go): Added UpdateVars method to WranglerRunner interface with LiveWranglerRunner implementation that: fetches module content from content/v2 API, fetches current vars, creates a new version via POST /versions with multipart upload, and deploys via POST /deployments
  3. Support wrangler OAuth for API calls (internal/dispatch/cf/provisioner.go): Added ResolveCloudflareAPITokenFn that prefers CLOUDFLARE_API_TOKEN env var and falls back to 'npx wrangler auth token' for OAuth sessions. Updated getWorkerVars to use this instead of hard-requiring CLOUDFLARE_API_TOKEN
  4. Update tests: enroll/unenroll should not invoke Deploy (internal/dispatch/cf/provisioner_test.go): Updated all enroll/unenroll tests to assert on updateVarsCalls instead of deployCalls. Removed stubWASMBuild and SourceDir from enroll/unenroll test configs since they are no longer needed
  5. Update CLI test fakes for new interface (internal/cli/mint_test.go): Added UpdateVars to fakeCFWranglerRunner with tracking. Updated success tests for enroll/unenroll to verify updateVarsCalls and assert Deploy is not called
  6. Update docs to state enroll/unenroll do not require local Worker sources (docs/cli/mint.md): Replaced 'redeploy with --keep-vars' language with 'Cloudflare Versions API - no local Worker sources or WASM build artifacts required' in both enroll and unenroll sections
  7. Update admin guide documentation (docs/guides/infrastructure/mint-administration.md): Updated Cloudflare enrollment and unenrollment descriptions to explain the version-clone approach and remove redeploy language
  8. Update CLI help text (internal/cli/mint.go): Updated long help text for both enroll and unenroll commands to describe Versions API approach instead of redeploy with --keep-vars

Tests: passed

Strategy change: Iteration 3: fundamentally changed the var-update approach from source-based wrangler deploy to API-based version clone, eliminating the dependency on local Worker sources and WASM build artifacts

Decision points
  • Chose to add UpdateVars as a single WranglerRunner method rather than multiple fine-grained methods (alternatives: Multiple methods (fetchContent, createVersion, deployVersion), Direct API calls in Provisioner; rationale: Single method keeps the version-clone complexity encapsulated in the runner layer where it belongs, matches the human instruction suggestion of 'a single UpdateDurableVars that hides the API multipart upload', and is simpler to fake in tests)
  • Used keep_bindings list for all non-plain_text binding types instead of fetching and re-specifying each binding (alternatives: Fetch all bindings and re-specify with inherit type, Use only a subset of keep_bindings; rationale: Comprehensive keep_bindings list is safer - it preserves any binding type the Worker may have, matching wrangler's copyWorkerVersionWithNewSecrets pattern)
  • Added ResolveCloudflareAPITokenFn using 'wrangler auth token' command as OAuth fallback (alternatives: Read wrangler config file directly, Require CLOUDFLARE_API_TOKEN always; rationale: Using 'wrangler auth token' (wrangler >= 4.57) is the documented approach and works with wrangler's token refresh logic)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 13, 2026
…unenroll

Replace the updateDurableVars implementation so enroll/unenroll no longer
requires local Worker sources, WASM build artifacts, or wrangler deploy.

The new path uses the Cloudflare Versions API to:
1. Fetch the currently deployed Worker's module content
2. Create a new version with updated plain_text bindings and
   keep_bindings for secrets/KV/DO/service bindings
3. Deploy the new version to 100% traffic

Also adds ResolveCloudflareAPITokenFn which falls back to
`wrangler auth token` when CLOUDFLARE_API_TOKEN is unset, enabling
enroll/unenroll via wrangler OAuth sessions.

Addresses human review feedback on #6137
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:46 AM UTC · Completed 7:02 AM UTC

Commit: 32e03cc · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Aug 13, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix

Manual CF enroll/unenroll testing on a live worker (mint-enroll-test) found bugs not covered by iteration 6. Public mint mode on Cloudflare is PER_REPO_WIF_REPOS=* (set by mint deploy --public) — ALLOWED_ORGS is per-org/tight mode only and has no public mode on CF. The CF provisioner still gates enroll/unenroll on mintcore.IsPublicMint(ALLOWED_ORGS), which is wrong for current semantics (see ADR-0078 / IsPublicMintRepos).

Fix (high): public-mode detection in CF enroll/unenroll

In internal/dispatch/cf/provisioner.go, replace all four IsPublicMint(existingOrgs) guards in EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, and RemoveRepoFromWorker with checks against PER_REPO_WIF_REPOS via mintcore.IsPublicMintRepos (build the map the same way NewHandler does: split vars["PER_REPO_WIF_REPOS"] on commas, lowercase entries).

Expected behavior after fix:

  • RegisterRepoInWorker / RemoveRepoFromWorker: when PER_REPO_WIF_REPOS=*, treat as public — enroll is a no-op; unenroll returns a clear error (mirror current org-unenroll messaging but cite PER_REPO_WIF_REPOS=*, not ALLOWED_ORGS=*).
  • EnsureOrgInWorker / RemoveOrgFromWorker: do not consult ALLOWED_ORGS for public-mode detection. Org enroll/unenroll is the per-org path; on a public CF mint (PER_REPO_WIF_REPOS=*) org operations should either no-op with an informative message or error — they must not be keyed off ALLOWED_ORGS=* (which CF public deploy does not set).

Update provisioner_test.go public-mode cases: use PER_REPO_WIF_REPOS: "*" instead of ALLOWED_ORGS: "*". Add a regression test for the corrupt state we hit live: deploy --public then mint enroll --platform=cloudflare org/repo must not produce PER_REPO_WIF_REPOS=*,owner/repo.

Fix (high): resolveCloudflareAPIToken wrangler output parsing

resolveCloudflareAPIToken currently strings.TrimSpaces the full npx wrangler auth token stdout (includes banner lines), producing invalid header field value for "Authorization" on enroll/unenroll without manually exporting CLOUDFLARE_API_TOKEN. Extract the last non-empty line of output (same pattern as deploy-side wrangler token resolution in cloudflare_api.go).

Fix (medium, optional): deploy footgun

mint deploy --allowed-orgs=… replaces the entire ALLOWED_ORGS var and can wipe prior org enrollments while leaving PER_REPO_WIF_REPOS intact. Consider warning when --allowed-orgs is explicitly set on redeploy, or document as destructive.

Fix (low, optional): no-op UX

No-op enroll/unenroll still prints "Enrollment/Unenrollment complete" — consider "already enrolled" / "not enrolled" when vars unchanged.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 12:12 PM UTC · Completed 12:23 PM UTC

Commit: 805a7b1 · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 13, 2026
…oll/unenroll

Replace IsPublicMint(ALLOWED_ORGS) with IsPublicMintRepos(PER_REPO_WIF_REPOS)
in all four CF enroll/unenroll methods. On Cloudflare, public mode is
PER_REPO_WIF_REPOS=* (set by mint deploy --public); ALLOWED_ORGS is the
per-org/tight mode variable and has no public wildcard on CF.

Also fix resolveCloudflareAPIToken to extract the last non-empty line from
wrangler auth token output, which may include banner lines that corrupt
the Authorization header value.

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 7 (human-triggered)

Fixed 3 of 5 findings (2 high-priority). Replaced all four IsPublicMint(ALLOWED_ORGS) guards with IsPublicMintRepos(PER_REPO_WIF_REPOS) in CF enroll/unenroll methods. Fixed resolveCloudflareAPIToken to extract last non-empty line from wrangler output. Updated and expanded test coverage. Disagreed with 2 optional findings (deploy footgun and no-op UX) as out of scope.

Fixed (3):

  1. public-mode detection in CF enroll/unenroll uses wrong variable (internal/dispatch/cf/provisioner.go): Replaced IsPublicMint(ALLOWED_ORGS) with IsPublicMintRepos(PER_REPO_WIF_REPOS) in all four methods: EnsureOrgInWorker, RemoveOrgFromWorker, RegisterRepoInWorker, RemoveRepoFromWorker. Added parsePerRepoWIFReposMap helper that mirrors NewHandler's PER_REPO_WIF_REPOS parsing convention. Updated error messages to cite PER_REPO_WIF_REPOS=* instead of ALLOWED_ORGS=*.
  2. resolveCloudflareAPIToken wrangler output parsing includes banner lines (internal/dispatch/cf/provisioner.go): Changed resolveCloudflareAPIToken to extract the last non-empty line from wrangler auth token stdout instead of TrimSpace on the full output. Added lastNonEmptyLine helper function. Wrangler may emit banner/diagnostic lines before the actual token, which corrupted the Authorization header.
  3. tests use ALLOWED_ORGS= for public mode instead of PER_REPO_WIF_REPOS=** (internal/dispatch/cf/provisioner_test.go): Updated all four public-mode test cases to use PER_REPO_WIF_REPOS=* instead of ALLOWED_ORGS=. Added regression test for deploy --public then repo enroll (must not produce PER_REPO_WIF_REPOS=,owner/repo). Added tests for org enroll/unenroll on public CF mint not consulting ALLOWED_ORGS. Added unit tests for parsePerRepoWIFReposMap and lastNonEmptyLine.

Disagreed (2):

  1. deploy footgun: --allowed-orgs replaces entire ALLOWED_ORGS var: Marked as medium/optional by the reviewer. This is a deploy-side UX concern outside the scope of this PR's enroll/unenroll changes. Would require changes to the deploy command, not the provisioner. Better addressed in a separate issue.
  2. no-op UX: enroll/unenroll still prints completion message when vars unchanged: Marked as low/optional by the reviewer. The no-op detection is correct (returns nil without calling updateDurableVars), but the CLI layer prints the generic success message. This is a CLI-layer UX improvement, not a correctness bug, and is best addressed separately.

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:24 PM UTC · Completed 12:41 PM UTC

Commit: 978210c · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ifireball

Copy link
Copy Markdown
Member

/fs-fix

Before addressing review items, squash all commits on this branch into a single commit and rebase onto upstream/main (fetch upstream/main first). The final branch should be one clean commit on top of current main.

Then fix all remaining relevant review findings on the squashed result (skip persona-table scope-creep in operations.md — the Mint Admin vs GCP Admin split is intentional).

input-validation

  1. internal/cli/mint.go — In runMintEnrollRepoCloudflare and runMintUnenrollRepoCloudflare, validate the repo slug with gcf.ValidateRepoSlug(repo) and reject gcf.PlaceholderOrg for the owner, matching the GCP enroll/unenroll repo paths.

  2. internal/dispatch/cf/provisioner.go — In ResolveCloudflareAuth (or wherever CLOUDFLARE_ACCOUNT_ID is returned from env), validate account ID format (^[a-f0-9]{32}$, same as whoami parsing) before using it in API URLs.

nondeterministic-output

  1. internal/dispatch/cf/provisioner.go — In createVersionWithVars, build the plain_text bindings list in deterministic order (sort binding names before marshaling) so version uploads produce stable metadata for debugging.

code-duplication

  1. internal/cli/mint.go — Extract the shared ~20-line preamble from the four Cloudflare enroll/unenroll functions (runMintEnrollOrgCloudflare, runMintEnrollRepoCloudflare, runMintUnenrollOrgCloudflare, runMintUnenrollRepoCloudflare) into a helper that: defaults effectiveName, constructs wrangler + provisioner with effectiveName, verifies Worker exists, and warns on preview versions. Keep per-org vs per-repo logic in the callers.

stale-docs

  1. skills/mint-enroll/SKILL.md — Add a platform triage step at the start (GCP vs --platform=cloudflare) and a short Cloudflare enrollment subsection (credentials, --worker-name, pointer to docs/cli/mint.md / mint-administration.md). Do not rewrite the full GCP runbook.

  2. docs/guides/dev/cli-internals.md — In the Command Decomposition table (or mint enroll row), add a footnote: GCP IAM roles apply to --platform=gcp; Cloudflare enrollment requires CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID (or wrangler login + wrangler auth token).

Tests

  • Add/update tests for repo slug validation on CF paths.
  • Add test that createVersionWithVars binding order is stable (or test sorted keys).
  • Ensure existing CF provisioner and CLI tests still pass after helper extraction.

Skip

  • operations.md persona label inconsistency (intentional).
  • Race-condition ETag concurrency (documented accepted limitation).
  • Optional deploy footgun / no-op UX from prior manual testing (out of scope unless trivial).

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:26 PM UTC · Completed 6:46 PM UTC

Commit: 978210c · View workflow run →

Adds --platform=cloudflare support to mint enroll and mint unenroll commands,
enabling org and per-repo enrollment against durable Cloudflare Workers.
Mirrors the existing platform flag on mint deploy and mint delete.

Changes:
- WranglerRunner interface: Added GetVars, HasPreviewVersions, UpdateVars
- CF Provisioner: Added EnsureOrgInWorker, RemoveOrgFromWorker,
  RegisterRepoInWorker, RemoveRepoFromWorker methods
- CLI enroll/unenroll: Added --platform, --worker-name, --preview flags
  with platform routing
- Help text documents CF credentials and mutable-vs-preview model
- Uses PER_REPO_WIF_REPOS for public-mode detection on CF paths

Closes #5633
- Validate repo slug (gcf.ValidateRepoSlug) and reject PlaceholderOrg
  in runMintEnrollRepoCloudflare and runMintUnenrollRepoCloudflare,
  matching the GCP repo enroll/unenroll validation
- Validate Cloudflare account ID format (^[a-f0-9]{32}$) in
  ResolveCloudflareAuth before using in API URLs
- Sort binding names in createVersionWithVars for deterministic
  version metadata
- Extract shared CF enroll/unenroll preamble into prepareCFEnrollContext
  helper (defaults effectiveName, constructs wrangler + provisioner,
  verifies Worker exists, warns on preview versions)
- Add CF platform triage step and enrollment subsection to
  skills/mint-enroll/SKILL.md
- Add CF credential footnote to docs/guides/dev/cli-internals.md
  Command Decomposition table
- Add tests for ValidateAccountID, repo slug validation on CF paths,
  PlaceholderOrg rejection, and deterministic binding order

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 8 (human-triggered)

Addressed all 8 human-instructed findings: squashed+rebased branch, added input validation (repo slug, PlaceholderOrg, account ID format), deterministic binding sort, extracted shared CF preamble helper, updated SKILL.md and cli-internals.md docs. Skipped 3 explicitly marked out-of-scope items (operations.md persona labels, ETag concurrency, deploy UX).

Fixed (8):

  1. squash all commits and rebase onto upstream/main: Squashed 8 commits into one clean commit on top of origin/main, then rebased the branch
  2. input-validation: repo slug and PlaceholderOrg in CF CLI paths (internal/cli/mint.go): Added gcf.ValidateRepoSlug(repo) and gcf.PlaceholderOrg rejection in runMintEnrollRepoCloudflare and runMintUnenrollRepoCloudflare, matching GCP repo enroll/unenroll validation
  3. input-validation: CF account ID format in ResolveCloudflareAuth (internal/dispatch/cf/provisioner.go): Added ValidateAccountID (^[a-f0-9]{32}$) and validate account ID in all return paths of ResolveCloudflareAuth before using in API URLs
  4. nondeterministic-output: sort bindings in createVersionWithVars (internal/dispatch/cf/provisioner.go): Sort binding names before marshaling in createVersionWithVars so version uploads produce stable metadata for debugging
  5. code-duplication: extract shared CF preamble helper (internal/cli/mint.go): Extracted shared ~20-line preamble from four CF enroll/unenroll functions into prepareCFEnrollContext helper with cfEnrollContext struct. Helper defaults effectiveName, constructs wrangler + provisioner, verifies Worker exists, and warns on preview versions
  6. stale-docs: SKILL.md missing CF triage step (skills/mint-enroll/SKILL.md): Added platform triage step at start (GCP vs --platform=cloudflare) and a short Cloudflare enrollment subsection with credentials, --worker-name, and pointer to docs
  7. stale-docs: cli-internals.md missing CF footnote (docs/guides/dev/cli-internals.md): Added footnote to Command Decomposition table: GCP IAM roles apply to --platform=gcp; Cloudflare requires CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID or wrangler login
  8. tests: repo slug validation, sorted binding keys, existing test updates: Added TestRunMintEnrollRepoCloudflare_InvalidRepoSlug, TestRunMintEnrollRepoCloudflare_PlaceholderOrg, TestRunMintUnenrollRepoCloudflare_InvalidRepoSlug, TestRunMintUnenrollRepoCloudflare_PlaceholderOrg, TestValidateAccountID, TestResolveCloudflareAuth_InvalidAccountID, TestCreateVersionWithVars_BindingOrderIsDeterministic. Updated existing tests to use valid 32-hex account IDs

Disagreed (3):

  1. operations.md persona label inconsistency: Human instruction explicitly marked this as intentional: the Mint Admin vs GCP Admin split is by design
  2. race-condition ETag concurrency: Human instruction explicitly marked this as a documented accepted limitation, out of scope
  3. deploy footgun / no-op UX: Human instruction explicitly marked this as out of scope unless trivial

Tests: passed

Decision points
  • Used format string with %s in notFoundMsg parameter for prepareCFEnrollContext to allow different error messages for enroll vs unenroll (alternatives: Separate enroll/unenroll error constants, Boolean isEnroll parameter; rationale: Format string is minimal and matches the existing pattern)
  • Validated account ID in all three return paths of ResolveCloudflareAuth rather than a single exit point (alternatives: Single validation at function end, Validation only on env var path; rationale: Each path returns independently; validating at each return point is clearest and prevents bypasses)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:52 PM UTC · Completed 7:09 PM UTC

Commit: 8f5ee5e · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed ready-for-merge All reviewers approved — ready to merge labels Aug 13, 2026
@ifireball

Copy link
Copy Markdown
Member

/fs-fix

Two small fixes from the latest review on 8f5ee5ee:

  1. error-handling (internal/dispatch/cf/provisioner.go, UpdateVars): When createVersionWithVars succeeds but deployVersionFn fails, include the orphaned version ID in the error message for debugging:
    fmt.Errorf("deploying version %s: %w", versionID, err)

  2. race-condition doc: Review notes the read-modify-write limitation is documented in CLI help and mint-administration.md but asks for explicit coverage. Add a brief mention in the Cloudflare enroll/unenroll sections of docs/cli/mint.md (if not already present) that concurrent enroll/unenroll commands against the same Worker can race and operators should run them serially — consistent with the existing "Enrollment ordering" / "Enroll serially" guidance elsewhere.

Skip squash, protected-path, persona table, and other open items.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 7:28 PM UTC · Completed 7:36 PM UTC

Commit: 8f5ee5e · View workflow run →

- Include orphaned version ID in UpdateVars deploy error message for
  easier debugging when createVersionWithVars succeeds but deploy fails
- Add "Enroll serially" / "Unenroll serially" callouts to the Cloudflare
  sections of docs/cli/mint.md, consistent with mint-administration.md

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

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 3 (human-triggered)

Addressed both review findings: (1) included orphaned version ID in UpdateVars deploy error message for debugging, (2) added race-condition serial-operation notes to Cloudflare enroll/unenroll sections in docs/cli/mint.md.

Fixed (2):

  1. Include orphaned version ID in deploy error message (internal/dispatch/cf/provisioner.go): Changed UpdateVars deploy error from 'deploying version: %w' to 'deploying version %s: %w' with versionID, so orphaned versions are identifiable when deployVersionFn fails after createVersionWithVars succeeds
  2. Add race-condition documentation to docs/cli/mint.md (docs/cli/mint.md): Added 'Enroll serially' and 'Unenroll serially' blockquote callouts to the Cloudflare enroll and unenroll sections, consistent with the existing guidance in mint-administration.md, with links to the Enrollment ordering section

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:37 PM UTC · Completed 7:55 PM UTC

Commit: c1facfe · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.

// EnsureOrgInWorker adds an org to the durable Worker's ALLOWED_ORGS.
// If the org is already present (case-insensitive), this is a no-op.
// The Worker must already exist (deployed via 'mint deploy').
func (p *Provisioner) EnsureOrgInWorker(ctx context.Context, org string) error {

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] race-condition

The enroll/unenroll Cloudflare path uses a read-modify-write cycle (GetVars → merge → UpdateVars) without concurrency control. Two concurrent commands targeting the same Worker will race, and one change may be silently lost. This is an accepted CLI limitation matching the GCP path's behavior, documented in the CLI help text and the Enrollment ordering section.

| GCP Admin (Mint) | `fullsend mint remove-role <role>` | Remove a role from the mint (deletes PEM secret by default) |
| GCP Admin (Mint) | `fullsend mint enroll <org\|owner/repo>` | Register an org or repo in the mint (does not grant Agent Platform access — use `inference provision`) |
| GCP Admin (Mint) | `fullsend mint unenroll <org\|owner/repo>` | Remove an org or repo from the mint |
| Mint Admin | `fullsend mint enroll <org\|owner/repo>` | Register an org or repo in the mint — supports `--platform=gcp` (default) and `--platform=cloudflare` |

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] scope-creep

The PR renames the persona for mint enroll/unenroll from 'GCP Admin (Mint)' to 'Mint Admin' while adjacent rows (deploy, delete, add-role, remove-role, status) still use 'GCP Admin (Mint)', creating a visual inconsistency within the same table.

Suggested fix: Either rename all multi-platform mint commands to 'Mint Admin' for consistency, or keep the original label.

Comment thread internal/cli/mint.go
// warnIrrelevantEnrollFlags prints a warning for each flag that was explicitly
// set but belongs to a different platform than the one being used. This is the
// enroll/unenroll counterpart to warnIrrelevantFlags (used by deploy/delete).
func warnIrrelevantEnrollFlags(cmd *cobra.Command, platform string) {

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] naming-consistency

Function name warnIrrelevantEnrollFlags follows a slightly different pattern than the existing warnIrrelevantFlags. These are separate functions because they have different flag sets (deploy/delete vs enroll/unenroll), so the naming is defensible, but the suffix style differs.

Suggested fix: Consider consolidating into a single parameterized helper, or accept the current naming as reflecting the distinct flag sets.

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 go Pull requests that update go code ready-for-review Agent PR ready for human review requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mint enroll / mint unenroll: add --platform=cloudflare (durable / mutable only)

1 participant