Fleet-Fix — one transform, every affected repo
Paths.
analysis-results/…andprogress-tracker/…in this skill are the default workspace layout. They resolve throughlocations.yamlin$TRAUST_CONFIG_HOME(docs/setup.md, Storage locations); substitute your configured roots.
Lens 3 identifies patterns ("1 weakness × N repos"); this skill buys them down as ONE campaign: a single reviewed transform, applied mechanically fleet-wide, shipped as a coordinated batch of MRs. Distinct-vulnerability count drops fastest per engineering hour here.
Hard gates (never waived)
- The spec is the change. Every transform lives as a schema-validated
spec (
contracts/schemas/fleet-fix.schema.json) inspecs/, carrying its own golden tests.apply_fleet_fix.pyrefuses to touch any repo while a golden test fails. - Diffs before forks, forks before MRs, humans before both. The
applier only modifies working clones and emits
<repo>-<id>.diff+ result JSON. Opening MRs is a separate stage that requires the user's explicit batch approval of the reviewed diffs — never auto-merge, never MR-on-apply. - Allowlist scope. The matcher touches only its
file_glob; guards (max_files_changed, clean-tree) revert anything that oversteps.
Procedure
1. Identify the fleet
One deterministic command — never hand-grep:
python3 harnessing/7-remediate/fleet-fix/scripts/fleet_targets.py \
(--cwe CWE-1104 | --rollup "pipelineRef" | \
--module github.com/openshift/library-go [--ecosystem npm] | \
--repos org/a org/b) \
--out /tmp/fleet-fix/<id>/fleet.json
For dependency-bump campaigns driven by a specific CVE, an
/impact-analysis artifact is a ready-made fleet: pass the repos
classified affected/likely_affected via --repos (e.g.
jq -r '.repos[] | select(.classification=="affected" or .classification=="likely_affected") | .repo' analysis-results/impact/<cve>-impact-analysis.json). This scopes the
campaign to demonstrated exposure instead of every importer of the
module.
Sources: --cwe reads the insecure-patterns pattern repo lists
(CWE-class fleets); --rollup greps PROGRESS.md rows for the campaign
cross-cutting patterns; --module queries portfolio-graph.db
imports_package/depends_on edges — dependency fleets: every repo
importing the module or any package under it. --module resolves both
Go (module:/srcpkg:) and non-Go (pkg:<eco>/<name>) consumers; a
bare short name matches across every ecosystem, so pair it with
--ecosystem <eco> (npm/pypi/maven/cargo/ruby/nuget/docker/actions/helm)
when the coordinate is non-Go to avoid a cross-ecosystem name collision.
Every target is enriched
from repo-graph: product reach (fix highest-blast-radius repos
first — the output is already sorted by it) and owner team (MR
routing at stage 5). Targets missing from repo-graph are listed with a
URL guess, never dropped. fleet.json is the campaign's target list of
record.
Optional accelerator — findings-db candidate listing. When scoping a fleet by finding state rather than pattern/module (e.g. "every repo with an open finding matching this fingerprint"), the C9 projection answers in one query (
/findings-db,v_open+graph_edges). Use it to shortlist, then feed the repos throughfleet_targets.py --repos …so the target list of record still carries the standard enrichment — and confirm finding state against the live ledger before generating diffs; the DB can be one census behind.
2. Author or select the transform spec
specs/<id>.yaml — matcher (pinned_ref_line for guarded line rewrites
with a git ls-remote resolver; ast_grep for structural rewrites),
rewrite template, guards, and at least one positive + one negative golden
test. New specs get a human review of the spec itself plus one
hand-reviewed golden diff before fleet application.
3. Apply across the fleet (diffs only)
For each repo (workflow batch for large fleets):
GIT_ALLOW_PROTOCOL=https git clone -- <repo> /tmp/fleet-fix/<id>/<repo> && \
python3 harnessing/7-remediate/fleet-fix/scripts/apply_fleet_fix.py \
--spec harnessing/7-remediate/fleet-fix/specs/<id>.yaml \
--repo /tmp/fleet-fix/<id>/<repo> \
--out-dir /tmp/fleet-fix/<id>/out
Collect the per-repo results: applied / no-match / unresolved / guard
refusals. Where the target repo has usable checks, run
harnessing/7-remediate/remediate-finding/run_checks.sh against the modified clone
and record outcomes.
4. Human review of the batch
Present every diff (they are small by construction) with the campaign summary: N applied, M no-match (pattern already fixed — burndown evidence), K unresolved. Stop here unless the user approves opening MRs.
5. Open MRs (approved batches only)
Reuse the remediate-finding fork flow per repo: private fork, branch
fleet-fix/<id>, the applier's diff as the commit, MR/PR with the shared
campaign body (pattern, evidence, spec link, golden tests) via gh/glab.
Track MR URLs in the campaign result JSON.
6. Burndown
track-findings ingests merge events against the pattern's findings;
insecure-patterns/trends show the pattern shrinking. Re-run step 3 on a
cadence: no-match on every repo = pattern extinct.
Outputs
| Artifact | Purpose |
|---|---|
specs/<id>.yaml |
The reviewed transform (goldens included) |
<repo>-<id>.diff / -result.json per repo |
Reviewable change + application record |
| Campaign summary (applied / no-match / unresolved / MRs) | The batch-approval packet and burndown record |
Failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
GOLDEN TEST FAILED |
spec drifted from its own expectations | fix the spec, never the applier |
exit 2 working tree is not clean |
reused clone | fresh clone per application |
unresolved: no URL in window |
file shape differs from matcher assumptions | extend the spec (with a new golden) — don't hand-edit the repo |
| many no-match results | pattern already remediated there | that's burndown, record it |