Skip to content

Commit baf181f

Browse files
committed
feat: add repo fleet evidence sweeps
1 parent dfef9d4 commit baf181f

12 files changed

Lines changed: 840 additions & 10 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8787
- `brigade center status/activity/reviews/templates`, `brigade center report plan/build/list/show/archive/review/compare/closeout`, and `brigade center actions plan/build/list/show/start/done/defer/archive` for local operator-center summaries, local report bundles, reviewed daily action queues, freshness comparison, and report closeout over work, scanner, review, handoff, tool, learning, context, project, security, and release state.
8888
- `brigade roadmap audit` and `brigade roadmap patterns` for roadmap closure checks, stale phase warnings, documented command drift, neutral pattern-family coverage, and source-pattern decisions.
8989
- `brigade repos init/list/show/scan/doctor/import-issues` for gitignored local repo-fleet readiness checks, safe setup metadata, fallback guidance detection, and `repo-fleet` work inbox imports.
90+
- `brigade repos sweep plan/run/runs/show/closeout` for explicit repo-fleet evidence refresh sweeps that run configured local read/report commands, write gitignored sweep receipts, track per-repo command status, and surface stale, failed, or unclosed sweeps through repo, center, work, and release health.
9091
- `brigade repos report plan/build/list/show/archive/closeout` and `brigade repos actions plan/build/list/show/start/done/defer/archive` for local repo-fleet operator rollups and reviewed fleet action queues using safe labels, counts, statuses, fingerprints, and receipt labels only.
9192
- `brigade work sweep closeout <sweep-id|latest>` for reviewable sweep closeout records that block unresolved pending imports, support explicit deferrals, and surface unclosed sweeps through inbox hygiene.
9293
- `brigade work backup init/status/doctor/import-issues` for read-only local backup health summaries and `backup-health` inbox imports.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,10 +515,11 @@ Roadmap and repo-fleet commands:
515515
- `brigade repos init` writes gitignored `.brigade/repos.toml`.
516516
- `brigade repos list`, `show <repo-id>`, and `scan` report safe repo metadata only: repo labels, branch, dirty counts, guidance-file presence, docs presence, test hints, handoff inboxes, publish-guard hook presence, Brigade config presence, and local receipt references.
517517
- `brigade repos doctor` reports setup gaps, and `brigade repos import-issues` creates `source: repo-fleet` task imports with stable source fingerprints.
518+
- `brigade repos sweep plan/run/runs/show/closeout` explicitly refreshes safe local evidence across configured repos, writes one fleet sweep receipt under `.brigade/repos/sweeps/`, can include optional configured read-only health commands, and records only repo ids, safe labels, command labels, status counts, receipt labels, and local log labels.
518519
- `brigade repos report plan/build/list/show/archive/closeout` builds local fleet operator reports under `.brigade/repos/reports/`, using safe repo ids, labels, counts, statuses, and receipt labels only.
519520
- `brigade repos actions plan/build/list/show/start/done/defer/archive` turns reviewed fleet reports into local fleet action queues under `.brigade/repos/actions/` without executing the suggested commands.
520521

521-
Repo fleet and pattern registry output is local and privacy preserving. It records presence, counts, labels, fingerprints, and receipt references, but does not copy repo guidance files, private paths, raw logs, scanner output, private config, owner names, exact private repo names, or raw evidence into public artifacts.
522+
Repo fleet and pattern registry output is local and privacy preserving. It records presence, counts, labels, fingerprints, command labels, log labels, and receipt references, but does not copy repo guidance files, private paths, raw logs, scanner output, private config, owner names, exact private repo names, or raw evidence into public artifacts. Fleet sweeps run only explicit foreground local read/report commands, never clone, pull, push, fix, promote, dismiss, or mutate remotes.
522523

