@lumenflow/cli 2.13.0 → 2.14.0

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.
package/README.md CHANGED
@@ -51,130 +51,132 @@ This package provides CLI commands for the LumenFlow workflow framework, includi
51
51
 
52
52
  ### Work Unit Management
53
53
 
54
- | Command | Description |
55
- | ---------------- | --------------------------------------------------------------------------- |
56
- | `wu-block` | Block a work unit and move it from in-progress to blocked status |
57
- | `wu-claim` | Claim a work unit by creating a worktree/branch and updating status |
58
- | `wu-cleanup` | Clean up worktree and branch after PR merge (PR-based completion workflow) |
59
- | `wu-create` | Create a new Work Unit with micro-worktree isolation (race-safe) |
60
- | `wu-delete` | Safely delete WU YAML files with micro-worktree isolation |
61
- | `wu-deps` | Visualize WU dependency graph |
62
- | `wu-done` | WU Done Helper |
63
- | `wu-edit` | Edit WU spec files with micro-worktree isolation |
64
- | `wu-infer-lane` | WU Lane Inference CLI (WU-908) |
65
- | `wu-preflight` | Fast validation of code_paths and test paths before gates run. |
66
- | `wu-prep` | Prepare WU for completion (run gates in worktree) |
67
- | `wu-proto` | Create and claim a prototype WU with relaxed validation (rapid prototyping) |
68
- | `wu-prune` | WU Prune Utility |
69
- | `wu-recover` | Analyze and fix WU state inconsistencies (WU-1090) |
70
- | `wu-release` | Release an orphaned WU from in_progress back to ready state for reclaiming |
71
- | `wu-repair` | WU State Repair Tool (Unified - WU-1826, WU-2240) |
72
- | `wu-spawn` | Generate Task tool invocation for sub-agent WU execution |
73
- | `wu-status` | Show WU status, location, and valid commands (WU-1090) |
74
- | `wu-unblock` | Unblock a work unit and move it from blocked to in-progress status |
75
- | `wu-unlock-lane` | Safely unlock a lane lock with audit logging |
76
- | `wu-validate` | Validate WU YAML files against schema (strict mode by default, WU-1329) |
54
+ | Command | Description |
55
+ | ---------------- | --------------------------------------------- |
56
+ | `wu-block` | Block WU with reason |
57
+ | `wu-claim` | Claim WU and create worktree |
58
+ | `wu-cleanup` | Cleanup after PR merge |
59
+ | `wu-create` | Create new WU spec |
60
+ | `wu-delete` | Delete WU spec and cleanup |
61
+ | `wu-deps` | Show WU dependencies |
62
+ | `wu-done` | Complete WU (merge, stamp, cleanup) from main |
63
+ | `wu-edit` | Edit WU spec fields |
64
+ | `wu-infer-lane` | Infer lane from code paths/description |
65
+ | `wu-preflight` | Pre-flight checks before wu:done |
66
+ | `wu-prep` | Run gates in worktree, prep for wu:done |
67
+ | `wu-proto` | Create WU prototype |
68
+ | `wu-prune` | Clean stale worktrees |
69
+ | `wu-recover` | Analyze and fix WU state inconsistencies |
70
+ | `wu-release` | Release orphaned WU (in_progress to ready) |
71
+ | `wu-repair` | Repair WU state issues |
72
+ | `wu-spawn` | Generate sub-agent spawn prompt |
73
+ | `wu-status` | Show WU status, location, valid commands |
74
+ | `wu-unblock` | Unblock WU |
75
+ | `wu-unlock-lane` | Unlock stuck lane |
76
+ | `wu-validate` | Validate WU spec |
77
77
 
78
78
  ### Memory & Session
79
79
 
