create-claude-cabinet 0.24.0 → 0.25.1

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 (38) hide show
  1. package/README.md +5 -5
  2. package/lib/cli.js +81 -2
  3. package/lib/verify-setup.js +140 -0
  4. package/package.json +1 -1
  5. package/templates/skills/debrief/phases/verify-coverage.md +101 -0
  6. package/templates/skills/execute/phases/verify-emit.md +104 -0
  7. package/templates/skills/plan/phases/verify-plan.md +97 -0
  8. package/templates/skills/verify/SKILL.md +228 -0
  9. package/templates/skills/verify/install.sh +342 -0
  10. package/templates/skills/verify/phases/calibrate.md +115 -0
  11. package/templates/skills/verify/phases/discover.md +114 -0
  12. package/templates/skills/verify/phases/draft.md +92 -0
  13. package/templates/skills/verify/phases/generate.md +71 -0
  14. package/templates/skills/verify/phases/scenario-template.md +119 -0
  15. package/templates/skills/verify/phases/update.md +81 -0
  16. package/templates/verify-runtime/CONVENTIONS.md +331 -0
  17. package/templates/verify-runtime/README.md +59 -0
  18. package/templates/verify-runtime/package-lock.json +2008 -0
  19. package/templates/verify-runtime/package.json +34 -0
  20. package/templates/verify-runtime/src/auto-check.ts +56 -0
  21. package/templates/verify-runtime/src/cli/preflight.ts +14 -0
  22. package/templates/verify-runtime/src/cli/report-last.ts +191 -0
  23. package/templates/verify-runtime/src/cli/report-status.ts +365 -0
  24. package/templates/verify-runtime/src/fixture-loader.ts +87 -0
  25. package/templates/verify-runtime/src/fresh-pass-cache.ts +193 -0
  26. package/templates/verify-runtime/src/human-verdict.ts +194 -0
  27. package/templates/verify-runtime/src/index.ts +66 -0
  28. package/templates/verify-runtime/src/manual-runner.ts +257 -0
  29. package/templates/verify-runtime/src/output.ts +177 -0
  30. package/templates/verify-runtime/src/path-hash.ts +241 -0
  31. package/templates/verify-runtime/src/preflight.ts +120 -0
  32. package/templates/verify-runtime/src/verdict-recorder.ts +318 -0
  33. package/templates/verify-runtime/src/world.ts +188 -0
  34. package/templates/verify-runtime/test/fixtures/sample.feature +17 -0
  35. package/templates/verify-runtime/test/fresh-pass-cache.test.ts +289 -0
  36. package/templates/verify-runtime/test/path-hash.test.ts +264 -0
  37. package/templates/verify-runtime/test/verdict-recorder.test.ts +58 -0
  38. package/templates/verify-runtime/tsconfig.json +16 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@ gives Claude a memory, 31 domain experts, a planning process, and the
5
5
  habit of starting sessions informed and ending them properly.
6
6
 
7
7
  Built by a guy who'd rather talk to Claude than write code. Most of it
8
- was built by Claude. I just complained until it worked.
8
+ was built by Claude. I just complained until it (mostly) worked.
9
9
 
10
10
  ## The Idea
11
11
 
@@ -63,8 +63,8 @@ automatically.
63
63
 
64
64
  - **`/orient`** — open every session with this. Claude reads project
65
65
  state, checks health, surfaces what needs attention, and briefs you
66
- so you never start blind. Think of it as the morning briefing before
67
- the cabinet gets to work.
66
+ so you never start blind. Think of it as the briefing before the
67
+ cabinet gets to work.
68
68
  - **`/debrief`** — close every session with this. Claude marks work
69
69
  done, records lessons, updates state, and prepares the briefing for
70
70
  next time. Without debrief, the next orient starts with stale
@@ -134,7 +134,7 @@ hooks — things that keep going wrong become things that can't go wrong.
134
134
 
135
135
  ## Your Workflow
136
136
 
137
- The day-to-day rhythm:
137
+ The rhythm of each session:
138
138
 
139
139
  1. **Start a session** → `/orient` (get briefed)
