vinta-ai-workflows 0.2.0-alpha3 → 0.2.0-alpha5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/README.md +5 -2
  3. package/package.json +1 -1
  4. package/schemas/vinta-ai-workflows-config.v1.schema.json +57 -0
  5. package/skills/vinta-analyze-codebase/SKILL.md +1 -1
  6. package/skills/vinta-bootstrap-ai-tools/SKILL.md +37 -8
  7. package/skills/vinta-derive-skills/SKILL.md +92 -39
  8. package/skills/vinta-derive-skills/resources/foundation-skills/deslop-comments/SKILL.md +148 -0
  9. package/skills/vinta-derive-skills/resources/foundation-skills/handoff/SKILL.md +100 -0
  10. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +39 -4
  11. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/claude-worktree-write-guard.py +118 -0
  12. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/gen-claude-sandbox-settings.sh +167 -0
  13. package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/sandbox-run.sh +3 -1
  14. package/skills/vinta-derive-skills/resources/foundation-skills/thermo-nuclear-code-quality-review/SKILL.md +129 -0
  15. package/skills/vinta-derive-skills/resources/handoff-to-client-template.md +107 -0
  16. package/skills/vinta-derive-skills/resources/plan-execution/README.md +42 -0
  17. package/skills/vinta-derive-skills/resources/{implement-plan-template-modular-substitutions.md → plan-execution/partials/commit-strategy/modular.md} +38 -70
  18. package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +46 -0
  19. package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +78 -0
  20. package/skills/vinta-derive-skills/resources/plan-execution/partials/model-pick.md +19 -0
  21. package/skills/vinta-derive-skills/resources/plan-execution/partials/pr-context.md +61 -0
  22. package/skills/vinta-derive-skills/resources/plan-execution/partials/review-layers.md +50 -0
  23. package/skills/vinta-derive-skills/resources/plan-execution/partials/worktree-seam.md +68 -0
  24. package/skills/vinta-derive-skills/resources/{amend-plan-template.md → plan-execution/shell/amend-plan-template.md} +48 -42
  25. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +47 -0
  26. package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +186 -0
  27. package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +30 -0
  28. package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +25 -0
  29. package/skills/vinta-derive-skills/resources/systematic-debugging-template.md +7 -3
  30. package/skills/vinta-update-project-skills/SKILL.md +1 -1
  31. package/skills/vinta-derive-skills/resources/implement-plan-template.md +0 -436
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env python3
2
+ """claude-worktree-write-guard.py — Claude Code PreToolUse hook that blocks
3
+ file-tool writes escaping a worktree into the protected main checkout.
4
+
5
+ Why this exists: Claude Code runs sub-agents (the Task tool) IN-PROCESS — they
6
+ share the orchestrator's OS process and tool pipeline, so there is no child
7
+ launch command to wrap with an OS sandbox (that model only works on harnesses
8
+ that spawn sub-agents as subprocesses, e.g. `codex exec`). A session-level
9
+ PreToolUse hook, by contrast, fires for every tool call in the session —
10
+ orchestrator AND Task sub-agents alike — because they run through the same
11
+ pipeline. This hook is the in-process equivalent of prepare-worktree's
12
+ `sandbox-run.sh` deny-main/allow-rest guard, scoped to the file-editing tools.
13
+
14
+ Scope: the file-write tools only — Edit, Write, MultiEdit, NotebookEdit. Bash
15
+ writes are NOT covered here (a hook can't reliably know every path a shell
16
+ command touches); use Claude Code's native `sandbox` (see
17
+ gen-claude-sandbox-settings.sh `--os-sandbox`) for OS-level Bash confinement,
18
+ and rely on implement-plan's post-run `git -C <main> status` backstop otherwise.
19
+
20
+ Model — deny-main, allow-rest (mirrors sandbox-run.sh): every path is allowed
21
+ EXCEPT ones that resolve under a --deny root, and an --allow root punches a
22
+ subtree back to writable even when nested under a --deny root. The worktree
23
+ usually lives UNDER the main checkout (.claude/worktrees/<name>/), and git
24
+ worktrees write commits into <main>/.git, so both the worktree and <main>/.git
25
+ are passed as --allow. Symlinks are resolved (realpath) before the check, so a
26
+ write through a symlink that lands back in the main checkout (e.g. a shared
27
+ node_modules) is correctly caught.
28
+
29
+ Usage (wired by gen-claude-sandbox-settings.sh into settings.json hooks):
30
+ claude-worktree-write-guard.py \
31
+ --deny <main-checkout-root> \
32
+ --allow <worktree-path> \
33
+ --allow <main-checkout-root>/.git \
34
+ --allow <main-checkout-root>/.vinta-ai-workflows
35
+
36
+ Reads the PreToolUse event JSON on stdin. Blocks by exiting 2 with a reason on
37
+ stderr — Claude Code feeds that stderr back to the model as the denial reason,
38
+ so the agent retries against the correct worktree path. Any non-write tool, or
39
+ a write that stays inside an --allow subtree, exits 0 (allowed). The guard fails
40
+ OPEN (exit 0) on malformed input or its own errors — it is a safety net layered
41
+ under the OS sandbox and the post-run backstop, never the sole gate, and must
42
+ never wedge a run by denying on a parsing hiccup.
43
+ """
44
+ import json
45
+ import os
46
+ import sys
47
+
48
+ # Tool-input keys that carry a write target, per tool.
49
+ PATH_KEYS = ("file_path", "notebook_path")
50
+ GUARDED_TOOLS = {"Edit", "Write", "MultiEdit", "NotebookEdit"}
51
+
52
+
53
+ def canon(path):
54
+ """Absolute, symlink-resolved path. realpath resolves a shared node_modules
55
+ / .vinta-ai-workflows symlink back to its real location so the prefix check
56
+ reflects where the byte actually lands, not the symlink's apparent path."""
57
+ return os.path.realpath(os.path.abspath(path))
58
+
59
+
60
+ def under(path, root):
61
+ """True if `path` is `root` or nested beneath it. Compares canonical paths
62
+ with a trailing separator so /main-evil is not treated as under /main."""
63
+ path, root = canon(path), canon(root)
64
+ return path == root or path.startswith(root.rstrip(os.sep) + os.sep)
65
+
66
+
67
+ def parse_args(argv):
68
+ deny, allow = [], []
69
+ i = 0
70
+ while i < len(argv):
71
+ if argv[i] == "--deny" and i + 1 < len(argv):
72
+ deny.append(argv[i + 1]); i += 2
73
+ elif argv[i] == "--allow" and i + 1 < len(argv):
74
+ allow.append(argv[i + 1]); i += 2
75
+ else:
76
+ i += 1
77
+ return deny, allow
78
+
79
+
80
+ def main():
81
+ deny, allow = parse_args(sys.argv[1:])
82
+ if not deny:
83
+ # Nothing to protect — nothing to do.
84
+ return 0
85
+
86
+ try:
87
+ event = json.load(sys.stdin)
88
+ except Exception:
89
+ return 0 # fail open — never wedge a run on a parse error
90
+
91
+ if event.get("tool_name") not in GUARDED_TOOLS:
92
+ return 0
93
+
94
+ tool_input = event.get("tool_input") or {}
95
+ target = next((tool_input[k] for k in PATH_KEYS if tool_input.get(k)), None)
96
+ if not target:
97
+ return 0 # no write target we recognize
98
+
99
+ # allow wins over deny (the worktree + .git + .vinta-ai-workflows are nested
100
+ # under the denied main root and must stay writable).
101
+ if any(under(target, a) for a in allow):
102
+ return 0
103
+ if any(under(target, d) for d in deny):
104
+ resolved = canon(target)
105
+ wt = allow[0] if allow else "the worktree"
106
+ sys.stderr.write(
107
+ f"Blocked write to the main checkout: {resolved}\n"
108
+ f"This file is outside the worktree. Write to the worktree instead "
109
+ f"(under {wt}). If you meant to edit a shared file, it is protected "
110
+ f"for the duration of this plan run.\n"
111
+ )
112
+ return 2 # exit 2 → Claude Code blocks the call, stderr is the reason
113
+
114
+ return 0
115
+
116
+
117
+ if __name__ == "__main__":
118
+ sys.exit(main())
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env bash
2
+ # gen-claude-sandbox-settings.sh — generate a Claude Code settings.json that
3
+ # confines a worktree session (orchestrator + its in-process Task sub-agents)
4
+ # to writing inside the worktree, blocking stray writes to the main checkout.
5
+ #
6
+ # Claude Code runs sub-agents in-process (the Task tool shares the orchestrator's
7
+ # OS process), so the subprocess-wrapping model of sandbox-run.sh does not apply
8
+ # — the guard has to live in the harness's own settings. This script wires two
9
+ # complementary layers, matching prepare-worktree's deny-main/allow-rest model:
10
+ #
11
+ # Layer A (always) — a PreToolUse hook (claude-worktree-write-guard.py) that
12
+ # blocks the file-editing tools (Edit/Write/MultiEdit/NotebookEdit) from
13
+ # writing outside the worktree. Filesystem-only; no network impact. Fires
14
+ # for the orchestrator AND every Task sub-agent (shared session pipeline).
15
+ #
16
+ # Layer B (opt-in, --os-sandbox) — Claude Code's NATIVE OS sandbox
17
+ # (`sandbox.filesystem.denyWrite`/`allowWrite`, Seatbelt on macOS / bwrap on
18
+ # Linux) so Bash-issued writes are blocked at the kernel too. NOTE: enabling
19
+ # the native sandbox also turns on NETWORK isolation — dep installs and
20
+ # `git push` break unless you allow-list registries/remotes. Pass the hosts
21
+ # the plan needs via --allow-domain (repeatable). Without --os-sandbox, Bash
22
+ # writes fall back to implement-plan's post-run `git -C <main> status` check.
23
+ #
24
+ # Both layers keep <worktree>, <main>/.git (git worktrees commit into the main
25
+ # repo's .git), and the shared .vinta-ai-workflows dir writable.
26
+ #
27
+ # Usage:
28
+ # gen-claude-sandbox-settings.sh \
29
+ # --worktree <worktree-path> \
30
+ # --main <main-checkout-root> \
31
+ # [--allow <extra-writable-path>]... # e.g. .vinta-ai-workflows if elsewhere
32
+ # [--out <settings-file>] # default: <worktree>/.claude/settings.json
33
+ # [--os-sandbox] # also emit the native sandbox block
34
+ # [--allow-domain <host>]... # network hosts to allow under --os-sandbox
35
+ #
36
+ # The generated file is meant to govern a Claude Code session ROOTED IN THE
37
+ # WORKTREE (`cd <worktree> && claude`), the recommended claude-code topology for
38
+ # an isolated plan run — the settings are read at session start. See Step 5.5 of
39
+ # ../SKILL.md for the two deployment models and their timing caveats.
40
+ #
41
+ # Exit: 0 on success; 2 on argument/setup errors.
42
+
43
+ set -u
44
+
45
+ die() { echo "gen-claude-sandbox-settings: $*" >&2; exit 2; }
46
+
47
+ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
48
+ GUARD="$SCRIPT_DIR/claude-worktree-write-guard.py"
49
+
50
+ WORKTREE=""; MAIN=""; OUT=""; OS_SANDBOX=0
51
+ EXTRA_ALLOW=(); DOMAINS=()
52
+ while [ $# -gt 0 ]; do
53
+ case "$1" in
54
+ --worktree) shift; [ $# -gt 0 ] || die "--worktree needs a path"; WORKTREE="$1"; shift ;;
55
+ --main) shift; [ $# -gt 0 ] || die "--main needs a path"; MAIN="$1"; shift ;;
56
+ --out) shift; [ $# -gt 0 ] || die "--out needs a path"; OUT="$1"; shift ;;
57
+ --allow) shift; [ $# -gt 0 ] || die "--allow needs a path"; EXTRA_ALLOW+=("$1"); shift ;;
58
+ --allow-domain) shift; [ $# -gt 0 ] || die "--allow-domain needs a host"; DOMAINS+=("$1"); shift ;;
59
+ --os-sandbox) OS_SANDBOX=1; shift ;;
60
+ *) die "unknown arg: $1" ;;
61
+ esac
62
+ done
63
+
64
+ [ -n "$WORKTREE" ] || die "--worktree <path> required"
65
+ [ -n "$MAIN" ] || die "--main <path> required"
66
+ [ -f "$GUARD" ] || die "guard hook not found next to this script: $GUARD"
67
+
68
+ # Canonicalize (leaf need not exist).
69
+ canon() {
70
+ local p="$1" d b
71
+ if [ -d "$p" ]; then (cd "$p" && pwd -P); return; fi
72
+ d=$(dirname "$p"); b=$(basename "$p")
73
+ if [ -d "$d" ]; then echo "$(cd "$d" && pwd -P)/$b"; else echo "$p"; fi
74
+ }
75
+
76
+ WORKTREE=$(canon "$WORKTREE")
77
+ MAIN=$(canon "$MAIN")
78
+ [ -d "$WORKTREE" ] || die "worktree path is not a directory: $WORKTREE"
79
+ [ -d "$MAIN" ] || die "main checkout path is not a directory: $MAIN"
80
+ [ -z "$OUT" ] && OUT="$WORKTREE/.claude/settings.json"
81
+
82
+ # Writable exceptions punched back through the main-checkout deny: the worktree,
83
+ # the shared git database (worktree commits land in <main>/.git), the shared
84
+ # .vinta-ai-workflows dir, plus any caller extras.
85
+ ALLOW=("$WORKTREE" "$MAIN/.git" "$MAIN/.vinta-ai-workflows")
86
+ for p in "${EXTRA_ALLOW[@]:-}"; do [ -n "$p" ] && ALLOW+=("$(canon "$p")"); done
87
+
88
+ if [ "$OS_SANDBOX" -eq 1 ] && [ "${#DOMAINS[@]}" -eq 0 ]; then
89
+ echo "gen-claude-sandbox-settings: WARNING --os-sandbox with no --allow-domain — network" >&2
90
+ echo " isolation is ON, so dep installs / git push over the network will fail. Pass the" >&2
91
+ echo " registry + git remote hosts via --allow-domain, or drop --os-sandbox for hook-only." >&2
92
+ fi
93
+
94
+ mkdir -p "$(dirname "$OUT")" || die "cannot create settings dir for $OUT"
95
+
96
+ # Emit JSON via python3 (safe escaping of paths); merge into an existing file if
97
+ # one is present so we don't clobber unrelated settings the worktree may carry.
98
+ GUARD="$GUARD" OUT="$OUT" MAIN="$MAIN" WORKTREE="$WORKTREE" OS_SANDBOX="$OS_SANDBOX" \
99
+ python3 - "${ALLOW[@]}" -- "${DOMAINS[@]:-}" <<'PY' || die "failed to write settings"
100
+ import json, os, sys
101
+
102
+ guard, out, main, worktree = (os.environ[k] for k in ("GUARD", "OUT", "MAIN", "WORKTREE"))
103
+ os_sandbox = os.environ["OS_SANDBOX"] == "1"
104
+
105
+ sep = sys.argv.index("--")
106
+ allow = [a for a in sys.argv[1:sep] if a]
107
+ domains = [d for d in sys.argv[sep + 1:] if d]
108
+
109
+ # Layer A — PreToolUse write-guard hook (always).
110
+ guard_cmd = " ".join(
111
+ [json.dumps(guard) if " " in guard else guard, "--deny", main]
112
+ + sum((["--allow", a] for a in allow), [])
113
+ )
114
+ hook_entry = {
115
+ "matcher": "Edit|Write|MultiEdit|NotebookEdit",
116
+ "hooks": [{"type": "command", "command": guard_cmd}],
117
+ }
118
+
119
+ # Merge into an existing settings file if present (preserve unrelated keys).
120
+ settings = {}
121
+ if os.path.exists(out):
122
+ try:
123
+ with open(out) as f:
124
+ settings = json.load(f) or {}
125
+ except Exception:
126
+ settings = {}
127
+
128
+ hooks = settings.setdefault("hooks", {})
129
+ pre = hooks.setdefault("PreToolUse", [])
130
+ # Drop any prior guard entry (idempotent re-runs) then append the fresh one.
131
+ pre = [e for e in pre if "claude-worktree-write-guard" not in json.dumps(e)]
132
+ pre.append(hook_entry)
133
+ hooks["PreToolUse"] = pre
134
+
135
+ # Layer B — native OS sandbox (opt-in).
136
+ if os_sandbox:
137
+ settings["sandbox"] = {
138
+ "enabled": True,
139
+ # Degrade to unsandboxed on platforms without Seatbelt/bwrap rather than
140
+ # hard-failing the run; the hook (Layer A) still guards file writes.
141
+ "failIfUnavailable": False,
142
+ "filesystem": {
143
+ "denyWrite": [main],
144
+ "allowWrite": allow,
145
+ },
146
+ "network": {
147
+ # Sandbox forces network isolation; without an allow-list, installs
148
+ # and pushes break. Callers pass registry + remote hosts.
149
+ "allowedDomains": domains,
150
+ },
151
+ }
152
+
153
+ with open(out, "w") as f:
154
+ json.dump(settings, f, indent=2)
155
+ f.write("\n")
156
+
157
+ print(f"wrote {out}")
158
+ print(f" layer A (hook): guard on Edit|Write|MultiEdit|NotebookEdit, deny {main}")
159
+ print(f" allow-write: {', '.join(allow)}")
160
+ if os_sandbox:
161
+ dom = ", ".join(domains) if domains else "(none — network will be blocked)"
162
+ print(f" layer B (os sandbox): enabled, network allowedDomains: {dom}")
163
+ else:
164
+ print(" layer B (os sandbox): OFF (hook-only; Bash writes rely on the post-run backstop)")
165
+ PY
166
+
167
+ echo "gen-claude-sandbox-settings: done."
@@ -75,7 +75,9 @@ fi
75
75
 
