mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
ac958c2326
## Summary [clawpatch](https://www.npmjs.com/package/clawpatch) is the automated Codex-driven code reviewer we used for a security/data-safety review pass over worktrunk. It keyed its state to a gitignored `.clawpatch/` directory — which means the work vanishes when the worktree is removed. This relocates that state to `.config/clawpatch/`, alongside the repo's other tool configs (`insta.yaml`, `nextest.toml`, `wt.toml`, …), and tracks only the durable, reusable inputs. The auto-mapper is near-blind to worktrunk's layout (it seeds features from `src/main.rs`, `src/bin/*`, `tests/*` only — not `src/commands/`, `src/git/`, `src/config/`), so every high-value finding came from six **hand-authored** feature definitions. Each is a threat model mapping a worktrunk security/data-safety invariant to the exact files implementing it — `approval_gate`, `merge_safety`, `remove`, `config_deprecation`, `repo_cache`, `switch_resolve`. That mapping is the actual review IP and is worth keeping in-repo: model- and tool-agnostic, equally usable by a future review pass. ## What's tracked vs not Tracked (8 files): the 6 `feat_custom_*.json` threat models, the tuned `config.json`, and a `.gitignore` allowlist block. Everything else stays gitignored — derived, machine-specific, or transient: - `findings/` — regenerated by `clawpatch review`, and tied to old branch state - `project.json` — regenerated by `clawpatch init`, contains an absolute `rootPath` - the auto-mapped `feat_cli-command` feature — deterministic from `clawpatch map` - `runs/` `reports/` `locks/` `patches/` — per-run transient The `.gitignore` uses an allowlist (`ignore .config/clawpatch/*`, then re-include `config.json` and `features/feat_custom_*.json`) so a future `clawpatch map`/`review` cannot accidentally start tracking regenerated state. ## Usage note Run with `--state-dir .config/clawpatch` or `CLAWPATCH_STATE_DIR=.config/clawpatch`. The in-file `stateDir` alone is not enough: clawpatch's config discovery only honors the relocated directory when the state dir is set via flag or env. `config.json` also adds `.config/clawpatch/**` to `exclude` so clawpatch never reviews its own state. ## Test plan No code change — config and gitignore only. Verified `clawpatch status --state-dir .config/clawpatch` reads the relocated state (7 features, 31 findings) and `git check-ignore` confirms the allowlist tracks exactly `config.json` + the 6 `feat_custom_*.json`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
106 lines
3.1 KiB
JSON
106 lines
3.1 KiB
JSON
{
|
|
"schemaVersion": 1,
|
|
"featureId": "feat_custom_remove",
|
|
"title": "wt remove and picker-driven removal: data-loss surface",
|
|
"summary": "Worktree removal is the highest-stakes destructive operation. Force-remove must require explicit user consent; cleanup must not silently discard uncommitted changes or untracked files. Race conditions between safety checks and the actual delete must fail closed (retain data). The picker also routes alt-r through handle_remove_output - must consult approvals read-only and skip rather than prompt mid-render. See CLAUDE.md > Data Safety, Time-of-check vs time-of-use.",
|
|
"kind": "library",
|
|
"source": "manual",
|
|
"confidence": "high",
|
|
"entrypoints": [
|
|
{
|
|
"path": "src/commands/picker/mod.rs",
|
|
"symbol": "do_removal",
|
|
"route": null,
|
|
"command": "wt remove (interactive)"
|
|
}
|
|
],
|
|
"ownedFiles": [
|
|
{
|
|
"path": "src/commands/picker/mod.rs",
|
|
"reason": "interactive removal entry"
|
|
},
|
|
{
|
|
"path": "src/git/repository/worktrees.rs",
|
|
"reason": "worktree list/remove primitives"
|
|
},
|
|
{
|
|
"path": "src/remove_dir.rs",
|
|
"reason": "filesystem removal logic"
|
|
},
|
|
{
|
|
"path": "src/commands/relocate.rs",
|
|
"reason": "relocate/move worktree (also destructive)"
|
|
}
|
|
],
|
|
"contextFiles": [
|
|
{
|
|
"path": "src/commands/command_approval.rs",
|
|
"reason": "approval system for pre-remove hooks"
|
|
},
|
|
{
|
|
"path": "src/commands/hooks.rs",
|
|
"reason": "pre/post-remove hook execution"
|
|
},
|
|
{
|
|
"path": "src/git/repository/working_tree.rs",
|
|
"reason": "dirty detection"
|
|
},
|
|
{
|
|
"path": "docs/content/faq.md",
|
|
"reason": "documented destructive surface"
|
|
},
|
|
{
|
|
"path": "CLAUDE.md",
|
|
"reason": "data safety invariants"
|
|
}
|
|
],
|
|
"tests": [
|
|
{
|
|
"path": "tests/integration_tests/remove.rs",
|
|
"command": "cargo test --test integration remove"
|
|
}
|
|
],
|
|
"tags": [
|
|
"rust",
|
|
"data-safety",
|
|
"destructive"
|
|
],
|
|
"trustBoundaries": [
|
|
"filesystem",
|
|
"user-input"
|
|
],
|
|
"status": "needs-fix",
|
|
"lock": null,
|
|
"findingIds": [
|
|
"fnd_sig-feat-custom-remove-3fc85fbc9_2fb36531bc",
|
|
"fnd_sig-feat-custom-remove-d1e281e8e_f4081924e6",
|
|
"fnd_sig-feat-custom-remove-54cc3614f_9edd442ab5",
|
|
"fnd_sig-feat-custom-remove-1dbe5d2d1_10faa93f43",
|
|
"fnd_sig-feat-custom-remove-7e79725d5_26a2834e00",
|
|
"fnd_sig-feat-custom-remove-3f4296191_028f92e016",
|
|
"fnd_sig-feat-custom-remove-34d4e5259_2210c7a66b",
|
|
"fnd_sig-feat-custom-remove-087a0b4bb_5c82e2c2a5"
|
|
],
|
|
"patchAttemptIds": [],
|
|
"analysisHistory": [
|
|
{
|
|
"runId": "20260516T210804-353f93",
|
|
"kind": "review",
|
|
"summary": "4 finding(s)",
|
|
"provider": "codex",
|
|
"model": null,
|
|
"createdAt": "2026-05-16T21:13:20.172Z"
|
|
},
|
|
{
|
|
"runId": "20260517T050139-b247d5",
|
|
"kind": "review",
|
|
"summary": "4 finding(s)",
|
|
"provider": "codex",
|
|
"model": null,
|
|
"createdAt": "2026-05-17T05:10:23.387Z"
|
|
}
|
|
],
|
|
"createdAt": "2026-05-16T21:00:00.000Z",
|
|
"updatedAt": "2026-05-17T05:10:23.387Z"
|
|
}
|