80
- | Command | Description |
81
- | -------------------- | ------------------------------------------------------------- |
82
- | `agent-issues-query` | Query logged issues from agent sessions |
83
- | `agent-log-issue` | Log an issue encountered during agent work |
84
- | `agent-session` | Start an agent session for a WU |
85
- | `agent-session-end` | End an agent session |
86
- | `mem-checkpoint` | Create a checkpoint node for context snapshots |
87
- | `mem-cleanup` | Prune closed memory nodes based on lifecycle policy and TTL |
88
- | `mem-context` | Generate context injection block for wu:spawn prompts |
89
- | `mem-create` | Create a memory node with optional provenance tracking |
90
- | `mem-delete` | Delete memory nodes (soft delete via metadata.status=deleted) |
91
- | `mem-export` | Export memory nodes as markdown or JSON |
92
- | `mem-inbox` | Read coordination signals from other agents |
93
- | `mem-init` | Initialize memory layer in repository |
94
- | `mem-ready` | Query ready nodes for a WU (deterministic ordering) |
95
- | `mem-signal` | Send a coordination signal to other agents |
96
- | `mem-start` | Create a session node linked to a WU |
97
- | `mem-summarize` | Rollup older memory nodes into summary nodes for compaction |
98
- | `mem-triage` | Review discovery nodes and promote to WUs or archive |
80
+ | Command | Description |
81
+ | -------------------- | ------------------------------------------ |
82
+ | `agent-issues-query` | Query GitHub issues for agent work |
83
+ | `agent-log-issue` | Log issue during agent session |
84
+ | `agent-session` | Start agent session |
85
+ | `agent-session-end` | End agent session |
86
+ | `mem-checkpoint` | Save progress checkpoint |
87
+ | `mem-cleanup` | Clean up stale memory data |
88
+ | `mem-context` | Get context for current lane/WU |
89
+ | `mem-create` | Create memory node (bug discovery) |
90
+ | `mem-delete` | Delete/archive a memory node |
91
+ | `mem-export` | Export memory as markdown |
92
+ | `mem-inbox` | Check coordination signals |
93
+ | `mem-init` | Initialize memory for WU |
94
+ | `mem-ready` | Check pending memory nodes |
95
+ | `mem-recover` | Generate recovery context after compaction |
96
+ | `mem-signal` | Broadcast coordination signal |
97
+ | `mem-start` | Start a memory session |
98
+ | `mem-summarize` | Summarize memory context |
99
+ | `mem-triage` | Triage discovered bugs |
99
100
 
100
101
  ### Initiative Orchestration
101
102
 
102
- | Command | Description |
103
- | ---------------------------- | ------------------------------------------------------------------ |
104
- | `initiative-add-wu` | Link one or more WUs to an initiative bidirectionally |
105
- | `initiative-bulk-assign-wus` | Bulk-assign orphaned WUs to initiatives based on lane prefix rules |
106
- | `initiative-create` | Create a new Initiative with micro-worktree isolation (race-safe) |
107
- | `initiative-edit` | Edit Initiative YAML files with micro-worktree isolation |
108
- | `initiative-list` | List all initiatives with progress percentages |
109
- | `initiative-plan` | Link a plan file to an initiative |
110
- | `initiative-status` | Show detailed initiative view with phases and WUs |
111
- | `orchestrate-init-status` | Show initiative orchestration status |
112
- | `orchestrate-initiative` | Orchestrate initiative execution with agents |
113
- | `orchestrate-monitor` | Monitor spawned agent progress and signals |
114
- | `spawn-list` | Display spawn trees for WUs or initiatives |
103
+ | Command | Description |
104
+ | ---------------------------- | -------------------------------- |
105
+ | `initiative-add-wu` | Add WU to initiative |
106
+ | `initiative-bulk-assign-wus` | Bulk assign WUs to initiative |
107
+ | `initiative-create` | Create new initiative |
108
+ | `initiative-edit` | Edit initiative fields |
109
+ | `initiative-list` | List all initiatives |
110
+ | `initiative-plan` | Link plan to initiative |
111
+ | `initiative-status` | Show initiative status |
112
+ | `orchestrate-init-status` | Compact initiative progress view |
113
+ | `orchestrate-initiative` | Orchestrate initiative execution |
114
+ | `orchestrate-monitor` | Monitor spawn/agent activity |
115
+ | `spawn-list` | List active spawned agents |
115
116
 
116
117
  ### Metrics & Analytics