140
140
  2. **Do your work** → talk to Claude, use `/plan` for anything non-trivial
@@ -227,7 +227,7 @@ other.
227
227
  ## Philosophy
228
228
 
229
229
  This started as the process layer of [Flow](https://github.com/orenmagid/flow),
230
- a cognitive workspace built on Claude Code over months of daily use. The
230
+ a cognitive workspace built on Claude Code over months of intensive use. The
231
231
  patterns that emerged — the session loop, cabinet-style audits, feedback
232
232
  enforcement — turned out to be transferable to any project.
233
233
 
package/lib/cli.js CHANGED
@@ -8,6 +8,7 @@ const { mergeSettings } = require('./settings-merge');
8
8
  const { create: createMetadata, read: readMetadata } = require('./metadata');
9
9
  const { setupDb } = require('./db-setup');
10
10
  const { setupOmega } = require('./omega-setup');
11
+ const { setupVerifyRuntime } = require('./verify-setup');
11
12
  const { reset } = require('./reset');
12
13
 
13
14
  const VERSION = require('../package.json').version;
@@ -451,6 +452,20 @@ const MODULES = {
451
452
  needsOmega: true,
452
453
  templates: ['skills/memory', 'scripts/cabinet-memory-adapter.py', 'scripts/migrate-memory-to-omega.py', 'rules/memory-capture.md', 'hooks/omega-memory-guard.sh'],
453
454
  },
455
+ 'verify': {
456
+ name: 'Verification Harness (Cucumber + Playwright)',
457
+ description: 'Walkthrough verification with human-in-the-loop verdict pauses. Replaces flat AC lists with re-runnable user-journey scenarios. Includes /verify skeleton skill + cabinet-verify npm runtime + opt-in integration phases for /plan, /execute, /debrief.',
458
+ mandatory: false,
459
+ default: false,
460
+ lean: false,
461
+ templates: [
462
+ 'skills/verify',
463
+ 'skills/plan/phases/verify-plan.md',
464
+ 'skills/execute/phases/verify-emit.md',
465
+ 'skills/debrief/phases/verify-coverage.md',
466
+ ],
467
+ postInstall: 'verify-setup',
468
+ },
454
469
  };
455
470
 
456
471
  /** Recursively collect all relative file paths under a directory. */
@@ -504,7 +519,8 @@ function parseArgs(argv) {
504
519
  targetDir: '.',
505
520
  };
506
521
 