76
76
  # ---- macOS: sandbox-exec -----------------------------------------------------
77
77
  if command -v sandbox-exec >/dev/null 2>&1; then
78
- profile=$(mktemp -t vinta-sandbox) || die "mktemp failed"
78
+ # Explicit XXXXXX template (portable across BSD mktemp on stock macOS and GNU
79
+ # mktemp from coreutils — `mktemp -t prefix` is not portable between them).
80
+ profile=$(mktemp "${TMPDIR:-/tmp}/vinta-sandbox.XXXXXX") || die "mktemp failed"
79
81
  trap 'rm -f "$profile"' EXIT
80
82
  {
81
83
  echo '(version 1)'
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: thermo-nuclear-code-quality-review
3
+ description: Run an extremely strict maintainability review of a diff — abstraction quality, giant files, and spaghetti-condition growth. This is a deep, on-demand structural audit, harsher than the normal per-phase review: it hunts for "code judo" reframes that make whole branches, helpers, modes, or layers disappear rather than polishing what's there. Use when the user asks for a "thermo-nuclear code quality review", "thermonuclear review", "deep code quality audit", or "harsh maintainability review", or when the standard review flow escalates a structural concern. Opt-in only — do not auto-run it on every diff; it is meant to be invoked deliberately. Read-only: it reports findings and hands each fix to a fixer, it does not edit code itself.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Thermo-nuclear code quality review
8
+
9
+ An intentionally severe maintainability audit. The normal review checks that a change is correct, tested, and in-convention. This review asks a harder question: **is the change structured as well as it could be, or did it grow complexity that a better framing would have avoided?**
10
+
11
+ The mindset is **ambitious structural simplification**, not incremental cleanup. Look for reframes that make whole branches, helpers, modes, conditionals, or layers disappear entirely — a "code judo" move that preserves behavior while collapsing the structure. Do not approve a change merely because it works.
12
+
13
+ This is a heavy, deliberate pass. It is **not** part of the automatic per-phase review — run it when asked, or when the standard `review-phase` review escalates a structural concern it can't fully resolve. (See "Relationship to the standard review flow" below.)
14
+
15
+ ## When to use / when to skip
16
+
17
+ Use it when:
18
+ - The user explicitly asks for a thermo-nuclear / deep / harsh maintainability review.
19
+ - A phase touched core architecture, added a large file, or introduced branching that "smells" — and the standard reviewer flagged it for a deeper look.
20
+ - Before merging something large or long-lived where structure matters more than speed.
21
+
22
+ Skip it when:
23
+ - The diff is a small, self-contained fix, a docs/comment change, or generated output. Running a thermo-nuclear pass on a two-line change wastes effort and produces noise.
24
+ - Correctness is the open question, not structure — use the standard review or the `systematic-debugging` skill for that.
25
+
26
+ ## Scope
27
+
28
+ Default scope is the current change set: find it with `git diff --stat` / `git status --short` against the base the work started from (or the phase diff, when a review conductor passes one). If the user names a PR, a directory, or specific files, use that scope instead.
29
+
30
+ Read the **full diff** of every file in scope, plus enough of each touched file's surroundings to judge structure — a diff line looks innocent until you see the 1,200-line file it lands in.
31
+
32
+ ## The standards (all non-negotiable)
33
+
34
+ 0. **Ambitious structural simplification.** Look for opportunities to reframe the change so that whole branches, helpers, modes, conditionals, or layers disappear entirely. This is the primary lens; the rest are specific ways it shows up.
35
+ 1. **File-size boundary.** Do not let a change push a file from under ~1,000 lines to over ~1,000 lines without a very strong reason, especially when the added code is extractable.
36
+ 2. **Spaghetti growth.** Be highly suspicious of new ad-hoc conditionals, scattered special cases, or one-off branches inserted into unrelated flows.
37
+ 3. **Design-first bias.** If behavior can stay the same while the structure becomes meaningfully cleaner, push for the cleaner version.
38
+ 4. **Direct code preference.** Treat brittle, ad-hoc, or "magic" behavior as a code-quality problem, not a clever solution.
39
+ 5. **Type and boundary cleanliness.** Question unnecessary optionality, `unknown`, `any`, or cast-heavy code when a clearer type boundary could exist. (Apply the equivalent in the project's language — loose typing, stringly-typed data, escape hatches.)
40
+ 6. **Canonical layer logic.** Call out feature logic leaking into shared paths, or implementation details leaking through an API boundary. Reuse existing canonical utilities instead of bespoke duplicates.
41
+ 7. **Sequential orchestration.** If independent work is serialized for no good reason, ask whether the flow should run in parallel, and whether partial-update logic leaves state less atomic than it should be.
42
+
43
+ ## Primary review questions
44
+
45
+ Apply these systematically to the diff:
46
+
47
+ 1. Is there a "code judo" move that would make this dramatically simpler?
48
+ 2. Can this change be reframed so fewer concepts, branches, or helper layers are needed?
49
+ 3. Does this improve or worsen the local architecture?
50
+ 4. Did the diff add branching complexity where a better abstraction should exist?
51
+ 5. Did a previously cohesive module become more coupled, more stateful, or harder to scan?
52
+ 6. Is this logic living in the right file and layer?
53
+ 7. Did this change enlarge a file or component past a healthy size boundary?
54
+ 8. Are there repeated conditionals that signal a missing model or missing helper?
55
+ 9. Is the implementation direct and legible, or does it rely on special cases and incidental control flow?
56
+ 10. Is this abstraction actually earning its keep, or is it just a wrapper?
57
+ 11. Did the diff introduce casts, optionality, or ad-hoc object shapes that obscure the real invariant?
58
+ 12. Is this logic in the canonical layer, or did the diff leak details across a boundary?
59
+ 13. Is this orchestration more sequential or less atomic than it needs to be?
60
+
61
+ ## Flag aggressively
62
+
63
+ - A complicated implementation where a cleaner reframe could eliminate whole categories of complexity.
64
+ - Refactors that move code without reducing conceptual overhead for readers.
65
+ - Files crossing ~1,000 lines because of this change, especially when the added code is extractable.
66
+ - New conditionals bolted onto unrelated code paths.
67
+ - One-off booleans, nullable modes, or flags complicating existing control flow.
68
+ - Feature-specific logic leaking into general-purpose modules.
69
+ - Generic "magic" handling that obscures a simple structure.
70
+ - Thin wrappers or identity abstractions adding indirection without clarity.
71
+ - Unnecessary casts, `any` / `unknown`, or optional params muddying contracts.
72
+ - Copy-pasted logic instead of an extracted helper.
73
+ - Narrow edge-case handling wedged into the middle of already-busy functions.
74
+ - Refactors that technically pass tests but reduce modularity or readability.
75
+ - "Temporary" branching that is likely to become permanent debt.
76
+ - Bespoke helpers duplicating existing canonical utilities.
77
+ - Logic placed in the wrong layer/package when more central ownership exists.
78
+ - Sequential async flow where independent work could run in parallel.
79
+ - Partial-update logic that leaves state less atomic than necessary.
80
+
81
+ ## Approval bar
82
+
83
+ Treat these as blockers unless there is a strong, stated justification:
84
+
85
+ - Plausible code-judo moves exist but were left unexplored.
86
+ - A file crosses ~1,000 lines because of this change without a compelling reason.
87
+ - Ad-hoc branching tangles an existing flow.
88
+ - Feature checks scatter across shared code to solve a local problem.
89
+ - Unnecessary abstractions, wrappers, or cast-heavy contracts increase indirection.
90
+ - Existing helpers are duplicated, or logic lands in the wrong canonical layer.
91
+
92
+ Do not approve merely because behavior seems correct.
93
+
94
+ ## Output format
95
+
96
+ Report findings prioritized in this order:
97
+
98
+ 1. Structural code-quality regressions.
99
+ 2. Missed opportunities for dramatic simplification / code-judo restructuring.
100
+ 3. Spaghetti / branching complexity increases.
101
+ 4. File-size and layering violations.
102
+ 5. Type / boundary and duplication issues.
103
+
104
+ For each finding: name the file and lines, state the concrete regression, and — this is the point of the review — describe the reframe that removes it, not just "this is complex." Do not flood the review with low-value nits when larger structural issues exist; a page of naming quibbles buries the one restructuring that matters.
105
+
106
+ Triage each finding like the standard review does:
107
+ - **BLOCKER** — must fix before merge (matches the approval-bar list above).
108
+ - **SHOULD-FIX** — fix now if cheap, otherwise a tracked follow-up.
109
+ - **NIT** — mention only if trivially cheap.
110
+
111
+ ## Relationship to the standard review flow
112
+
113
+ This skill is read-only, like the project's normal review. It does not edit code. When a finding warrants a change, hand it to the project's `fixer` agent ([ai-tools/agents/fixer.md](ai-tools/agents/fixer.md)) with the finding quoted verbatim, exactly as the standard fix loop does.
114
+
115
+ The per-phase review (the `reviewer` agent, [ai-tools/agents/reviewer.md](ai-tools/agents/reviewer.md)) applies a condensed version of standard 0 on every phase — "is there an obvious code-judo reframe?" When that lens surfaces something structural that deserves a full audit, the reviewer escalates by invoking this skill against the phase diff. Reserve the full pass for when it earns its cost; the condensed lens is enough for routine phases.
116
+
117
+ ## Pitfalls
118
+
119
+ - **Nit flooding.** The failure mode of a harsh review is a wall of trivial comments that hides the one finding that matters. Lead with structure; drop the nits.
120
+ - **"Cleaner" that is not.** A reframe that trades one form of complexity for another, or that hurts readability to save lines, is not a win. Only push a change when it genuinely reduces conceptual overhead.
121
+ - **Rewriting for taste.** Behavior must be preserved. This is a review, not a redesign mandate — propose reframes, quote them as findings, and let the fixer and conductor decide.
122
+ - **Running it everywhere.** It is opt-in for a reason. Auto-running it on every small diff produces noise and slows the flow.
123
+
124
+ ## Verification
125
+
126
+ - Every finding names a concrete file/line and a specific reframe, not a vague "too complex."
127
+ - Findings are ordered by the priority list above, BLOCKER first.
128
+ - The pass edited no code directly — any change went through a fixer.
129
+ - If nothing structural was found on a large, multi-file diff, you looked again before saying so.
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: handoff-to-client
3
+ description: Generate a markdown API-change handoff document for the client teams consuming {{PROJECT_NAME}}'s {{API_STYLE}} API ({{CLIENT_PLATFORMS_LIST}}). Documents every endpoint/operation added, changed, deprecated, or removed on the current branch relative to `{{DEFAULT_BRANCH}}` — request/response shapes, auth, errors, breaking-change flags, and per-platform migration notes — so client implementers can adopt the changes without reading this repo's source. Use when the user says "write the client handoff", "document the API changes for the clients", "hand off to the frontend/mobile team", or before merging an API-changing branch.
4
+ ---
5
+
6
+ # Handoff to client
7
+
8
+ {{PROJECT_NAME}} ({{STACK_SUMMARY}}) is an API-only repo — its consumers live in separate codebases: {{CLIENT_PLATFORMS_LIST}}. Client implementers cannot read this repo's diff; this skill turns an API-changing branch into a self-contained markdown document they implement against.
9
+
10
+ The document is a contract description, not a changelog: every change carries enough shape detail (fields, types, nullability, examples) that a client engineer can code against it without opening this repo.
11
+
12
+ ## Step 0 — Resolve the scope
13
+
14
+ Default scope is the **current branch vs `{{DEFAULT_BRANCH}}`**:
15
+
16
+ ```
17
+ git fetch origin {{DEFAULT_BRANCH}}
18
+ git diff origin/{{DEFAULT_BRANCH}}...HEAD --stat
19
+ ```
20
+
21
+ Overrides, when the user asks: an explicit commit range (`<from>..<to>`), a merged PR, or a whole feature plan (enumerate the plan's phase branches/commits from its tracking file and union their diffs). State the resolved scope in the output doc's header.
22
+
23
+ If the resolved diff touches no API surface at all, say so and stop — do not fabricate a document.
24
+
25
+ ## Step 1 — Enumerate the API surface changes
26
+
27
+ Work from the diff, never from memory:
28
+
29
+ 1. List changed files in the scope and keep the API-relevant ones: route/URL declarations, controllers/views/resolvers, serializers/schemas/DTOs, auth/permission code, error handlers, API versioning config.
30
+ 2. For each, read the changed hunks and map them to concrete operations (method + path for REST; query/mutation/subscription for GraphQL; service/rpc for gRPC).
31
+ 3. Classify every operation touched: **added**, **changed**, **deprecated**, or **removed**.
32
+ 4. For each **changed** operation, diff the request and response shapes field by field: added fields, removed fields, type changes, nullability changes, renamed fields, default changes, new/changed validation rules, pagination or filtering changes.
33
+ 5. Flag **breaking** vs **non-breaking**. Breaking = anything an existing client call could fail or misbehave on: removed/renamed field or endpoint, narrowed type, newly-required request field, changed status code or error shape, auth/permission tightening. Additive-and-optional is non-breaking.
34
+
35
+ {{API_SPEC_BLOCK}}
36
+
37
+ Also sweep for non-endpoint contract changes clients still feel: auth flows (token lifetimes, scopes, header names), rate limits, webhooks/event payloads, websocket messages, file-upload conventions, and deprecation timelines.
38
+
39
+ ## Step 2 — Write the document
40
+
41
+ Path: `{{CLIENT_HANDOFF_DIR}}/{YYYY-MM-DD}-{branch-or-feature-slug}.md`. Create the directory if missing.
42
+
43
+ ```markdown
44
+ # API changes: {feature / branch title}
45
+
46
+ - **Date:** {YYYY-MM-DD}
47
+ - **Scope:** {branch} vs {{DEFAULT_BRANCH}} ({short-sha range})
48
+ - **Audience:** {{CLIENT_PLATFORMS_LIST}}
49
+ - **Breaking changes:** {count, or "none"}
50
+
51
+ ## Summary
52
+ 2–5 sentences: what shipped and why clients care. Lead with breaking changes.
53
+
54
+ ## Breaking changes ← omit section if none; never bury these
55
+ Per change: what breaks, which operations, what the client must do, and the
56
+ deadline/version after which the old behavior disappears.
57
+
58
+ ## {Operation-by-operation sections, grouped added / changed / deprecated / removed}
59
+
60
+ ### {METHOD /path} or {operationName}
61
+ - **Status:** added | changed | deprecated | removed — {breaking? yes/no}
62
+ - **Auth:** {scheme, required scopes/permissions}
63
+ - **Request:** field table or annotated example — name, type, required?,
64
+ constraints, default. For *changed*: call out exactly what differs from before.
65
+ - **Response:** per status code — annotated example JSON with field meanings,
66
+ nullability, enum values spelled out.
67
+ - **Errors:** status codes + error body shape + when each fires.
68
+ - **Example:** one complete request/response pair with realistic (fake) data.
69
+ - **Client migration notes:** what each platform ({{CLIENT_PLATFORMS_LIST}})
70
+ concretely changes — omit platforms unaffected by this operation.
71
+
72
+ ## Other contract changes
73
+ Auth flow / rate limits / webhooks / versioning changes from the Step 1 sweep.
74
+
75
+ ## Rollout
76
+ Is the change live, behind a flag, or awaiting deploy? Environment availability
77
+ (staging first?), and the sequencing clients must respect.
78
+ ```
79
+
80
+ Rules for the body:
81
+
82
+ - **Examples over prose.** Every operation gets at least one realistic request/response example. Use plausible fake data — never real user data, tokens, or secrets copied from fixtures or logs.
83
+ - **Shape from code, not intent.** Derive field names, types, and nullability from the serializer/schema/resolver code in the diff (or the regenerated spec), not from the plan document or commit messages.
84
+ - **Self-contained.** No links into this repo's source as the primary explanation; the audience may not have repo access. Citing paths as *provenance* is fine.
85
+ - **Neutral tone for migration notes.** Describe what the client must handle; don't prescribe their internal architecture.
86
+
87
+ ## Step 3 — Verify before handing off
88
+
89
+ 1. Re-read each documented operation against the actual code one final time — signatures drift during writing.
90
+ 2. Grep the diff for API-relevant files you did NOT document and confirm each is genuinely client-invisible (internal refactor, tests, comments).
91
+ 3. Confirm every **breaking** flag: would an unmodified existing client call still succeed? If unsure, mark it breaking — false alarms are cheaper than silent breakage.
92
+ 4. Exercise at least one added/changed endpoint against a locally running instance when feasible, and paste the real (redacted) response into the doc's example.
93
+ 5. Report the doc path to the user, flag the breaking-change count, and remind them `{{CLIENT_HANDOFF_DIR}}` may be gitignored — share or commit the file deliberately so it actually reaches the client teams.
94
+
95
+ ## Pitfalls
96
+
97
+ - **Documenting the plan instead of the code.** Plans drift during implementation; the diff is the truth. Always derive shapes from what actually merged into the branch.
98
+ - **Missing error-shape changes.** Clients parse error bodies too. A changed error envelope breaks clients just as hard as a changed success payload.
99
+ - **"Non-breaking" by wishful thinking.** A field that changed from `string` to `string | null` breaks any client that never null-checks. Judge breakage from the strictest plausible client, not the friendliest.
100
+ - **Serializer-level changes hidden behind unchanged routes.** A diff can change a response shape without touching any route file — sweep serializers/schemas/DTOs even when the route list looks untouched.
101
+ - **Stale doc after review fixes.** If review feedback changes the API after the handoff was written, regenerate the doc — a superseded handoff to clients is worse than none.
102
+
103
+ ## Verification
104
+
105
+ - The doc exists under `{{CLIENT_HANDOFF_DIR}}/`, is scoped-and-dated, and every API-relevant file in the diff is either documented or consciously excluded.
106
+ - Every breaking change appears in the **Breaking changes** section, not only in its per-operation entry.
107
+ - A client engineer with zero access to this repo could implement every documented change from the doc alone.
@@ -0,0 +1,42 @@
1
+ # `plan-execution/` — shell templates + shared partials
2
+
3
+ Source-of-truth fragments for the **plan-execution skill family** that
4
+ `vinta-derive-skills` renders into a target project's `ai-tools/skills/`:
5
+
6
+ - `implement-plan` (conductor) — parse → classify → resolve `WORKROOT` → per-phase loop → track → report.
7
+ - `implement-phase` — compose prompt + pick model + spawn implementer (one phase).
8
+ - `review-phase` — three-layer review + fix loop (shared by all three conductors).
9
+ - `integrate-phase` — push + open PR via context file (commit-strategy-resolved).
10
+ - `amend-plan` (conductor) — history-rewriting topology; reuses `review-phase` + the implementer-prompt partial.
11
+
12
+ ## Layout
13
+
14
+ ```
15
+ plan-execution/
16
+ ├─ shell/ one *-template.md per rendered SKILL.md (thin; mostly includes)
17
+ └─ partials/ shared bodies authored once, included by multiple shells
18
+ ```
19
+
20
+ ## Assembly mechanism (executed by the `vinta-derive-skills` agent)
21
+
22
+ Each shell carries include directives. The derive-skills agent, per skill:
23
+
24
+ 1. **Expand includes.**
25
+ - `<!-- include: partials/<file>.md -->` — splice the whole partial body in place of the marker.
26
+ - `<!-- include: partials/<file>.md#BLOCK -->` — splice only the block between `<!-- block-begin: BLOCK -->` and `<!-- block-end: BLOCK -->` inside that partial (markers themselves excluded).
27
+ 2. **Substitute `{{PLACEHOLDER}}`** using the inventory + Step 0 interview answers (same substitution table as every other Bucket B template — see [../../SKILL.md](../../SKILL.md)).
28
+ 3. **Write** the assembled, fully-substituted body to `ai-tools/skills/<name>/SKILL.md`.
29
+
30
+ Partials and shells are **not** `SKILL.md` files, so they legitimately keep `{{...}}` and `<!-- include -->` markers; `validate-skill-md` never walks them. The **shipped** SKILL.md must have every include expanded and every placeholder substituted — validate that before saving.
31
+
32
+ ## The `WORKROOT` seam (why worktree branching is gone)
33
+
34
+ The conductor resolves three values **once** (see `partials/worktree-seam.md#WORKROOT_RESOLUTION`) and passes them to every sub-skill as data:
35
+
36
+ | Value | `use_worktree = false` | `use_worktree = true` |
37
+ |---|---|---|
38
+ | `WORKROOT` | `<main_checkout>` | `<worktree_path>` |
39
+ | `BASE_BRANCH` | `{{DEFAULT_BRANCH}}` | `<worktree_branch>` |
40
+ | `SANDBOX_TIER` | `none` | `enforced` \| `none` (probed by prepare-worktree) |
41
+
42
+ Every `git` / lint / test / build call in every sub-skill uses `git -C <WORKROOT>` **uniformly** — no `if use_worktree` inside them. Only two genuine conditionals remain, each local and data-driven: the `SANDBOX_TIER`-gated spawn wrap in `implement-phase`, and the `WORKROOT != main_checkout`-gated stray-write check in `review-phase`.