117
118
 
118
- | Command | Description |
119
- | ------------------ | ----------------------------------------------- |
120
- | `flow-bottlenecks` | Identify workflow bottlenecks and critical path |
121
- | `flow-report` | Generate DORA metrics flow report |
122
- | `metrics` | Unified Metrics CLI with subcommands (WU-1110) |
123
- | `metrics-snapshot` | Capture current metrics snapshot for dashboards |
119
+ | Command | Description |
120
+ | ------------------ | ---------------------------- |
121
+ | `flow-bottlenecks` | Identify flow bottlenecks |
122
+ | `flow-report` | Generate flow metrics report |
123
+ | `metrics` | View workflow metrics |
124
+ | `metrics-snapshot` | Capture metrics snapshot |
124
125
 
125
126
  ### Lane Tooling
126
127
 
127
- | Command | Description |
128
- | -------------- | -------------------------------------------------- |
129
- | `lane-health` | Check lane configuration health (WU-1188) |
130
- | `lane-suggest` | Suggest lane definitions based on codebase context |
128
+ | Command | Description |
129
+ | -------------- | --------------------------- |
130
+ | `lane-health` | Check lane config health |
131
+ | `lane-suggest` | Suggest lane for code paths |
131
132
 
132
133
  ### Verification & Gates
133
134
 
134
- | Command | Description |
135
- | ------- | ------------------------------------------------------------------------------------------ |
136
- | `gates` | Run quality gates with support for docs-only mode, incremental linting, and tiered testing |
135
+ | Command | Description |
136
+ | -------------------- | ----------------------------- |
137
+ | `gates` | Run all quality gates |
138
+ | `lumenflow-gates` | Run all quality gates (alias) |
139
+ | `lumenflow-validate` | Run validation checks (alias) |
140
+ | `validate` | Run validation checks |
137
141
 
138
142
  ### System & Setup
139
143
 
140
- | Command | Description |
141
- | -------------------------- | --------------------------------------------------------------------------------- |
142
- | `backlog-prune` | Backlog Prune Command |
143
- | `init-plan` | Link a plan file to an initiative |
144
- | `lumenflow` | Initialize LumenFlow in a project\n\n |
145
- | `lumenflow-commands` | List all available LumenFlow CLI commands |
146
- | `lumenflow-docs-sync` | Sync agent onboarding docs to existing projects (skips existing files by default) |
147
- | `lumenflow-doctor` | Check LumenFlow safety components and configuration |
148
- | `lumenflow-init` | Initialize LumenFlow in a project\n\n |
149
- | `lumenflow-integrate` | Integrate LumenFlow enforcement with AI client tools |
150
- | `lumenflow-metrics` | Unified Metrics CLI with subcommands (WU-1110) |
151
- | `lumenflow-release` | Release Command |
152
- | `lumenflow-sync-templates` | Sync internal docs to CLI templates for release-cycle maintenance |
153
- | `lumenflow-upgrade` | LumenFlow Upgrade CLI Command |
154
- | `lumenflow-validate` | |
155
- | `plan-create` | Create a new plan file in repo plansDir |
156
- | `plan-edit` | Edit a section in a plan file |
157
- | `plan-link` | Link a plan file to a WU or initiative |
158
- | `plan-promote` | Promote a plan to approved status |
159
- | `signal-cleanup` | Prune old signals based on TTL policy to prevent unbounded growth |
160
- | `state-bootstrap` | State Bootstrap Command |
161
- | `state-cleanup` | Orchestrate all state cleanup: signals, memory, events |
162
- | `state-doctor` | Check state integrity and optionally repair issues |
163
- | `sync-templates` | Sync internal docs to CLI templates for release-cycle maintenance |
164
- | `validate` | |
144
+ | Command | Description |
145
+ | -------------------------- | ----------------------------------------- |
146
+ | `backlog-prune` | Clean stale backlog entries |
147
+ | `init-plan` | Link plan to initiative (alias) |
148
+ | `lumenflow` | Initialize LumenFlow in a project |
149
+ | `lumenflow-commands` | List all available CLI commands |
150
+ | `lumenflow-docs-sync` | Sync agent docs (for upgrades) (alias) |
151
+ | `lumenflow-doctor` | Diagnose LumenFlow configuration |
152
+ | `lumenflow-init` | Initialize LumenFlow in a project (alias) |
153
+ | `lumenflow-integrate` | Generate enforcement hooks for client |
154
+ | `lumenflow-metrics` | View workflow metrics (alias) |
155
+ | `lumenflow-release` | Run release workflow |
156
+ | `lumenflow-sync-templates` | Sync templates to project |
157
+ | `lumenflow-upgrade` | Upgrade LumenFlow packages |
158
+ | `plan-create` | Create a new plan |
159
+ | `plan-edit` | Edit plan content |
160
+ | `plan-link` | Link plan to WU or initiative |
161
+ | `plan-promote` | Promote plan to WU |
162
+ | `signal-cleanup` | Clean up stale signals |
163
+ | `state-bootstrap` | Bootstrap state store |
164
+ | `state-cleanup` | Clean up stale state data |
165
+ | `state-doctor` | Diagnose state store issues |
166
+ | `sync-templates` | Sync templates to project (alias) |
165
167
 