523524
Code review producer commands:
524525

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Goal: make Brigade a safe target for local automations that discover useful work
4646
- Check local release readiness before publish operations. Status: implemented with `brigade release plan`, `doctor`, `run`, `runs`, and `show`, collecting work closeout, verification, review, scanner, security, handoff, content-guard, docs, changelog, roadmap, and git-state evidence without pushing, tagging, or mutating remotes.
4747
- Build local release candidate packets before manual publish steps. Status: implemented with `brigade release candidate plan/build/list/show/archive`, local candidate bundles, evidence JSON, release notes drafts, manual-only publish plans, candidate health warnings, and no remote mutation.
4848
- Audit roadmap closure and neutral pattern coverage. Status: started with `brigade roadmap audit`, `brigade roadmap patterns`, stale phase warnings, documented command drift checks, roadmap-audit imports, and source-pattern decision records without public reference names.
49-
- Inspect local repo fleet readiness without copying private repo contents. Status: started with gitignored `.brigade/repos.toml`, `brigade repos init/list/show/scan/doctor/import-issues`, `brigade repos report plan/build/list/show/archive/closeout`, and `brigade repos actions plan/build/list/show/start/done/defer/archive`, using safe metadata summaries, local fleet reports, reviewed fleet action queues, AGENTS and fallback guidance detection, repo-fleet imports, and daily-loop health surfacing.
49+
- Inspect local repo fleet readiness without copying private repo contents. Status: started with gitignored `.brigade/repos.toml`, `brigade repos init/list/show/scan/doctor/import-issues`, `brigade repos sweep plan/run/runs/show/closeout`, `brigade repos report plan/build/list/show/archive/closeout`, and `brigade repos actions plan/build/list/show/start/done/defer/archive`, using safe metadata summaries, explicit fleet evidence sweeps, local fleet reports, reviewed fleet action queues, AGENTS and fallback guidance detection, repo-fleet imports, and daily-loop health surfacing.
5050
- Close out scanner sweeps after review. Status: started with `brigade work sweep closeout <sweep-id|latest>`, deferred import recording, missing-reference blocking, and inbox hygiene warnings for unclosed sweeps.
5151
- Inspect local operator state from one read-only center view. Status: started with `brigade center status/activity/reviews/templates`, `brigade center report plan/build/list/show/archive/review/compare/closeout`, and `brigade center actions plan/build/list/show/start/done/defer/archive`, aggregating local work, imports, sweeps, reviews, handoffs, tools, learning, context packs, projects, security, and release state into JSON, reviewed action queues, closeouts, and local report bundles without a daemon or server.
5252

docs/operator-center.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,12 @@ Each action stores:
107107

108108
## Repo Fleet Rollups
109109

110+
`brigade repos sweep plan/run/runs/show/closeout` explicitly refreshes safe local evidence across configured repos. A sweep runs only configured foreground local read/report commands inside each enabled repo, records per-command status and safe stdout/stderr summaries, stores raw logs only in gitignored local files, and writes one receipt under `.brigade/repos/sweeps/`. Sweep receipts use safe repo ids, labels, command labels, status counts, receipt labels, and local log labels.
111+
110112
`brigade repos report plan/build/list/show/archive/closeout` builds a local fleet rollup from configured `.brigade/repos.toml` entries. Fleet reports live under `.brigade/repos/reports/` and use safe repo ids, labels, counts, statuses, fingerprints, and receipt labels only. They do not copy exact private repo names, owner names, org names, local paths, raw logs, raw scanner output, or raw evidence into public artifacts.
111113

112114
`brigade repos actions plan/build/list/show/start/done/defer/archive` turns a reviewed or deferred fleet report into a local fleet action queue under `.brigade/repos/actions/`. Fleet actions are metadata records only. They point to the safe repo label, source subsystem, source local id, safe summary, and suggested command, but do not execute the command.
113115

114-
Center status, center reviews, work brief, work doctor, release doctor, and release evidence include fleet report and fleet action queue health.
116+
Center status, center reviews, work brief, work doctor, release doctor, and release evidence include fleet sweep, fleet report, and fleet action queue health.
115117

116118
The operator center never invokes scanners, tools, reviewers, handoff ingestion, release publishing, git commands that mutate state, or remote APIs. Only `center report build`, `center report archive`, and `center actions build/start/done/defer/archive` write local gitignored center files.

docs/repo-fleet.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ brigade repos show <repo-id>
1111
brigade repos scan
1212
brigade repos doctor
1313
brigade repos import-issues
14+
brigade repos sweep plan
15+
brigade repos sweep run
16+
brigade repos sweep runs
17+
brigade repos sweep show <sweep-id>
18+
brigade repos sweep closeout <sweep-id|latest>
1419
brigade repos report plan
1520
brigade repos report build
1621
brigade repos report list
@@ -39,8 +44,29 @@ Fleet action queues are written under:
3944
.brigade/repos/actions/
4045
```
4146

47+
Fleet sweep receipts are written under:
48+
49+
```text
50+
.brigade/repos/sweeps/
51+
```
52+
4253
Fleet reports include safe repo ids, safe labels, status counts, blocker and warning counts, top pending action summaries, receipt labels, and suggested next commands. Fleet actions store local metadata only: repo id, safe label, source subsystem, source local id, status, priority or severity, safe summary, suggested command, timestamps, and source fingerprint.
4354

55+
`brigade repos sweep plan` shows which configured repos would be refreshed and which read-only local Brigade commands would run. `brigade repos sweep run` executes the configured foreground refresh for selected repos, records per-repo command summaries, and stores raw stdout and stderr only in gitignored local logs. Receipt JSON uses repo ids, safe labels, command labels, status counts, fingerprints, and local log labels. It does not store exact repo paths.
56+
57+
Repos may define optional read-only health commands in gitignored config:
58+
59+
```toml
60+
[[repo.health_command]]
61+
label = "local-health"
62+
command = "python3 -m brigade work brief --json"
63+
timeout = 120
64+
```
65+
66+
Health commands are parsed into argv and run without a shell. High-risk shell, remote-copy, and metacharacter-heavy command shapes are rejected before a sweep runs.
67+
68+
Sweep filters include `--repo <repo-id>`, `--all`, `--stale-only`, `--include-disabled`, and `--force`. `brigade repos sweep closeout` marks a sweep as `reviewed`, `deferred`, `superseded`, or `archived` after the operator has inspected the refreshed evidence. Fleet reports, center status, center reviews, work brief, work doctor, and release doctor surface stale, failed, or unclosed fleet sweeps.
69+
4470
`brigade repos actions build` requires the source fleet report to be closed out as `reviewed` or `deferred` unless `--allow-unreviewed` is passed. Repeated builds dedupe by repo id, report fingerprint, and source item fingerprint, including archived actions from the same report.
4571

4672
Privacy boundaries:

docs/roadmap-completion-plan.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ Implementation scope:
132132

133133
- Add gitignored `.brigade/repos.toml`.
134134
- Add `brigade repos init/list/show/scan/doctor/import-issues`.
135+
- Add `brigade repos sweep plan/run/runs/show/closeout`.
135136
- Add `brigade repos report plan/build/list/show/archive/closeout`.
136137
- Add `brigade repos actions plan/build/list/show/start/done/defer/archive`.
137138
- Discover repos under configured roots such as `~/repos`.
@@ -148,13 +149,15 @@ Implementation scope:
148149
- missing test command hint
149150
- missing Brigade bootstrap where opted in
150151
- Import issues into the scanner inbox as `source: repo-fleet`.
152+
- Refresh safe local evidence explicitly through fleet sweeps that run configured read/report commands, write local receipts, and feed reports and action queues without cloning, fixing, promoting, or mutating remotes.
151153

152154
Acceptance:
153155

154156
- Tests cover config init/list/show/scan/doctor text and JSON.
155157
- Tests cover repo fixtures with AGENTS, CLAUDE fallback, roadmap, publish-guard, handoff inboxes, and dirty state.
156158
- Tests prove private file contents are not copied into imports or docs.
157159
- Tests cover repo-fleet imports, dedupe, and dismissed-until-changed behavior.
160+
- Tests cover fleet sweep plan/run/runs/show/closeout text and JSON, filtering, stale-only selection, failed-repo isolation, safe log labels, and daily-loop integration.
158161
- Tests cover fleet report plan/build/list/show/archive and fleet action plan/build/list/show/start/done/defer/archive text and JSON.
159162
- Tests prove private repo names, owner names, org names, local paths, and raw evidence are not copied into public docs, fixtures, imports, handoffs, release evidence, or committed diffs.
160163

@@ -172,6 +175,12 @@ Phase 40 status:
172175
- Fleet actions write local queues under `.brigade/repos/actions/`, require a reviewed or deferred fleet report unless explicitly overridden, dedupe by repo id plus report/source fingerprint, and update action metadata only.
173176
- Center status, center reviews, work brief, work doctor, and release doctor surface fleet report and fleet action health.
174177

178+
Phase 41 status:
179+
180+
- Implemented command surface: `brigade repos sweep plan/run/runs/show/closeout`.
181+
- Fleet sweeps run explicit local read/report commands in configured repos, write gitignored receipts under `.brigade/repos/sweeps/`, store raw logs locally, and expose safe repo ids, labels, command labels, status counts, log labels, and receipt labels only.
182+
- Fleet reports, center status, center reviews, work brief, work doctor, and release doctor surface stale, failed, or unclosed fleet sweep health.
183+
175184
### 3. Inspiration Pattern Registry
176185

177186
Deliverable: a bounded local record of external workflow patterns Brigade intends to support.

src/brigade/center_cmd.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ def _reviews(target: Path) -> list[dict[str, Any]]:
406406
repo_health = repos_cmd.health(target)
407407
repo_report = repo_health.get("report") if isinstance(repo_health.get("report"), dict) else {}
408408
repo_actions = repo_health.get("actions") if isinstance(repo_health.get("actions"), dict) else {}
409-
for bucket, command in ((repo_report, "brigade repos report build"), (repo_actions, "brigade repos actions list")):
409+
repo_sweep = repo_health.get("sweep") if isinstance(repo_health.get("sweep"), dict) else {}
410+
for bucket, command in ((repo_report, "brigade repos report build"), (repo_actions, "brigade repos actions list"), (repo_sweep, "brigade repos sweep run")):
410411
top = bucket.get("top_issue") if isinstance(bucket.get("top_issue"), dict) else None
411412
if top:
412413
items.append(_item("repo-fleet", str(top.get("name") or "repo-fleet"), str(top.get("status") or "warn"), str(top.get("detail") or "repo fleet issue"), str(top.get("suggested_next_command") or command)))

src/brigade/cli.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,32 @@ def _build_parser() -> argparse.ArgumentParser:
263263
p_repos_actions_archive.add_argument("--completed", action="store_true", required=True, help="Archive completed actions.")
264264
p_repos_actions_archive.add_argument("--target", "-t", type=Path, default=Path("."), help="Repo or workspace to update.")
265265
p_repos_actions_archive.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
266+
p_repos_sweep = repos_sub.add_parser("sweep", help="Plan, run, and close out explicit repo fleet evidence sweeps.")
267+
repos_sweep_sub = p_repos_sweep.add_subparsers(dest="repos_sweep_command", metavar="<repos-sweep-command>")
268+
repos_sweep_sub.required = True
269+
for name in ("plan", "run"):
270+
p_repos_sweep_cmd = repos_sweep_sub.add_parser(name, help=f"{name.title()} a repo fleet evidence sweep.")
271+
p_repos_sweep_cmd.add_argument("--target", "-t", type=Path, default=Path("."), help="Repo or workspace to inspect.")
272+
p_repos_sweep_cmd.add_argument("--repo", dest="repo_ids", action="append", default=[], help="Repo id to include. May be repeated.")
273+
p_repos_sweep_cmd.add_argument("--all", dest="all_repos", action="store_true", help="Include all enabled repos.")
274+
p_repos_sweep_cmd.add_argument("--stale-only", action="store_true", help="Only include repos without a successful sweep.")
275+
p_repos_sweep_cmd.add_argument("--include-disabled", action="store_true", help="Allow disabled configured repos.")
276+
p_repos_sweep_cmd.add_argument("--force", action="store_true", help="Force a refresh even when evidence is fresh.")
277+
p_repos_sweep_cmd.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
278+
p_repos_sweep_runs = repos_sweep_sub.add_parser("runs", help="List repo fleet sweep receipts.")
279+
p_repos_sweep_runs.add_argument("--target", "-t", type=Path, default=Path("."), help="Repo or workspace to inspect.")
280+
p_repos_sweep_runs.add_argument("--limit", type=int, default=20, help="Maximum sweeps to list.")
281+
p_repos_sweep_runs.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
282+
p_repos_sweep_show = repos_sweep_sub.add_parser("show", help="Show one repo fleet sweep receipt.")
283+
p_repos_sweep_show.add_argument("sweep_id", help="Sweep id, unique prefix, or latest.")
284+
p_repos_sweep_show.add_argument("--target", "-t", type=Path, default=Path("."), help="Repo or workspace to inspect.")
285+
p_repos_sweep_show.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
286+
p_repos_sweep_closeout = repos_sweep_sub.add_parser("closeout", help="Close out one repo fleet sweep review.")
287+
p_repos_sweep_closeout.add_argument("sweep_id", nargs="?", default="latest", help="Sweep id, unique prefix, or latest.")
288+
p_repos_sweep_closeout.add_argument("--target", "-t", type=Path, default=Path("."), help="Repo or workspace to update.")
289+
p_repos_sweep_closeout.add_argument("--status", choices=["reviewed", "deferred", "superseded", "archived"], default="reviewed")
290+
p_repos_sweep_closeout.add_argument("--reason", default=None, help="Review reason.")
291+
p_repos_sweep_closeout.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
266292

267293
# handoff
268294
p_handoff = sub.add_parser("handoff", help="Inspect memory handoff inbox health.")
@@ -1601,6 +1627,35 @@ def main(argv=None) -> int:
16011627
return repos_cmd.actions_archive_completed(target=args.target, json_output=args.json)
16021628
parser.error(f"unknown repos actions command: {args.repos_actions_command}")
16031629
return 2
1630+
if args.repos_command == "sweep":
1631+
if args.repos_sweep_command == "plan":
1632+
return repos_cmd.sweep_plan(
1633+
target=args.target,
1634+
repo_ids=args.repo_ids,
1635+
all_repos=args.all_repos,
1636+
stale_only=args.stale_only,
1637+
include_disabled=args.include_disabled,
1638+
force=args.force,
1639+
json_output=args.json,
1640+
)
1641+
if args.repos_sweep_command == "run":
1642+
return repos_cmd.sweep_run(
1643+
target=args.target,
1644+
repo_ids=args.repo_ids,
1645+
all_repos=args.all_repos,
1646+
stale_only=args.stale_only,
1647+
include_disabled=args.include_disabled,
1648+
force=args.force,
1649+
json_output=args.json,
1650+
)
1651+
if args.repos_sweep_command == "runs":
1652+
return repos_cmd.sweep_runs(target=args.target, limit=args.limit, json_output=args.json)
1653+
if args.repos_sweep_command == "show":
1654+
return repos_cmd.sweep_show(target=args.target, sweep_id=args.sweep_id, json_output=args.json)
1655+
if args.repos_sweep_command == "closeout":
1656+
return repos_cmd.sweep_closeout(target=args.target, sweep_id=args.sweep_id, status=args.status, reason=args.reason, json_output=args.json)
1657+
parser.error(f"unknown repos sweep command: {args.repos_sweep_command}")
1658+
return 2
16041659
parser.error(f"unknown repos command: {args.repos_command}")
16051660
return 2
16061661
if cmd == "handoff":

0 commit comments

Comments
 (0)