feat: promote /prepare-compact into vault-cli plugin (spec 034) - #95
Conversation
There was a problem hiding this comment.
Now I have enough context to produce the review. The mechanical funnel found zero findings across 74 rule YAMLs. The changed files are all markdown/documentation (command docs, spec, prompt record, changelog) — no code files. The selector-mode short-circuit applies.
Step 4 short-circuit: applicable set is empty AND mechanical findings are zero → selector clean — no adjudication needed.
Proceeding to Step 5 with my manual review of the changed files.
PR Review: feat/prepare-compact-plugin-command → master
Changed files (from diff stat)
| File | +Lines | Purpose |
|---|---|---|
CHANGELOG.md |
+4 | ## Unreleased feat bullet |
commands/prepare-compact.md |
+102 | New slash command |
prompts/completed/185-*.md |
+229 | Dark-factory prompt execution record |
specs/in-progress/034-*.md |
+132 | Spec |
Step 3a — LICENSE
LICENSE file present at repo root. ✓
Step 3b — precommit (selector mode)
Skipped — selector mode defers lint/test to CI. ✓
Step 4 — Mechanical funnel
ast-grep-runner: 0 findings across 74 rule YAMLs. ✓
Step 4 (selector) — Candidates
Changed files are all markdown/YAML metadata — no go, py, js, ts, sh source files. No judgment-rule glob matches. Selector clean — no adjudication needed.
Manual Review (changed files only)
commands/prepare-compact.md
Security model — allowed-tools is correctly granular:
- No bare
Bash, noEdit, noTask, noAskUserQuestion - Six scoped read-only/status Bash subcommands only:
git status:*,git log:*,git rev-parse:*,pgrep:*,dark-factory status:*,docker ps:* mkdir:*only for creating the checkpoint directoryWritefor checkpoint file;Skillfor sibling call;Read/Globfor vault traversal- This is the correct pattern — the tool surface cannot commit, push, or kill even if prose is misread. ✓
Session-id trust boundary — correctly derived from the session's own scratchpad path, not from user input or file content. ✓
Read-and-report-only invariant — "Never auto-commit, auto-push, kill a daemon" appears verbatim in the body. ✓
Sibling vault-cli:sync-progress reference — correctly namespaced, no fallback logic (same-plugin sibling, guaranteed present). ✓
Resume block — 4-field schema (Next action:, Live background:, Un-pushed / uncommitted:, Open decision:) appears exactly once each; no extra or renamed fields. ✓
Verdict strings — both ✅ Ready to compact and ⚠️ Not compact-safe yet present verbatim. ✓
No closer panel — ⚪ DONE appears exactly once, inside the prohibition sentence (Do NOT emit a session-close-style closer panel — no ⚪ DONE block). 👤 You: appears zero times. ✓
Namespace contrast — /vault-cli:session-close appears fully-namespaced; no bare /session-close found. ✓
Checkpoint path — ~/.claude/compact-checkpoints/<session-id>.md appears verbatim; per-session (not shared-fixed-path) rationale present. ✓
Path announcement — "state the full path in your final message" present, ensuring the checkpoint file is announced, not silently written. ✓
Graceful degradation — dark-factory status || echo "no daemon" and docker ps || echo "no containers" appear as real shell fallback wiring inside the fenced snippet (not just prose). ✓
Daemon interpretation — "A stopped daemon does not mean stopped work" and "A running dark-factory daemon is worth pausing for" both present verbatim. ✓
AC fix verification — The Plan noted that the original AC had an unsatisfiable grep count of 0 for ⚪ DONE (the faithful port necessarily contains the string inside the prohibition sentence). The spec AC at line 52 explicitly documents this: "NOTE: the source file contains ⚪ DONE once, on its line 82, inside the prohibition. An AC demanding 0 is unsatisfiable by a faithful port..." — the spec correctly updated the AC to expect count=1. The command file is therefore correct as-written. ✓
CHANGELOG.md
## Unreleased section added directly above ## v0.112.0, with a properly formatted feat: bullet describing the new command. ✓
specs/in-progress/034-prepare-compact-plugin-command.md and prompts/completed/185-*.md
These are dark-factory internal artifacts. The spec is at status: verifying (its own workflow state); the prompt is status: completed. These files are metadata about the development process, not shipped code.
Step 5: Consolidated Report
Must Fix (Critical): None. ✓
Should Fix (Important): None. ✓
Nice to Have (Optional): None. ✓
Selector Mode Traceability
- Candidates: 0 judgment rules matched by Step 4b-i glob filter (changed files are markdown/YAML metadata — no source code files)
- Applicable: 0
- Skipped: N/A — no candidates triggered
- Mechanicals: 0 findings across 74 rule YAMLs
Step 6: Next Steps
No test coverage gaps to address — this is a documentation/plugin-command change with no executable code.
Step 7: Manual Review — Complete
Worktree clean. Review done.
{
"verdict": "approve",
"summary": "The PR correctly promotes /prepare-compact into the vault-cli plugin as a first-class slash command. The allowed-tools list is granular and capability-scoped (no bare Bash), the session-id trust boundary is correct, the 4-field resume-block schema is preserved, both verdict strings are present, and the CHANGELOG entry is properly formatted. The mechanical funnel found zero findings; the selector-mode adjudication found no applicable judgment rules. The only internal oddity is the spec file at specs/in-progress/ showing status 'verifying' rather than 'completed', but this is a dark-factory workflow state issue, not a PR quality issue.",
"comments": [],
"concerns_addressed": [
"security: allowed-tools uses granular scoping (Bash(git status:*), Bash(pgrep:*), etc.) instead of bare Bash — structurally enforces read-only invariant",
"correctness: AC for ⚪ DONE count was updated in spec from 0 to 1 to match the faithful port (prohibition sentence contains the string); command file is correct as-written",
"correctness: dangling step cross-references — verified absent; command file has no broken internal links",
"tests: this is a plugin command (markdown); no executable test suite applies"
]
}superseded by new automated review
There was a problem hiding this comment.
Now let me do the Step 4c-sel classification. Based on my analysis of the changed files and the rules index:
Changed files: CHANGELOG.md, commands/prepare-compact.md, prompts/completed/185-spec-034-prepare-compact-plugin-command.md, specs/in-progress/034-prepare-compact-plugin-command.md
Step 4b-i candidates (trigger globs matching changed files):
changelog/conventional-prefix-required(trigger:CHANGELOG.md) → applicableagent-cmd/command-frontmatter(trigger:commands/**/*.md) → applicableagent-cmd/command-thin(trigger:commands/**/*.md) → applicablechangelog/preamble-frozen(script rule, no trigger) → applicablechangelog/unreleased-entry-required(script rule, no trigger) → applicable
Step 4c-sel classification:
changelog/conventional-prefix-required→ applicable — CHANGELOG.md changed, feat: prefix present ✓changelog/preamble-frozen→ applicable — CHANGELOG.md changed,## Unreleasedat correct position ✓changelog/unreleased-entry-required→ applicable — CHANGELOG.md has new bullet ✓agent-cmd/command-frontmatter→ applicable — newcommands/prepare-compact.mdcreated ✓agent-cmd/command-thin→ applicable — new commands file, judgment warranted ✓
Step 4d-sel adjudication:
All mechanical findings: 0 (zero findings confirmed)
changelog/conventional-prefix-required— Bulletfeat: add /vault-cli:prepare-compact...starts withfeat:✓ PASSchangelog/preamble-frozen—## Unreleasedat line 11, between preamble (lines 1–9) and## v0.112.0(line 15) ✓ PASSchangelog/unreleased-entry-required— New bullet under## Unreleased✓ PASSagent-cmd/command-frontmatter— File hasdescription:and granularallowed-tools:with no bareBash✓ PASSagent-cmd/command-thin— 102-line command with 6 structured steps, bash snippet, and prohibition section — appropriate complexity for its purpose, no judgment violation ✓
Manual verification of plan concerns:
| Concern | File | Verification | Status |
|---|---|---|---|
| Granular allowed-tools, no bare Bash | commands/prepare-compact.md |
Lines 8–14: 6 scoped Bash + mkdir, no bare Bash |
✅ |
| Session-id from scratchpad path | commands/prepare-compact.md:88 |
"derived from the session's own scratchpad path — never from user input" | ✅ |
| Prompt injection surface | commands/prepare-compact.md |
Only Skill/Read/Write/Glob/six scoped Bash; writes only to checkpoint path | ✅ |
| All 16 acceptance strings | commands/prepare-compact.md |
All present (verified: sync-progress, Sweep every goal, Compact-safety checks, stopped-daemon warning, RESUME AFTER COMPACT, 4 field labels, compact-checkpoints/.md, Per session, Ready to compact, Not compact-safe yet, Never auto-commit, Unanswered gates, worth pausing for, state the full path, prohibition) | ✅ |
| ⚪ DONE exactly once | commands/prepare-compact.md:102 |
Line 102: "no ⚪ DONE block" — prohibition sentence only | ✅ |
| 👤 You: zero times | commands/prepare-compact.md |
Not present anywhere | ✅ |
| /vault-cli:session-close namespaced | commands/prepare-compact.md:102 |
"Unlike /vault-cli:session-close" — correctly namespaced |
✅ |
| No bare /session-close | commands/prepare-compact.md |
grep '[^:]/session-close' returns 0 |
✅ |
| CHANGELOG ## Unreleased placement | CHANGELOG.md:11 |
Between preamble and ## v0.112.0 | ✅ |
| Plugin versions unchanged | N/A | Diff does not touch .claude-plugin/ files | ✅ |
Selector-mode Traceability:
- Candidates: 5 rules matched by Step 4b-i glob filter
- Applicable: 5 rules (all classified applicable; no skipped)
- Skipped: none
Notes:
prompts/completed/andspecs/in-progress/files have no triggers inrules/index.jsonand no relevant rule patterns fire on themmake precommitwas not re-run in this review (selector mode) — CI covers lint+test; the pre-filled context confirmed 0 findings- No worktree cleanup needed (was already at PR HEAD)
{
"verdict": "approve",
"summary": "Faithful port of `/vault-cli:prepare-compact` as a first-class plugin command. Security surface is tight (6 scoped Bash subcommands + mkdir, no bare Bash, session-id from scratchpad path), all 16 acceptance-criterion strings verified present, ⚪ DONE appears exactly once in prohibition sentence, 👤 You: absent, /vault-cli:session-close correctly namespaced, CHANGELOG ## Unreleased correctly placed with feat: prefix, plugin versions unchanged. No mechanical or judgment violations found.",
"comments": [],
"concerns_addressed": [
"security: granular allowed-tools (6 scoped Bash + mkdir) verified in commands/prepare-compact.md lines 8-14 — no unscoped Bash entry exists",
"security: session-id trust boundary satisfied by commands/prepare-compact.md:88 — derived from session's scratchpad path, not user input",
"security: prompt injection surface limited by minimal tool surface (Skill/Read/Write/Glob/six scoped Bash); writes only to checkpoint path",
"correctness: all 16 load-bearing strings confirmed present in commands/prepare-compact.md",
"correctness: ⚪ DONE appears exactly once (line 102, prohibition sentence only) — no actual closer panel",
"correctness: 👤 You: absent (0 occurrences confirmed)",
"correctness: /vault-cli:session-close correctly namespaced; no bare /session-close",
"correctness: CHANGELOG ## Unreleased correctly placed between preamble and ## v0.112.0 with feat: prefix",
"correctness: plugin version strings (.claude-plugin/plugin.json) not in diff — unchanged at 0.112.0",
"tests: precommit not re-run (selector mode — CI covers lint+test); 0 mechanical findings from ast-grep runner"
]
}
Promotes
/prepare-compactfrom the per-machine~/.claude/commands/directory into the plugin as/vault-cli:prepare-compact, so it ships via normal plugin install/update instead of living on one machine.Why vault-cli
The command's only writes are vault progress updates — a
vault-cli:sync-progresscall plus a sweep of touched goal/task pages — plus the per-session checkpoint file. The git/docker/dark-factory checks are read-only reporting that annotate the verdict, not actions. That puts it in the vault lifecycle namespace besidesession-close,sync-progress, andupdate-task.Changes
commands/prepare-compact.md— new command, all six steps preservedCHANGELOG.md—feat:bullet under## Unreleasedspecs/in-progress/034-prepare-compact-plugin-command.md+ generated promptNotable
allowed-toolsis now granular, not bareBash. This makes the read-and-report-only invariant capability-enforced rather than prose-only — the command structurally cannot commit, push, or kill anything. Note this required adding theWritetool andBash(mkdir:*): the source's bareBashwas silently covering the checkpoint file write, which granular scoping would otherwise have broken at runtime.One acceptance criterion was fixed during audit. The original AC demanded
grep -c '⚪ DONE'return0, but the source contains that string once — inside the safety instruction "Do NOT emit a⚪ DONE/ session-close panel." A faithful port could not pass it, and the cheapest workaround would have been rewording the safety rule to dodge the grep. Now requires exactly1, with a separate negative check (👤 You:count0) that catches an actually-emitted panel.Verification. All 16 load-bearing strings confirmed present in the produced file, including the stopped-daemon warning and the
|| echo "no daemon"shell fallbacks. The container cannot read the original source file (it lives outside the repo), so the port is reconstructed from the spec's quoted strings — hence the direct string-level verification rather than trusting precommit alone.The restructure (numbered steps →
##sections) left four dangling "step N" cross-references pointing at steps the file no longer has; fixed in the follow-up commit.Out of scope
Fate of the local
~/.claude/commands/prepare-compact.mdduplicate — tracked separately; nothing outside this repo is touched.