166
168
  ### File & Git Operations
167
169
 
168
- | Command | Description |
169
- | ------------- | -------------------- |
170
- | `file-delete` | File Delete CLI Tool |
171
- | `file-edit` | File Edit CLI Tool |
172
- | `file-read` | File Read CLI Tool |
173
- | `file-write` | File Write CLI Tool |
174
- | `git-branch` | Git Branch CLI Tool |
175
- | `git-diff` | Git Diff CLI Tool |
176
- | `git-log` | Git Log CLI Tool |
177
- | `git-status` | Git Status CLI Tool |
170
+ | Command | Description |
171
+ | ------------- | -------------------------------- |
172
+ | `file-delete` | Delete file with audit trail |
173
+ | `file-edit` | Edit file with audit trail |
174
+ | `file-read` | Read file with audit trail |
175
+ | `file-write` | Write file with audit trail |
176
+ | `git-branch` | Show git branch with audit trail |
177
+ | `git-diff` | Show git diff with audit trail |
178
+ | `git-log` | Show git log with audit trail |
179
+ | `git-status` | Show git status with audit trail |
178
180
 
179
181
  <!-- END AUTO-GENERATED SECTION -->
180
182
 
@@ -279,5 +281,3 @@ For detailed upgrade instructions, migration guides, and troubleshooting, see [U
279
281
  ## License
280
282
 
281
283
  Apache-2.0
282
-
283
- <!-- MODIFIED -->
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Gates Graceful Degradation
3
+ *
4
+ * WU-1520: When gate scripts (format:check, lint, typecheck, spec:linter)
5
+ * are missing from package.json, emit a warning and skip instead of failing.
6
+ *
7
+ * Defense in depth: even if scaffold adds scripts, gates should degrade
8
+ * gracefully for any missing script.
9
+ *
10
+ * @module gates-graceful-degradation
11
+ */
12
+ import { readFileSync, existsSync } from 'node:fs';
13
+ import path from 'node:path';
14
+ /**
15
+ * Scripts that can be skipped when missing from package.json.
16
+ * These are optional tooling scripts that not every project configures.
17
+ */
18
+ export const SKIPPABLE_GATE_SCRIPTS = [
19
+ 'format:check',
20
+ 'lint',
21
+ 'typecheck',
22
+ 'spec:linter',
23
+ ];
24
+ /**
25
+ * Gates that are never skippable, regardless of configuration.
26
+ * These enforce critical invariants and must always run.
27
+ */
28
+ export const NON_SKIPPABLE_GATES = ['invariants'];
29
+ /**
30
+ * Check whether a script exists in a package.json scripts object.
31
+ *
32
+ * @param scriptName - The script name to check (e.g., 'lint', 'format:check')
33
+ * @param scripts - The scripts object from package.json, or undefined
34
+ * @returns true if the script exists
35
+ */
36
+ export function checkScriptExists(scriptName, scripts) {
37
+ if (!scripts)
38
+ return false;
39
+ return Object.prototype.hasOwnProperty.call(scripts, scriptName);
40
+ }
41
+ /**
42
+ * Load the scripts object from a package.json file.
43
+ *
44
+ * @param projectRoot - Project root directory containing package.json
45
+ * @returns The scripts object, or undefined if package.json is missing or unreadable
46
+ */
47
+ export function loadPackageJsonScripts(projectRoot) {
48
+ const packageJsonPath = path.join(projectRoot, 'package.json');
49
+ if (!existsSync(packageJsonPath))
50
+ return undefined;
51
+ try {
52
+ const content = readFileSync(packageJsonPath, 'utf8');
53
+ const pkg = JSON.parse(content);
54
+ return pkg.scripts;
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ }
60
+ /**
61
+ * Build a human-readable warning message for a missing gate script.
62
+ * Includes instructions on how to add the missing script.
63
+ *
64
+ * @param scriptName - The missing script name
65
+ * @returns Warning message string
66
+ */
67
+ export function buildMissingScriptWarning(scriptName) {
68
+ const suggestions = {
69
+ 'format:check': '"format:check": "prettier --check ."',
70
+ lint: '"lint": "eslint ."',
71
+ typecheck: '"typecheck": "tsc --noEmit"',
72
+ 'spec:linter': '"spec:linter": "node tools/spec-linter.js"',
73
+ };
74
+ const suggestion = suggestions[scriptName] ?? `"${scriptName}": "<your-command>"`;
75
+ return [
76
+ `Warning: "${scriptName}" script not found in package.json - skipping gate.`,
77
+ ` To enable this gate, add to your package.json scripts:`,
78
+ ` ${suggestion}`,
79
+ ].join('\n');
80
+ }
81
+ /**
82
+ * Determine whether a gate should be skipped due to a missing script.
83
+ *
84
+ * @param gateName - The gate name (e.g., 'lint', 'invariants')
85
+ * @param scriptName - The underlying script name, or null for non-script gates
86
+ * @param scripts - The scripts object from package.json
87
+ * @param strict - When true, missing scripts cause a hard failure instead of skip
88
+ * @returns 'skip' if the gate should be skipped, 'run' if it should run, 'fail' if strict mode failure
89
+ */
90
+ export function resolveGateAction(gateName, scriptName, scripts, strict) {
91
+ // Non-skippable gates always run
92
+ if (NON_SKIPPABLE_GATES.includes(gateName)) {
93
+ return 'run';
94
+ }
95
+ // If no script name is associated (e.g., custom run functions), always run
96
+ if (!scriptName) {
97
+ return 'run';
98
+ }
99
+ // If script exists, run it
100
+ if (checkScriptExists(scriptName, scripts)) {
101
+ return 'run';
102
+ }
103
+ // Script is missing
104
+ if (strict) {
105
+ return 'fail';
106
+ }
107
+ return 'skip';
108
+ }
109
+ /**
110
+ * Format a summary of gate results showing passed, skipped, and failed gates.
111
+ *
112
+ * @param results - Array of gate results
113
+ * @returns Formatted summary string
114
+ */
115
+ export function formatGateSummary(results) {
116
+ if (results.length === 0) {
117
+ return 'No gates were executed.';
118
+ }
119
+ const passed = results.filter((r) => r.status === 'passed');
120
+ const skipped = results.filter((r) => r.status === 'skipped');
121
+ const failed = results.filter((r) => r.status === 'failed');
122
+ const warned = results.filter((r) => r.status === 'warned');
123
+ const lines = [];
124
+ lines.push('Gate Summary:');
125
+ lines.push('');
126
+ for (const result of results) {
127
+ const statusIcon = result.status === 'passed'
128
+ ? 'PASS'
129
+ : result.status === 'skipped'
130
+ ? 'SKIP'
131
+ : result.status === 'warned'
132
+ ? 'WARN'
133
+ : 'FAIL';
134
+ const duration = result.durationMs > 0 ? ` (${result.durationMs}ms)` : '';
135
+ const reason = result.reason ? ` - ${result.reason}` : '';
136
+ lines.push(` [${statusIcon}] ${result.name}${duration}${reason}`);
137
+ }
138
+ lines.push('');
139
+ const parts = [];
140
+ if (passed.length > 0)
141
+ parts.push(`${passed.length} passed`);
142
+ if (skipped.length > 0)
143
+ parts.push(`${skipped.length} skipped`);
144
+ if (warned.length > 0)
145
+ parts.push(`${warned.length} warned`);
146
+ if (failed.length > 0)
147
+ parts.push(`${failed.length} failed`);
148
+ lines.push(parts.join(', '));
149
+ return lines.join('\n');
150
+ }
151
+ //# sourceMappingURL=gates-graceful-degradation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gates-graceful-degradation.js","sourceRoot":"","sources":["../src/gates-graceful-degradation.ts"],"names":[],"mappings":"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"}
package/dist/gates.js CHANGED
@@ -71,6 +71,8 @@ import { runLaneHealthCheck } from './lane-health.js';
71
71
  // WU-1315: Onboarding smoke test
72
72
  import { runOnboardingSmokeTestGate } from './onboarding-smoke-test.js';
73
73
  import { BRANCHES, PACKAGES, PKG_MANAGER, ESLINT_FLAGS, ESLINT_COMMANDS, ESLINT_DEFAULTS, SCRIPTS, CACHE_STRATEGIES, DIRECTORIES, GATE_NAMES, GATE_COMMANDS, CLI_MODES, EXIT_CODES, FILE_SYSTEM, PRETTIER_ARGS, PRETTIER_FLAGS, } from '@lumenflow/core/dist/wu-constants.js';
74
+ // WU-1520: Gates graceful degradation for missing optional scripts
75
+ import { checkScriptExists, buildMissingScriptWarning, loadPackageJsonScripts, resolveGateAction, formatGateSummary, SKIPPABLE_GATE_SCRIPTS, } from './gates-graceful-degradation.js';
74
76
  /**
75
77
  * WU-1087: Gates-specific option definitions for createWUParser.
76
78
  * Exported for testing and consistency with other CLI commands.
@@ -107,6 +109,12 @@ export const GATES_OPTIONS = {
107
109
  flags: '--verbose',
108
110
  description: 'Stream output in agent mode instead of logging to file',
109
111
  },
112
+ // WU-1520: --strict flag makes missing scripts a hard failure for CI
113
+ strict: {
114
+ name: 'strict',
115
+ flags: '--strict',
116
+ description: 'Fail on missing gate scripts instead of skipping (for CI enforcement)',
117
+ },
110
118
  };
111
119
  /**
112
120
  * WU-1087: Parse gates options using createWUParser for consistency.
@@ -142,6 +150,7 @@ export function parseGatesOptions() {
142
150
  fullCoverage: opts.fullCoverage,
143
151
  coverageMode: opts.coverageMode ?? 'block',
144
152
  verbose: opts.verbose,
153
+ strict: opts.strict,
145
154
  };
146
155
  }
147
156
  finally {
@@ -1114,6 +1123,11 @@ async function executeGates(opts) {
1114
1123
  const laneHealthMode = loadLaneHealthConfig(process.cwd());
1115
1124
  // WU-1356: Resolve configured gates commands for test execution
1116
1125
  const configuredGatesCommands = resolveGatesCommands(process.cwd());
1126
+ // WU-1520: Strict mode and script existence checking for graceful degradation
1127
+ const isStrict = opts.strict || false;
1128
+ const packageJsonScripts = loadPackageJsonScripts(process.cwd());
1129
+ // WU-1520: Track gate results for summary
1130
+ const gateResults = [];
1117
1131
  if (useAgentMode) {
1118
1132
  console.log(`🧾 gates (agent mode): output -> ${agentLog.logPath} (use --verbose for streaming)\n`);
1119
1133
  }
@@ -1153,12 +1167,17 @@ async function executeGates(opts) {
1153
1167
  }
1154
1168
  // Determine which gates to run
1155
1169
  // WU-2252: Invariants gate runs FIRST and is included in both docs-only and regular modes
1170
+ // WU-1520: scriptName field maps gates to their package.json script for existence checking
1156
1171
  const gates = effectiveDocsOnly
1157
1172
  ? [
1158
1173
  // WU-2252: Invariants check runs first (non-bypassable)
1159
1174
  { name: GATE_NAMES.INVARIANTS, cmd: GATE_COMMANDS.INVARIANTS },
1160
- { name: GATE_NAMES.FORMAT_CHECK, run: runFormatCheckGate },
1161
- { name: GATE_NAMES.SPEC_LINTER, run: runSpecLinterGate },
1175
+ {
1176
+ name: GATE_NAMES.FORMAT_CHECK,
1177
+ run: runFormatCheckGate,
1178
+ scriptName: SCRIPTS.FORMAT_CHECK,
1179
+ },
1180
+ { name: GATE_NAMES.SPEC_LINTER, run: runSpecLinterGate, scriptName: SCRIPTS.SPEC_LINTER },
1162
1181
  // WU-1467: prompts:lint removed -- was a stub (exit 0), not an authoritative gate
1163
1182
  { name: GATE_NAMES.BACKLOG_SYNC, run: runBacklogSyncGate },
1164
1183
  // WU-2315: System map validation (warn-only until orphan docs are indexed)
@@ -1201,13 +1220,22 @@ async function executeGates(opts) {
1201
1220
  : [
1202
1221
  // WU-2252: Invariants check runs first (non-bypassable)
1203
1222
  { name: GATE_NAMES.INVARIANTS, cmd: GATE_COMMANDS.INVARIANTS },
1204
- { name: GATE_NAMES.FORMAT_CHECK, run: runFormatCheckGate },
1223
+ {
1224
+ name: GATE_NAMES.FORMAT_CHECK,
1225
+ run: runFormatCheckGate,
1226
+ scriptName: SCRIPTS.FORMAT_CHECK,
1227
+ },
1205
1228
  {
1206
1229
  name: GATE_NAMES.LINT,
1207
1230
  cmd: isFullLint ? pnpmCmd(SCRIPTS.LINT) : GATE_COMMANDS.INCREMENTAL,
1231
+ scriptName: SCRIPTS.LINT,
1232
+ },
1233
+ {
1234
+ name: GATE_NAMES.TYPECHECK,
1235
+ cmd: pnpmCmd(SCRIPTS.TYPECHECK),
1236
+ scriptName: SCRIPTS.TYPECHECK,
1208
1237
  },
1209
- { name: GATE_NAMES.TYPECHECK, cmd: pnpmCmd(SCRIPTS.TYPECHECK) },
1210
- { name: GATE_NAMES.SPEC_LINTER, run: runSpecLinterGate },
1238
+ { name: GATE_NAMES.SPEC_LINTER, run: runSpecLinterGate, scriptName: SCRIPTS.SPEC_LINTER },
1211
1239
  // WU-1467: prompts:lint removed -- was a stub (exit 0), not an authoritative gate
1212
1240
  { name: GATE_NAMES.BACKLOG_SYNC, run: runBacklogSyncGate },
1213
1241
  { name: GATE_NAMES.SUPABASE_DOCS_LINTER, run: runSupabaseDocsGate },
@@ -1278,6 +1306,32 @@ async function executeGates(opts) {
1278
1306
  let lastFormatCheckFiles = null;
1279
1307
  for (const gate of gates) {
1280
1308
  let result;
1309
+ // WU-1520: Check if the gate's underlying script exists in package.json
1310
+ const gateScriptName = gate.scriptName ?? null;
1311
+ const gateAction = resolveGateAction(gate.name, gateScriptName, packageJsonScripts, isStrict);
1312
+ if (gateAction === 'skip') {
1313
+ const logLine = makeGateLogger({ agentLog, useAgentMode });
1314
+ const warningMsg = buildMissingScriptWarning(gateScriptName);
1315
+ logLine(`\n${warningMsg}\n`);
1316
+ gateResults.push({
1317
+ name: gate.name,
1318
+ status: 'skipped',
1319
+ durationMs: 0,
1320
+ reason: 'script not found in package.json',
1321
+ });
1322
+ continue;
1323
+ }
1324
+ if (gateAction === 'fail') {
1325
+ const logLine = makeGateLogger({ agentLog, useAgentMode });
1326
+ logLine(`\nāŒ "${gateScriptName}" script not found in package.json (--strict mode)\n`);
1327
+ gateResults.push({
1328
+ name: gate.name,
1329
+ status: 'failed',
1330
+ durationMs: 0,
1331
+ reason: 'script not found in package.json (strict mode)',
1332
+ });
1333
+ die(`${gate.name} failed: missing script "${gateScriptName}" in package.json (--strict mode requires all gate scripts)`);
1334
+ }
1281
1335
  if (gate.run) {
1282
1336
  result = await gate.run({ agentLog, useAgentMode });
1283
1337
  if (gate.name === GATE_NAMES.FORMAT_CHECK) {
@@ -1328,6 +1382,13 @@ async function executeGates(opts) {
1328
1382
  else {
1329
1383
  writeSync(agentLog.logFd, `\n${msg}\n\n`);
1330
1384
  }
1385
+ // WU-1520: Track skipped coverage gate in summary
1386
+ gateResults.push({
1387
+ name: gate.name,
1388
+ status: 'skipped',
1389
+ durationMs: 0,
1390
+ reason: 'changed tests - coverage is partial',
1391
+ });
1331
1392
  continue;
1332
1393
  }
1333
1394
  // WU-1433: Special handling for coverage gate
@@ -1382,11 +1443,26 @@ async function executeGates(opts) {
1382
1443
  else {
1383
1444
  writeSync(agentLog.logFd, `\n${warnMsg}\n\n`);
1384
1445
  }
1446
+ // WU-1520: Track warned gates in summary
1447
+ gateResults.push({
1448
+ name: gate.name,
1449
+ status: 'warned',
1450
+ durationMs: result.duration,
1451
+ });
1385
1452
  continue;
1386
1453
  }
1387
1454
  if (gate.name === GATE_NAMES.FORMAT_CHECK) {
1388
1455
  emitFormatCheckGuidance({ agentLog, useAgentMode, files: lastFormatCheckFiles });
1389
1456
  }
1457
+ // WU-1520: Track failed gate before dying
1458
+ gateResults.push({
1459
+ name: gate.name,
1460
+ status: 'failed',
1461
+ durationMs: result.duration,
1462
+ });
1463
+ // WU-1520: Print summary before failing
1464
+ const logLine = makeGateLogger({ agentLog, useAgentMode });
1465
+ logLine(`\n${formatGateSummary(gateResults)}\n`);
1390
1466
  if (useAgentMode) {
1391
1467
  const tail = readLogTail(agentLog.logPath);
1392
1468
  console.error(`\nāŒ ${gate.name} failed (agent mode). Log: ${agentLog.logPath}\n`);
@@ -1396,11 +1472,20 @@ async function executeGates(opts) {
1396
1472
  }
1397
1473
  die(`${gate.name} failed`);
1398
1474
  }
1475
+ // WU-1520: Track passed gate
1476
+ gateResults.push({
1477
+ name: gate.name,
1478
+ status: 'passed',
1479
+ durationMs: result.duration,
1480
+ });
1399
1481
  }
1400
1482
  // WU-2064: Create/update gates-latest.log symlink for easy agent access
1401
1483
  if (agentLog) {
1402
1484
  updateGatesLatestSymlink({ logPath: agentLog.logPath, cwd: process.cwd(), env: process.env });
1403
1485
  }
1486
+ // WU-1520: Print gate summary showing passed/skipped/failed/warned
1487
+ const summaryLogLine = makeGateLogger({ agentLog, useAgentMode });
1488
+ summaryLogLine(`\n${formatGateSummary(gateResults)}`);
1404
1489
  if (!useAgentMode) {
1405
1490
  console.log('\nāœ… All gates passed!\n');
1406
1491
  }