feat(campaign): plan export + execution-by-reuse (v5.7 Fase 2 Block 4c)
Completes Block 4 (4b backlog + 4c export/execution). Asymmetric: plan
export is new testable code; execution is pure reuse of the existing
per-repo implement/rollback (no new execution machinery), per the plan's
"reuse existing backup/rollback".
Plan export ("planer følger arbeidsstedet"):
- scanners/lib/campaign-export.mjs (pure, now injected, 8 tests):
planExportPath(repo,sessionId) -> <repo>/docs/config-audit-plan-<sessionId>.md
(sessionId-keyed so same-day re-audits never collide);
buildPlanExportDocument({...,now}) -> provenance header + verbatim plan.
- scanners/campaign-export-cli.mjs (-cli, read-only by default, 10 tests):
--repo resolves the repo's linked session, reads its action-plan.md,
assembles the doc, emits {exportable,problems,targetPath,document}. Two
gates -> exit 1 advisory: no-session-linked / no-action-plan. Writes the
file ONLY under opt-in --write (byte-faithful copy; the LLM never re-types
a 200-line plan). --sessions-dir override for hermetic tests; exit 0/1/3.
Command: commands/campaign.md gains an `export <path>` mode (Step 6:
preview -> approve -> --write), then routes the user to the existing
/config-audit implement (backup + verify) + rollback + set-status
implemented. Nothing auto-written (Verifiseringsplikt).
Byte-stable: lib + -cli + command-doc only -> scanner count stays 15,
agents 7, commands 21 (export is a mode, not a new command), SC-5 +
backcompat suite untouched. suite 1150->1168. Block 4a (migrateLedger)
still deferred to the first breaking schema change.
Docs: CLAUDE.md section + badge 1150->1168/65->67 files; README badge +
campaign row + Testing prose (fixed stale 1055/59 -> true 1168/67).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
49833aded8
commit
319e5541c9
7 changed files with 575 additions and 22 deletions
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: config-audit:campaign
|
||||
description: Machine-wide audit campaign — track which repos are pending/audited/planned/implemented across sessions, with a machine-wide roll-up. Human-approved writes only.
|
||||
argument-hint: "[init | add <path>... | add --discover <root> | set-status <path> <status>]"
|
||||
argument-hint: "[init | add <path>... | set-status <path> <status> | export <path>]"
|
||||
allowed-tools: Read, Write, Edit, Bash, Glob
|
||||
model: opus
|
||||
---
|
||||
|
|
@ -21,19 +21,28 @@ and applied **only on explicit approval**, by invoking one deterministic write-C
|
|||
The command never hand-edits the ledger JSON.
|
||||
|
||||
This is the **THIN** campaign surface (ledger + roll-up + status + a cross-repo prioritized
|
||||
backlog to pick from). Execution is a later block — this command does not run audits or apply
|
||||
fixes itself; it tracks where each repo stands and shows what to tackle next.
|
||||
backlog to pick from + plan **export**). It does not run audits or apply fixes itself: it tracks
|
||||
where each repo stands, shows what to tackle next, exports a planned repo's plan into that repo's
|
||||
own `docs/`, and points at the **existing** per-repo `/config-audit implement` (backup + apply +
|
||||
verify) and `/config-audit rollback` for execution — Block 4c reuses that machinery, it does not
|
||||
reinvent it.
|
||||
|
||||
## Two CLIs back this command
|
||||
## Three CLIs back this command
|
||||
|
||||
- **Read (report):** `scanners/campaign-cli.mjs` — loads + validates the ledger, emits the
|
||||
repo list + roll-up. Never writes.
|
||||
repo list + roll-up + backlog. Never writes.
|
||||
- **Write (mutate):** `scanners/campaign-write-cli.mjs` — `init` / `add` / `set-status`, each a
|
||||
thin wrapper over the invariant-enforcing lib transforms + save. Invoked **only** after the
|
||||
user approves a specific action.
|
||||
- **Export:** `scanners/campaign-export-cli.mjs` — `--repo <path>` resolves the repo's linked
|
||||
session, reads its `action-plan.md`, and assembles a `docs/config-audit-plan-<sessionId>.md`.
|
||||
Read-only (a preview) by default; it writes the file **only** under `--write`, which is
|
||||
invoked **only** after the user approves. The CLI writes the file byte-faithfully — the plan is
|
||||
never re-typed.
|
||||
|
||||
Both take `--ledger-file <path>` (defaults to the durable path) and `--output-file <path>`;
|
||||
the write-CLI also takes `--reference-date <YYYY-MM-DD>` (the audit date stamp).
|
||||
All take `--ledger-file <path>` (defaults to the durable path) and `--output-file <path>`; the
|
||||
write-CLI + export-CLI also take `--reference-date <YYYY-MM-DD>` (the date stamp), and the
|
||||
export-CLI takes `--sessions-dir <path>` (defaults to `~/.claude/config-audit/sessions`).
|
||||
|
||||
## Implementation
|
||||
|
||||
|
|
@ -46,6 +55,7 @@ From `$ARGUMENTS`, pick the mode:
|
|||
- `add <path>...` → add one or more repo paths.
|
||||
- `add --discover <root>` → find git repos under `<root>` and let the user pick which to add.
|
||||
- `set-status <path> <status>` → transition a tracked repo (`status` ∈ pending/audited/planned/implemented).
|
||||
- `export <path>` → export a planned repo's action plan into that repo's own `docs/`.
|
||||
- `help` → show this surface and stop.
|
||||
|
||||
Set a shared date stamp for any write: `TODAY=$(date +%F)`.
|
||||
|
|
@ -165,7 +175,48 @@ node ${CLAUDE_PLUGIN_ROOT}/scanners/campaign-write-cli.mjs set-status <path> <st
|
|||
Exit **3** = invalid status, untracked repo, or no ledger → report the message plainly and do
|
||||
not retry blindly. On success, read the result and re-show the updated roll-up + repo row.
|
||||
|
||||
### Step 6: Next steps
|
||||
### Step 6 (mode `export`): Export a repo's plan to its own `docs/` — preview, approve, write
|
||||
|
||||
"Planer følger arbeidsstedet": a planned repo's action plan belongs in **that repo's** `docs/`,
|
||||
not buried in a session dir. This step copies it there, byte-faithfully.
|
||||
|
||||
**Preview first (read-only — never writes).** The repo must be tracked and have a linked session
|
||||
that carries an `action-plan.md` (i.e. `/config-audit plan` has run there). Run without `--write`:
|
||||
|
||||
```bash
|
||||
node ${CLAUDE_PLUGIN_ROOT}/scanners/campaign-export-cli.mjs --repo "<path>" \
|
||||
--reference-date "$TODAY" \
|
||||
--output-file ~/.claude/config-audit/sessions/campaign-export.json 2>/dev/null; echo $?
|
||||
```
|
||||
|
||||
Exit **0** = previewable, **1** = tracked but not exportable yet, **3** = error (untracked repo,
|
||||
no/corrupt ledger). Read `~/.claude/config-audit/sessions/campaign-export.json` with the Read tool.
|
||||
|
||||
- **Exit 1 — read `problems`** and guide, then stop (nothing to export):
|
||||
- `no-session-linked` → "`<name>` has no linked audit session. Link one with
|
||||
`/config-audit campaign set-status <path> <status> --session <id>`, or audit + plan it first."
|
||||
- `no-action-plan` → "`<name>`'s session has no plan yet. Run `/config-audit plan` in that repo
|
||||
first, mark it `planned`, then export."
|
||||
- **Exit 0 — show, then ask.** Tell the user the destination (`targetPath`) and a **short** preview
|
||||
— the first ~12 lines of `document` only, never the whole file, never the raw JSON (UX rules).
|
||||
Ask for explicit approval to write it.
|
||||
|
||||
**On approval, write it** (the CLI does the faithful copy — do NOT hand-write the file):
|
||||
|
||||
```bash
|
||||
node ${CLAUDE_PLUGIN_ROOT}/scanners/campaign-export-cli.mjs --repo "<path>" --write \
|
||||
--reference-date "$TODAY" \
|
||||
--output-file ~/.claude/config-audit/sessions/campaign-export.json 2>/dev/null; echo $?
|
||||
```
|
||||
|
||||
Confirm: "Plan exported to `<targetPath>`." Then hand off to the **existing** execution machinery
|
||||
(Block 4c reuses it — this command does not run it for you):
|
||||
|
||||
> To **execute**: run `/config-audit implement` in `<path>` — it backs up every changed file,
|
||||
> applies the plan, and verifies. To **undo**: `/config-audit rollback`. When done, record it:
|
||||
> `/config-audit campaign set-status <path> implemented`.
|
||||
|
||||
### Step 7: Next steps
|
||||
|
||||
Tailor to where the campaign stands:
|
||||
|
||||
|
|
@ -174,20 +225,27 @@ Tailor to where the campaign stands:
|
|||
- **Pending repos exist:** "Run `/config-audit` in a pending repo to audit it, then
|
||||
`/config-audit campaign set-status <path> audited` to record the result here."
|
||||
- **Audited but not planned:** "`/config-audit plan` in that repo, then mark it `planned`."
|
||||
- **Planned repos exist:** "`/config-audit campaign export <path>` to drop the plan into that
|
||||
repo's own `docs/`, then `/config-audit implement` there to execute it (backup + verify)."
|
||||
- **Backlog has items:** point at the top backlog repo and the natural next verb for its status
|
||||
(audit → plan → export → implement).
|
||||
- Always: the campaign survives this session — re-run `/config-audit campaign` anytime to see
|
||||
the machine-wide picture.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Read-only report, human-approved writes.** `campaign-cli` never writes; every mutation goes
|
||||
through `campaign-write-cli` and only after explicit approval, exactly mirroring how
|
||||
`/config-audit knowledge-refresh` gates register writes.
|
||||
- **Deterministic core, not byte-stable command.** The lib transforms + both CLIs are
|
||||
- **Read-only report, human-approved writes.** `campaign-cli` never writes; every mutation —
|
||||
ledger changes via `campaign-write-cli`, plan exports via `campaign-export-cli --write` — happens
|
||||
only after explicit approval, exactly mirroring how `/config-audit knowledge-refresh` gates
|
||||
register writes. The export-CLI's default (no `--write`) is a read-only preview.
|
||||
- **Deterministic core, not byte-stable command.** The lib transforms + all three CLIs are
|
||||
unit-tested and deterministic (`--reference-date` injected); this command's orchestration is
|
||||
judgment-driven and deliberately **not** in the snapshot suite (like `/config-audit optimize`
|
||||
and `knowledge-refresh`).
|
||||
- The `-cli` suffix keeps both CLIs out of the scan-orchestrator, so the scanner count and the
|
||||
byte-stable snapshot suite are unaffected.
|
||||
- **THIN scope:** ledger + roll-up + status + a read-only cross-repo prioritized backlog. The
|
||||
backlog is a derived pick-list (`buildBacklog`, severity-weighted); execution is a later block —
|
||||
this command tracks state and shows what to tackle next, it does not run audits or apply fixes.
|
||||
- The `-cli` suffix keeps all three CLIs out of the scan-orchestrator, so the scanner count and
|
||||
the byte-stable snapshot suite are unaffected.
|
||||
- **THIN scope:** ledger + roll-up + status + a cross-repo prioritized backlog + plan export.
|
||||
Execution is **not** reinvented here — `export` drops a planned repo's plan into its own `docs/`
|
||||
(a durable record), and the user runs the existing `/config-audit implement` (backup + apply +
|
||||
verify) + `/config-audit rollback` to execute and undo. This command tracks state and routes the
|
||||
work; it does not run audits or apply fixes itself.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue