Repository navigation
Track downstream migration for effective Git credential preflight #1598
Description
Context
#1597 makes Fro Bot fail closed when effective Git credentials remain available on credential-withheld events: pull_request, issue_comment, and issues.
A default-branch audit on September 11, 2026 identified 29 downstream repositories using fro-bot/agent: 26 public and 3 private. GitHub's dependents count is stale or incomplete, so this checklist is based on direct workflow inspection.
The duplicate marcusrbrown/dashboard fork has been deleted; fro-bot/dashboard is the canonical repository.
Required posture
The runtime requirement on credential-withheld events is:
- no effective
http.*.extraheaderin local, global, system, worktree,include, orincludeIfconfig; - no credential embedded in the
originURL.
persist-credentials: false removes the common checkout-generated local header. Static workflow inspection verifies that checkout posture; it cannot prove the absence of host-level configuration.
Flat persist-credentials: false is the default. Use the event-gated form only when the same job needs persisted Git credentials for schedule or workflow_dispatch:
persist-credentials: >-
${{ !contains(fromJSON('["pull_request", "issue_comment", "issues"]'), github.event_name) }}That list intentionally matches fro-bot/agent's current credential classification. pull_request_review_comment and discussion_comment remain deferred surfaces; changing their posture requires a policy change in the action first, not a downstream-only expansion.
Checkout posture verified
- bfra-me/ha-addon-repository
- bfra-me/works
- fro-bot/dashboard
- marcusrbrown/cortexkit_anthropic-auth
- marcusrbrown/dev-like
- marcusrbrown/infra
- marcusrbrown/marcusrbrown
- marcusrbrown/marcusrbrown.com
- marcusrbrown/marcusrbrown.github.io
- marcusrbrown/mothership
- marcusrbrown/renovate-config
- marcusrbrown/sparkle
- marcusrbrown/systematic
- marcusrbrown/tokentoilet
- marcusrbrown/vbs
- thejustinwalsh/three-flatland
Migration merged
- bfra-me/.github — fix(ci): withhold checkout credentials on affected triggers bfra-me/.github#2717
- bfra-me/renovate-action — fix(ci): withhold checkout credentials on affected triggers bfra-me/renovate-action#3777
- fro-bot/.github — fix(ci): gate issue runs and avoid persisted credentials .github#3878
- fro-bot/space-bus — fix(ci): withhold checkout credentials on affected triggers space-bus#174
- marcusrbrown/.dotfiles — fix: harden credential posture marcusrbrown/.dotfiles#2576
- marcusrbrown/containers — fix(ci): withhold checkout credentials on affected triggers marcusrbrown/containers#789
- marcusrbrown/gpt — fix(ci): set fro-bot checkout credentials posture marcusrbrown/gpt#2762
- marcusrbrown/opencode-copilot-delegate — fix(fro-bot): stop persisting credentials on mention runs marcusrbrown/opencode-copilot-delegate#396
- Private repositories — 3 total: checkout posture verified.
Accepted risk while archived
- fro-bot/tokentoilet — workflows cannot run while the repository is archived; remediate before unarchiving.
- marcusrbrown/copiloting — workflows cannot run while the repository is archived; remediate before unarchiving.
Runtime verification
Release v0.111.0 contains #1597 and advanced the v0 branch to 620a314e241ec2f4a72167eb1ad2c5a3a909cc86.
Each active repository needs one successful pull_request, issue_comment, or issues run after the release to verify the effective Git config on the actual runner. A clean static checkout audit is necessary, not sufficient. The two archived repositories are excluded until unarchived.
Status: v0.111.0 published; 8 of 27 active repositories verified.
Verified by a qualifying post-release run observed on a release containing the credential preflight:
- fro-bot/.github — run 35527845273 (
issue_comment, 2026-09-20 18:08 UTC) - fro-bot/dashboard — run 35493818966 (
pull_request, 2026-09-20 06:18 UTC) - marcusrbrown/.dotfiles — run 35494527976 (
pull_request, 2026-09-20 06:35 UTC) - marcusrbrown/infra — run 35528159045 (
pull_request, 2026-09-20 18:14 UTC) - marcusrbrown/marcusrbrown.github.io — run 35496927961 (
issues, 2026-09-20 07:27 UTC) - marcusrbrown/mothership — run 36353516394 (
issues, 2026-09-27 21:57 UTC) - marcusrbrown/systematic — run 35483359948 (
issues, 2026-09-20 02:17 UTC) - marcusrbrown/tokentoilet — run 35486556143 (
pull_request, 2026-09-20 03:30 UTC)
Remaining 19: 1 collector-covered repository has a qualifying run still on a pre-preflight pin (thejustinwalsh/three-flatland), 13 have no qualifying run yet, 2 have unavailable evidence, and the 3 private repositories are outside the collector's reach. None of those states is evidence of migration or of removal. The action is mention-gated, so a repository that invokes it only on schedule may never verify through this path — an expected steady state rather than a regression.
Runtime evidence accumulates from organic affected-event runs; this tracker does not create synthetic issues or comments merely to exercise the preflight.
Completion
The downstream checkout migration is complete: all nine active migrations landed before v0.111.0. Keep this issue open until runtime verification reaches 27 of 27 active repositories.
Activity
fro-bot commentedon Sep 11, 2026
Triage: downstream migration tracking for the effective-credential preflight
Classification: tracking / coordination issue, not a defect. No code change is required in this repo to close it. The one thing that is actionable here is that the checklist's stated compliance criterion is narrower than what #1597 actually enforces, and the completion gate as written is not reachable given how this action is released.
What I verified
I read
origin/fix/effective-git-credential-preflight(the #1597 branch, 5 commits ahead ofmain) and ran its tests locally.The preflight itself is real and already on
mainin narrow form —src/harness/phases/bootstrap.ts:79-86callsassertNoPersistedGitCredentialsandcore.setFaileds on a hit. The withheld event set is exactlypull_request,issue_comment,issues, perclassifyEventinpackages/runtime/src/agent/response-delivery.ts:30-44. Thepersist-credentialsexpression in the issue body matches that set exactly — it is correct as written.#1597 widens the check substantially.
git config --local --get-regexp '^http\..*\.extraheader$'becomesgit config --includes --name-only --get-regexp '^http\.(.*\.)?extraheader$', plus arev-parse --absolute-git-dirrepo-context probe, and every ambiguous exit code now denies instead of passing.I measured the delta by running the branch's new integration suite against
main's implementation:The 13 vectors
mainmisses include global-scope headers, system-scope headers,includeIf.gitdirtargets, worktree-scoped config, bare repositories, bare-keyhttp.extraheader(no URL subsection), malformed config, and invalidGIT_DIR.Finding 1 — the checklist's compliance criterion no longer matches the check (highest impact)
The "Required posture" section defines compliance as "a checkout that sets
persist-credentials: false." After #1597 that is necessary but not sufficient. The check now reads the effective config across local, global, system, worktree, andincludeIfscopes. A repo with a perfectly compliantactions/checkoutstill fails if anything in the job leaves a host-scopehttp.*.extraheader— a container image with a baked/etc/gitconfig, a self-hosted runner, a composite setup action, or any earlier step doinggit config --global.This means the 16 entries under "Already compliant" were validated against the old criterion, and static workflow inspection cannot prove the new one. They are probably fine — hosted runners don't ship an extraheader — but the list is asserting more than the evidence supports. I'd relabel that section as "checkout is compliant" rather than "compliant", and treat the first post-release
issuesrun in each repo as the actual verification.Finding 2 — the completion gate is unreachable as written
The issue says migration must land "before consuming the breaking behavior from #1597." But:
docs/examples/fro-bot.yaml:407tells consumers to usefro-bot/agent@v0— a floating major tag.breaking: true → release: minorto.releaserc.yaml, keeping the project in0.x.So #1597 ships as a routine minor,
v0moves, and every@v0consumer picks up fail-closed behavior on their next withheld-event run with no opt-in window. There is no "before consuming" for them to migrate in.Only two orderings actually satisfy the stated gate:
Finding 3 — local-scope self-heal would collapse most of the migration
actions/checkoutwrites its extraheader into local config only. The action already runs before the model and already inspects that config. It couldgit config --local --unset-allthe matched key (and rewrite a credentialedorigin) instead of failing, while still failing closed on global/system/includeIfscopes it can't safely own.That would make ~all 12 pending repos a no-op and remove the release-ordering problem entirely. The tradeoffs are real — mutating a consumer's checkout is surprising, and failing loudly arguably produces better long-term hygiene — so this deserves an explicit decision on #1597 rather than an implicit one. Worth noting the security property is identical either way: an unset local extraheader is exactly as unreadable to the model as a refused run.
Finding 4 — the event list will drift, 30 times over
The
["pull_request", "issue_comment", "issues"]array duplicatesclassifyEventinto every downstream workflow.pull_request_review_commentanddiscussion_commentare documented inresponse-delivery.ts:24-28as deferred surfaces. If either is later reclassified asaffected, all 30 repos silently regress to persisting credentials on a withheld event — and the preflight would catch it as a hard failure across the fleet at once.#1597 already builds the right guard for this repo: the new
scripts/fro-bot-workflow.test.tsblock derives the expected persist decision fromresolveResponseDeliveryand checks it against the workflow's own declaredon:triggers, so the YAML and the policy can't diverge. That pattern is the fix — it just needs to be reachable from downstream repos (a published snippet, a composite action, or an exported expression) rather than living only in this repo's test suite.Finding 5 — the error message dropped the matched key name
mainnamed the offending config key in the error (git-credential-check.ts:37-41). #1597'sERR_HEADER_FOUNDdoes not, even though it runs with--name-only, so the matched key is already non-secret by construction. For an issue whose entire premise is 30 repos debugging this failure remotely, putting the key name back is high-value and zero-risk. Small suggestion for #1597.Finding 6 — guidance inconsistency for the remediation PRs
The issue recommends the event-gated expression as the default posture, but
docs/examples/fro-bot.yaml:401— the canonical example downstream repos copy — uses flatpersist-credentials: false, in a single job that handles all triggers includingscheduleandworkflow_dispatch. Flatfalseis the simpler and safer default; the gated form is only needed when the same job also needs a persisted checkout credential for autonomous pushes.Worth stating which is the default before writing 12 PRs, otherwise they'll diverge. The per-repo notes already hint at this (some say "set
false", others say "add event-gated"), so the distinction exists — it just isn't stated as a rule.Proposed next steps
Blocking on #1597:
ERR_HEADER_FOUND(Finding 5).On this issue:
3. Rewrite "Required posture" to state the real criterion: no effective
http.*.extraheaderin any scope, and no credential in theoriginURL. Note thatpersist-credentials: falsecovers the common case but not host-level config.4. State the default (flat
false) and when to use the gated form (Finding 6).5. Resolve the two archived repos now — archived repos cannot dispatch workflows, so the live risk is zero. Record "accepted risk: archived, workflows cannot run" and strike them rather than carrying them as blockers.
6. Re-label "Already compliant" as checkout-level compliance, and add a post-release verification pass (Finding 1).
Follow-up, separate issue:
7. Make the persist-credentials expression consumable from one place so it can't drift across 30 repos (Finding 4).
What I could not verify
ghis unavailable in this run, so I could not confirm the repository inventory, the 30/27/3 counts, the per-repo workflow contents, or the archived status offro-bot/tokentoiletandmarcusrbrown/copiloting. Everything above is derived from this repository's source, the #1597 branch, and local test execution.Run Summary
fix(ci): make runtime verification observation-only
feat(ci): sweep downstream runtime verification in the daily report
feat(ci): sweep downstream runtime verification in the daily report