507
- for (const arg of args) {
522
+ for (let i = 0; i < args.length; i++) {
523
+ const arg = args[i];
508
524
  if (arg === '--yes' || arg === '-y') flags.yes = true;
509
525
  else if (arg === '--lean') flags.lean = true;
510
526
  else if (arg === '--no-db') flags.noDb = true;
@@ -512,6 +528,9 @@ function parseArgs(argv) {
512
528
  else if (arg === '--help' || arg === '-h') flags.help = true;
513
529
  else if (arg === '--reset') flags.reset = true;
514
530
  else if (arg === '--force') flags.force = true;
531
+ else if (arg === '--modules' && i + 1 < args.length) {
532
+ flags.modules = args[++i].split(',').map(s => s.trim()).filter(Boolean);
533
+ }
515
534
  else if (!arg.startsWith('-')) flags.targetDir = arg;
516
535
  }
517
536
 
@@ -526,6 +545,8 @@ function printHelp() {
526
545
  --yes, -y Accept all defaults, no prompts
527
546
  --lean Install core modules only (no work-tracking, compliance, validate)
528
547
  --no-db Skip work tracking database setup
548
+ --modules <keys> Comma-separated module keys to install (e.g., 'verify,memory').
549
+ Mandatory modules are always included.
529
550
  --dry-run Show what would be copied without writing
530
551
  --reset Remove Claude Cabinet files (uses manifest for safety)
531
552
  --force With --reset: remove even customized files
@@ -665,7 +686,45 @@ async function run() {
665
686
  let skippedModules = {};
666
687
  let includeDb = !flags.noDb;
667
688
 
668
- if (flags.lean) {
689
+ if (flags.modules) {
690
+ // Explicit module selection via --modules <key1,key2,...>. Mandatory
691
+ // modules are always included regardless of the flag value, so the
692
+ // installer always has session-loop etc. available.
693
+ //
694
+ // On an upgrade (existing .ccrc.json present), --modules is treated
695
+ // ADDITIVELY: the requested keys are merged with whatever the
696
+ // existing installation already had enabled. Otherwise an `--modules
697
+ // verify` invocation on a project with 9 enabled modules would
698
+ // silently deregister 8 of them. Replacing the existing set on
699
+ // upgrade is almost never what the operator wants. (Fresh installs
700
+ // unchanged — there's no prior set to merge with.)
701
+ const mandatoryKeys = Object.entries(MODULES)
702
+ .filter(([, mod]) => mod.mandatory)
703
+ .map(([key]) => key);
704
+ const requested = flags.modules.filter(k => MODULES[k]);
705
+ const unknown = flags.modules.filter(k => !MODULES[k]);
706
+ if (unknown.length > 0) {
707
+ console.log(` ⚠ Unknown modules ignored: ${unknown.join(', ')}`);
708
+ console.log(` Known modules: ${Object.keys(MODULES).join(', ')}`);
709
+ }
710
+ let existingEnabled = [];
711
+ if (dirState === 'existing-install') {
712
+ const existing = readMetadata(projectDir);
713
+ existingEnabled = Object.entries(existing.modules || {})
714
+ .filter(([k, enabled]) => enabled && MODULES[k])
715
+ .map(([k]) => k);
716
+ if (existingEnabled.length > 0) {
717
+ console.log(` Existing modules carried forward: ${existingEnabled.join(', ')}`);
718
+ }
719
+ }
720
+ selectedModules = [...new Set([...mandatoryKeys, ...existingEnabled, ...requested])];
721
+ const skippedKeys = Object.keys(MODULES).filter(k => !selectedModules.includes(k));
722
+ for (const k of skippedKeys) {
723
+ skippedModules[k] = `Skipped by --modules flag`;
724
+ }
725
+ if (!selectedModules.includes('work-tracking')) includeDb = false;
726
+ console.log(` Installing modules: ${selectedModules.join(', ')}\n`);
727
+ } else if (flags.lean) {
669
728
  selectedModules = Object.entries(MODULES)
670
729
  .filter(([, mod]) => mod.mandatory || mod.lean)
671
730
  .map(([key]) => key);
@@ -955,6 +1014,26 @@ async function run() {
955
1014
  }
956
1015
  }
957
1016
 
1017
+ // --- Run module postInstall hooks ---
1018
+ // Modules with a `postInstall` field dispatch to a matching setup
1019
+ // function after templates are copied. Currently only 'verify-setup'
1020
+ // (the cabinet-verify tarball builder) is wired.
1021
+ for (const moduleKey of selectedModules) {
1022
+ const mod = MODULES[moduleKey];
1023
+ if (!mod.postInstall) continue;
1024
+ if (mod.postInstall === 'verify-setup') {
1025
+ try {
1026
+ console.log('');
1027
+ const verifyResult = setupVerifyRuntime({ dryRun: !!flags.dryRun });
1028
+ for (const r of verifyResult.results || []) console.log(` 📋 ${r}`);
1029
+ } catch (err) {
1030
+ console.log(` ⚠ cabinet-verify runtime setup failed: ${err.message}`);
1031
+ console.log(' /verify install.sh will fail until the runtime is installed.');
1032
+ console.log(' Re-run the installer to retry.');
1033
+ }
1034
+ }
1035
+ }
1036
+
958
1037
  // --- Manifest key migration (act:d1f16bee) ---
959
1038
  // When CC renames directories (e.g., perspectives/ → cabinet-*/), old manifest
960
1039
  // keys no longer match new template paths. Migrate keys BEFORE cleanup so the
@@ -0,0 +1,140 @@
1
+ /**
2
+ * verify-setup.js — install the cabinet-verify runtime tarball at
3
+ * ~/.claude-cabinet/verify/<version>/dist/.
4
+ *
5
+ * Mirrors the omega-setup.js pattern: dispatched from cli.js's install
6
+ * pipeline when the `verify` module is selected. Idempotent: skips if
7
+ * the matching-version tarball is already present.
8
+ *
9
+ * See templates/verify-runtime/CONVENTIONS.md §Install Dir and
10
+ * §Tarball Install Pattern for the frozen contract this implements.
11
+ */
12
+
13
+ const { execSync } = require('child_process');
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+
18
+ const CC_HOME = path.join(os.homedir(), '.claude-cabinet');
19
+ const VERIFY_BASE = path.join(CC_HOME, 'verify');
20
+
21
+ /**
22
+ * Set up the cabinet-verify runtime.
23
+ *
24
+ * Steps:
25
+ * 1. Read the version from templates/verify-runtime/package.json
26
+ * 2. Compute installDir = ~/.claude-cabinet/verify/<version>/dist/
27
+ * 3. If a tarball already exists at that path with the same version,
28
+ * log "already installed" and skip
29
+ * 4. Otherwise: mkdir -p installDir; npm pack inside templates/verify-runtime/;
30
+ * move the .tgz to installDir
31
+ * 5. Write ~/.claude-cabinet/verify/current/VERSION (single-line pointer
32
+ * used by /verify install.sh per CONVENTIONS.md Version Resolution)
33
+ *
34
+ * @param {Object} opts
35
+ * @param {boolean} [opts.dryRun] — print planned actions, don't execute
36
+ * @param {string} [opts.runtimeSourceDir] — override the templates/verify-runtime/
37
+ * location. Defaults to <repo>/templates/verify-runtime/ resolved from __dirname.
38
+ * @returns {Object} { installPath, version, status: 'installed'|'skipped'|'dry-run' }
39
+ */
40
+ function setupVerifyRuntime(opts = {}) {
41
+ const dryRun = !!opts.dryRun;
42
+ const runtimeSourceDir =
43
+ opts.runtimeSourceDir || path.resolve(__dirname, '..', 'templates', 'verify-runtime');
44
+
45
+ // 1. Read version
46
+ const packageJsonPath = path.join(runtimeSourceDir, 'package.json');
47
+ if (!fs.existsSync(packageJsonPath)) {
48
+ throw new Error(
49
+ `verify-setup: ${packageJsonPath} not found. Cannot resolve cabinet-verify version.`,
50
+ );
51
+ }
52
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
53
+ const version = pkg.version;
54
+ if (typeof version !== 'string' || version.length === 0) {
55
+ throw new Error(`verify-setup: ${packageJsonPath} has no version field`);
56
+ }
57
+
58
+ // 2. Compute installDir
59
+ const installDir = path.join(VERIFY_BASE, version, 'dist');
60
+ const tarballName = `cabinet-verify-${version}.tgz`;
61
+ const tarballPath = path.join(installDir, tarballName);
62
+ const versionPointer = path.join(VERIFY_BASE, 'current', 'VERSION');
63
+
64
+ const results = [];
65
+
66
+ if (dryRun) {
67
+ results.push(`Would install cabinet-verify@${version}`);
68
+ results.push(` source: ${runtimeSourceDir}`);
69
+ results.push(` target: ${tarballPath}`);
70
+ results.push(` pointer: ${versionPointer}`);
71
+ return { installPath: installDir, version, status: 'dry-run', results };
72
+ }
73
+
74
+ // 3. Idempotency check. Existence alone is not enough — an earlier
75
+ // stub-touch (e.g., a test fixture) can leave a 0-byte file at the
76
+ // tarball path. npm install on the consumer side then fails with
77
+ // ENODATA / TAR_BAD_ARCHIVE. Validate the file has non-trivial size
78
+ // before treating it as installed.
79
+ if (fs.existsSync(tarballPath) && fs.statSync(tarballPath).size > 1024) {
80
+ results.push(`cabinet-verify@${version} already installed (${tarballPath})`);
81
+ // Even on skip, ensure the VERSION pointer is up-to-date so a later
82
+ // version downgrade doesn't leave a stale pointer behind.
83
+ writeVersionPointer(versionPointer, version);
84
+ results.push(`Updated VERSION pointer: ${versionPointer}`);
85
+ return { installPath: installDir, version, status: 'skipped', results };
86
+ }
87
+
88
+ // Remove a 0-byte stub before re-packing so the move below doesn't
89
+ // hit a "file exists" surprise.
90
+ if (fs.existsSync(tarballPath)) {
91
+ fs.unlinkSync(tarballPath);
92
+ }
93
+
94
+ // 4. mkdir + npm pack + move
95
+ fs.mkdirSync(installDir, { recursive: true });
96
+ // Run npm pack in the runtime source dir. --pack-destination writes
97
+ // the tarball directly to installDir without an intermediate cwd move.
98
+ const packStdout = execSync(`npm pack --silent --pack-destination "${installDir}"`, {
99
+ cwd: runtimeSourceDir,
100
+ encoding: 'utf8',
101
+ }).trim();
102
+
103
+ // npm pack prints the tarball filename to stdout on the last non-empty line.
104
+ // Normalise to the expected name so consuming projects can rely on the
105
+ // CONVENTIONS.md-documented path.
106
+ const lastLine = packStdout.split('\n').filter(Boolean).pop() || '';
107
+ const producedName = path.basename(lastLine);
108
+ if (producedName && producedName !== tarballName) {
109
+ // npm pack may produce a name like 'cabinet-verify-0.1.0.tgz' that already
110
+ // matches; if it doesn't (e.g., npm prefixes with @scope/), rename to match.
111
+ const producedPath = path.join(installDir, producedName);
112
+ if (fs.existsSync(producedPath)) {
113
+ fs.renameSync(producedPath, tarballPath);
114
+ }
115
+ }
116
+
117
+ if (!fs.existsSync(tarballPath)) {
118
+ throw new Error(
119
+ `verify-setup: npm pack completed but ${tarballPath} not found. ` +
120
+ `stdout was: ${packStdout.slice(0, 500)}`,
121
+ );
122
+ }
123
+
124
+ results.push(`Installed cabinet-verify@${version} to ${tarballPath}`);
125
+
126
+ // 5. Write VERSION pointer
127
+ writeVersionPointer(versionPointer, version);
128
+ results.push(`Wrote VERSION pointer: ${versionPointer}`);
129
+
130
+ return { installPath: installDir, version, status: 'installed', results };
131
+ }
132
+
133
+ function writeVersionPointer(pointerPath, version) {
134
+ fs.mkdirSync(path.dirname(pointerPath), { recursive: true });
135
+ // No trailing newline — CONVENTIONS.md frozen contract requires the
136
+ // file to equal the version string exactly under `cat`.
137
+ fs.writeFileSync(pointerPath, version, 'utf8');
138
+ }
139
+
140
+ module.exports = { setupVerifyRuntime };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Claude Cabinet — opinionated process scaffolding for Claude Code projects",
5
5
  "bin": {
6
6
  "create-claude-cabinet": "bin/create-claude-cabinet.js"
@@ -0,0 +1,101 @@
1
+ # /debrief integration with cabinet-verify — verify-coverage phase
2
+
3
+ **Contract: v0.x soft — may change before v1.0.** This phase enables
4
+ the `/verify` integration with `/debrief`. It is a customization phase
5
+ (opt-in), copied into your project only when the `verify` module is
6
+ selected during `npx create-claude-cabinet`.
7
+
8
+ ## What this phase does
9
+
10
+ At session close, scan the acts shipped during the session window.
11
+ For each act that touched UI code, check whether a feature-file edit
12
+ landed in the same git commit window. If not, warn — drift risk:
13
+ the product changed but the scenarios didn't.
14
+
15
+ This is the safety net for the `/plan` + `/execute` integration. The
16
+ upstream phases TRY to keep features in sync during the session;
17
+ this phase catches the cases where they failed or weren't invoked.
18
+
19
+ ## No-op guard
20
+
21
+ The phase should exit silently if the project has no `e2e/features/`
22
+ directory.
23
+
24
+ ```bash
25
+ test -d e2e/features
26
+ ```
27
+
28
+ Without the runtime installed, there's nothing to be out of sync with.
29
+
30
+ ## When this phase runs
31
+
32
+ Position: during `/debrief`'s inventory phase, after acts have been
33
+ enumerated, before the briefing is presented to the user. The warning
34
+ appears in the **Attention Items** section of the debrief output.
35
+
36
+ ## Detection algorithm
37
+
38
+ 1. **Establish the session window.** The session-start git sha is
39
+ captured at orient (or, lacking that, the most recent commit
40
+ before the first session action). HEAD is the end of the window.
41
+ 2. **List feature-file edits in the window.**
42
+ ```bash
43
+ git diff --name-only <session-start-sha>..HEAD | grep '\.feature$'
44
+ ```
45
+ 3. **List shipped acts.** Query pib-db for actions completed during
46
+ the session:
47
+ ```bash
48
+ node scripts/pib-db.mjs query "SELECT fid, text, notes FROM actions WHERE completed = 1 AND completed_at >= '<session-start-iso>'"
49
+ ```
50
+ 4. **For each shipped act, decide whether it was UI-touching.** Look
51
+ at the act's notes' Surface Area section. If the surface includes
52
+ any of:
53
+ - `webapp/frontend/`, `app/`, `pages/`, `components/`
54
+ - paths matching project-specific UI conventions (read
55
+ `phases/ui-paths.md` if defined; otherwise use the default
56
+ heuristic above)
57
+
58
+ then it's UI-touching.
59
+ 5. **For each UI-touching act, check if any feature file in the
60
+ diff overlaps with the act's surface area or description.** If
61
+ not, the act is "uncovered."
62
+
63
+ ## Warning format
64
+
65
+ For each uncovered UI act, emit one Attention Items entry:
66
+
67
+ > **Act <fid> shipped without a feature-file update — drift risk.**
68
+ > The act touched [paths] but no `.feature` file changed in the same
69
+ > session window. Consider:
70
+ > - `/verify update <fid>` to propose the matching scenario edits
71
+ > - Or accept the drift if the change isn't user-visible enough to
72
+ > warrant a scenario update.
73
+
74
+ Advisory only — debrief still completes. The user decides whether to
75
+ run `/verify update` immediately or defer.
76
+
77
+ ## Tuning to reduce false positives
78
+
79
+ The default heuristic over-warns. Two common refinements:
80
+
81
+ 1. **Path filter.** Project-specific UI paths in `phases/ui-paths.md`
82
+ (if defined) override the default heuristic. For example, a project
83
+ where `app/` is the framework's app dir (server-side rendering)
84
+ but `webapp/frontend/` is the SPA might only count the latter.
85
+ 2. **Per-act opt-out.** An act can declare in its notes that it's a
86
+ "no-feature-edit-needed" act:
87
+ ```
88
+ ## Verify Coverage
89
+ Skip: this change is internal — no UI behavior changed.
90
+ ```
91
+ The phase reads this and skips the act.
92
+
93
+ ## What this phase does NOT do
94
+
95
+ - It does not file an action for the uncovered act. The user runs
96
+ `/verify update` (or chooses to ignore) at their discretion.
97
+ - It does not block debrief. Even with 10 uncovered acts, debrief
98
+ completes — the warnings just accumulate in the Attention Items
99
+ section.
100
+ - It does not run the verification suite (`npm run verify`). That's
101
+ the user's call after they decide whether to update scenarios.
@@ -0,0 +1,104 @@
1
+ # /execute integration with cabinet-verify — verify-emit phase
2
+
3
+ **Contract: v0.x soft — may change before v1.0.** This phase enables
4
+ the `/verify` integration with `/execute`. It is a customization phase
5
+ (opt-in), copied into your project only when the `verify` module is
6
+ selected during `npx create-claude-cabinet`.
7
+
8
+ ## What this phase does
9
+
10
+ When `/execute` runs a plan that contains a `## Verify Plan` section,
11
+ this phase writes the proposed feature-file edits alongside the
12
+ code change in the same execution pass. The goal: feature files
13
+ stay in sync with the product because the edit lands in the same
14
+ commit as the code that motivated it.
15
+
16
+ ## No-op guards
17
+
18
+ This phase will exit silently in two cases — checked in order:
19
+
20
+ 1. **The loaded plan has no `## Verify Plan` section.** Most plans
21
+ (backend-only, refactors, deploy configs) won't have one. No warning,
22
+ no log line.
23
+ 2. **The project has no `e2e/features/` directory.** Same as the
24
+ `/plan` integration phase — without the runtime installed, there
25
+ are no feature files to edit. Skip entirely.
26
+
27
+ Detection:
28
+
29
+ ```bash
30
+ test -d e2e/features
31
+ ```
32
+
33
+ Both guards must pass before this phase emits any output or writes
34
+ any files.
35
+
36
+ ## When this phase runs
37
+
38
+ Position: after the file-group implementation loop, before the
39
+ pre-commit cabinet sweep. The order matters — feature-file edits
40
+ should be in the working tree when cabinet members review the full
41
+ diff, so a security or QA cabinet member sees that the scenarios were
42
+ updated alongside the code.
43
+
44
+ ## How to read the Verify Plan section
45
+
46
+ Parse the plan notes for `## Verify Plan`. Each entry under the
47
+ heading begins with `- features/<file>.feature:` followed by a verb
48
+ phrase. Verbs the parser recognizes:
49
+
50
+ - `ADD step after <anchor>` — insert a new step at a specific position
51
+ - `MODIFY step <checkId>` — change an existing step's text
52
+ - `NEW scenario` — create a whole new `.feature` file
53
+ - `REMOVE step <checkId>` — delete a step
54
+ - `(deferred) <anything>` — record but don't write
55
+
56
+ For each non-deferred entry:
57
+
58
+ 1. Resolve the target file. If it's `NEW scenario`, the file is the
59
+ one named in the entry (must not exist yet).
60
+ 2. Apply the edit. For ADD/MODIFY/REMOVE, parse the surrounding step
61
+ structure to find the right insertion point. For NEW, write the
62
+ file using the skeleton from `templates/skills/verify/phases/scenario-template.md`.
63
+ 3. Verify the result by re-reading the file. The pathHash of any
64
+ modified step changes — that's expected (per CONVENTIONS.md the
65
+ cache will invalidate downstream verdicts).
66
+
67
+ ## AC enforcement
68
+
69
+ Each Verify Plan entry maps to an implicit acceptance criterion:
70
+ "the feature-file edit was written." If `/execute` finishes its
71
+ implementation loop but a Verify Plan edit didn't land, mark that
72
+ AC unmet in the verification breadcrumb.
73
+
74
+ The code change itself can still ship — Verify Plan failures are
75
+ acceptance failures, not blockers — but the user sees the unmet AC
76
+ when they review the breadcrumb. The `/debrief` integration's
77
+ `verify-coverage` phase catches this even if the user misses it.
78
+
79
+ ## Failure handling
80
+
81
+ If a feature-file edit can't be applied (e.g., the anchor step
82
+ described in `ADD step after <anchor>` doesn't exist in the file):
83
+
84
+ 1. Print a warning naming the file, the edit, and the reason
85
+ 2. Mark the corresponding AC unmet in the breadcrumb's `deviations`
86
+ array
87
+ 3. Continue with the next entry — don't fail the whole execute pass
88
+
89
+ The user resolves these in a follow-up `/verify update <fid>` invocation
90
+ after the code change lands.
91
+
92
+ ## NEW scenario handling
93
+
94
+ When the entry is `NEW scenario`, the feature file must not exist
95
+ yet. If it does, escalate:
96
+
97
+ > Verify Plan proposes a NEW scenario at features/13-first-time-user.feature
98
+ > but that file already exists. Did you mean ADD step or MODIFY?
99
+ > Skipping this entry — fix the plan and re-run.
100
+
101
+ NEW scenarios use the scaffolding shape from `phases/scenario-template.md`
102
+ of `/verify`. Pull the template content and substitute the scenario
103
+ name, persona tag, cost tag, and a starter journey from the entry's
104
+ prose.
@@ -0,0 +1,97 @@
1
+ # /plan integration with cabinet-verify — verify-plan phase
2
+
3
+ **Contract: v0.x soft — may change before v1.0.** This phase enables
4
+ the `/verify` integration with `/plan`. It is a customization phase
5
+ (opt-in), copied into your project only when the `verify` module is
6
+ selected during `npx create-claude-cabinet`.
7
+
8
+ ## What this phase does
9
+
10
+ When `/plan` produces a plan involving UI changes, emit a
11
+ `## Verify Plan` section that lists feature-file paths plus proposed
12
+ edits. `/execute` reads this section and writes the feature-file
13
+ edits alongside the code change, so scenarios stay in sync with the
14
+ product.
15
+
16
+ ## No-op guard
17
+
18
+ Before doing anything, this phase checks for `e2e/features/` in the
19
+ project root. **If the directory is absent, exit silently** — the
20
+ project hasn't installed the `/verify` runtime, and emitting a
21
+ Verify Plan section against scenarios that don't exist would be
22
+ noise.
23
+
24
+ Detection:
25
+
26
+ ```bash
27
+ test -d e2e/features
28
+ ```
29
+
30
+ If the test fails, skip this phase entirely. No warning, no log line.
31
+ The user installs `/verify` when they're ready; until then, `/plan`
32
+ behaves as if this phase isn't here.
33
+
34
+ ## When to emit a Verify Plan section
35
+
36
+ Emit the section when the plan's surface area touches UI code. The
37
+ specific signals depend on project conventions; common patterns:
38
+
39
+ - Modified file path matches `webapp/frontend/src/`, `app/`, `pages/`,
40
+ or `components/`
41
+ - Plan description mentions a route, a page, a modal, a UI flow
42
+ - Plan changes user-visible behavior (vs. internal refactor, schema
43
+ migration, deploy config)
44
+
45
+ If unsure, ask the user during plan drafting: "Does this plan need a
46
+ Verify Plan section? (y/n)". Don't auto-emit on every plan — that
47
+ generates noise for backend-only changes.
48
+
49
+ ## Section format
50
+
51
+ The `## Verify Plan` section sits alongside the existing
52
+ `## Acceptance Criteria` and `## Surface Area` sections. It lists
53
+ feature-file paths plus proposed edits:
54
+
55
+ ```markdown
56
+ ## Verify Plan
57
+
58
+ - features/01-desktop-rewrite.feature: ADD step after the existing
59
+ "settings-bar-appears" check — verify the model picker is absent
60
+ (1.10b model-picker-absent).
61
+ - features/03-multi-tab.feature: MODIFY the "active-run-banner-text"
62
+ step to assert the new banner copy after Phase B.
63
+ - features/13-first-time-user.feature: NEW scenario — invite roundtrip
64
+ + empty states for a freshly-signed-up user.
65
+ ```
66
+
67
+ Edit verbs:
68
+ - **ADD step after `<anchor>`** — a new step inserted at a specific
69
+ position
70
+ - **MODIFY step `<checkId>`** — change an existing step's assertion
71
+ - **NEW scenario** — a whole new `.feature` file
72
+ - **REMOVE step `<checkId>`** — for deprecations
73
+
74
+ Each entry begins with `- features/<file>.feature:` so the line is
75
+ machine-parseable for `/execute`'s `verify-emit` phase.
76
+
77
+ ## Integration with acceptance criteria
78
+
79
+ The Verify Plan section is **treated like an AC**: if `/execute`
80
+ fails to write the feature-file edit, the corresponding AC is marked
81
+ unmet. This is enforced by the `verify-emit` phase in `/execute`.
82
+
83
+ You may explicitly tag a Verify Plan entry as deferred:
84
+
85
+ ```markdown
86
+ - features/05-framework-smoke.feature: (deferred) ADD step for the
87
+ new framework migration banner — requires the migration to land
88
+ in webapp first.
89
+ ```
90
+
91
+ Deferred entries are recorded but don't block execution.
92
+
93
+ ## When the plan has no UI surface
94
+
95
+ Backend-only plans, schema migrations, and infrastructure changes
96
+ don't need a Verify Plan section. Emitting one for them generates
97
+ noise during `/execute`. Use judgment — when in doubt, ask the user.