@lumenflow/cli 5.2.3 → 5.2.5

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.
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  // Copyright (c) 2026 Hellmai Ltd
3
3
  // SPDX-License-Identifier: AGPL-3.0-only
4
- import { existsSync, readFileSync } from 'node:fs';
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
6
  import { Command } from 'commander';
7
+ import { CONTEXT_VALIDATION, resolveLocation } from '@lumenflow/core';
7
8
  import { createHttpControlPlaneSyncPort, parseWorkspaceControlPlaneConfig, } from '@lumenflow/control-plane-sdk';
8
9
  import { CONFIG_FILES } from '@lumenflow/core/wu-constants';
9
10
  import { parseYAML } from '@lumenflow/core/wu-yaml';
@@ -12,6 +13,20 @@ const LOG_PREFIX = '[approval:request]';
12
13
  const WORKSPACE_PATH = CONFIG_FILES.WORKSPACE_CONFIG;
13
14
  const DEFAULT_REQUESTER_TYPE = 'agent';
14
15
  const REQUESTER_TYPE_VALUES = new Set(['agent', 'user']);
16
+ const HUMAN_ESCALATION_RESERVED_CONTEXT_KEYS = new Set([
17
+ 'triggers',
18
+ 'summary',
19
+ 'resolver_command',
20
+ 'skip_gates_reason',
21
+ 'fix_wu',
22
+ ]);
23
+ /**
24
+ * WU-2874: Approval type constant for worker-agent human-escalation handoff.
25
+ * Workers emit this instead of prose-dumping wu:escalate/wu:done commands.
26
+ */
27
+ export const HUMAN_ESCALATION_APPROVAL_TYPE = 'human-escalation';
28
+ /** WU-2874: Local sidecar directory for structured escalation context. */
29
+ export const ESCALATION_SIDECAR_DIR = join('.lumenflow', 'state', 'approvals');
15
30
  function asRecord(value) {
16
31
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
17
32
  throw new Error('Expected a JSON object payload');
@@ -27,18 +42,28 @@ function parseJsonRecord(value, label) {
27
42
  throw new Error(`Invalid ${label}: ${reason}`, { cause: error });
28
43
  }
29
44
  }
45
+ function parseHumanEscalationExtraContext(value) {
46
+ const parsed = parseJsonRecord(value, '--context');
47
+ return Object.fromEntries(Object.entries(parsed).filter(([key]) => !HUMAN_ESCALATION_RESERVED_CONTEXT_KEYS.has(key)));
48
+ }
30
49
  function parseOptions(argv = process.argv) {
31
50
  const program = new Command()
32
51
  .name('approval-request')
33
52
  .description('Request an approval from the configured control-plane')
34
53
  .requiredOption('--type <type>', 'Approval type (for example: wu_assignment)')
35
- .requiredOption('--subject <json>', 'JSON object describing the approval subject')
54
+ .option('--subject <json>', 'JSON object describing the approval subject')
36
55
  .option('--context <json>', 'Optional JSON object with additional context')
37
56
  .option('--requester-id <id>', 'Requester identifier')
38
57
  .option('--requester-type <type>', 'Requester type: agent or user', DEFAULT_REQUESTER_TYPE)
39
58
  .option('--expires-at <iso>', 'Optional expiry timestamp (ISO-8601)')
40
59
  .option('--workspace-id <id>', 'Override workspace_id from workspace.yaml')
41
60
  .option('--json', 'Output JSON response', false)
61
+ // WU-2874: Structured flags for --type human-escalation
62
+ .option('--wu <id>', 'WU id (human-escalation: required)')
63
+ .option('--triggers <csv>', 'Comma-separated escalation_triggers that fired (human-escalation)')
64
+ .option('--summary <text>', 'Summary of worker changes (human-escalation: required)')
65
+ .option('--skip-gates-reason <text>', 'If approval should chain --skip-gates, the reason')
66
+ .option('--fix-wu <id>', 'Paired fix-WU id when skip-gates-reason is set')
42
67
  .exitOverride();
43
68
  try {
44
69
  program.parse(argv);
@@ -65,7 +90,85 @@ function parseOptions(argv = process.argv) {
65
90
  expiresAt: parsed.expiresAt?.trim(),
66
91
  workspaceId: parsed.workspaceId?.trim(),
67
92
  json: parsed.json ?? false,
93
+ wu: parsed.wu?.trim(),
94
+ triggers: parsed.triggers,
95
+ summary: parsed.summary,
96
+ skipGatesReason: parsed.skipGatesReason,
97
+ fixWu: parsed.fixWu?.trim(),
98
+ };
99
+ }
100
+ /**
101
+ * WU-2874: Validate + assemble structured subject/context from human-escalation flags.
102
+ * Keeps the original free-form --subject/--context path untouched for other types.
103
+ */
104
+ export function buildHumanEscalationPayload(options) {
105
+ if (!options.wu) {
106
+ throw new Error('--wu is required when --type human-escalation');
107
+ }
108
+ if (!options.summary) {
109
+ throw new Error('--summary is required when --type human-escalation');
110
+ }
111
+ const triggers = (options.triggers ?? '')
112
+ .split(',')
113
+ .map((t) => t.trim())
114
+ .filter((t) => t.length > 0);
115
+ const payload = {
116
+ wu: options.wu,
117
+ triggers,
118
+ summary: options.summary,
68
119
  };
120
+ if (options.skipGatesReason)
121
+ payload.skipGatesReason = options.skipGatesReason;
122
+ if (options.fixWu)
123
+ payload.fixWu = options.fixWu;
124
+ return payload;
125
+ }
126
+ /**
127
+ * Resolve where a human-escalation sidecar should live. Worker agents request
128
+ * approvals from their claimed worktree, but reviewers run approval:review from
129
+ * the main checkout so the sidecar must be written where the reviewer can read
130
+ * it. Unknown/non-git roots fall back to the caller's root for unit tests and
131
+ * non-worktree control-plane use.
132
+ */
133
+ export async function resolveEscalationSidecarRoot(rootDir = process.cwd(), location) {
134
+ const resolvedLocation = location ?? (await resolveLocation(rootDir));
135
+ const { LOCATION_TYPES } = CONTEXT_VALIDATION;
136
+ if (resolvedLocation.type === LOCATION_TYPES.WORKTREE) {
137
+ return resolvedLocation.mainCheckout;
138
+ }
139
+ if (resolvedLocation.type === LOCATION_TYPES.MAIN ||
140
+ resolvedLocation.type === LOCATION_TYPES.DETACHED) {
141
+ return resolvedLocation.gitRoot;
142
+ }
143
+ return rootDir;
144
+ }
145
+ /**
146
+ * WU-2874: Write sidecar file so approval:review --approve can chain the
147
+ * resolver without a list/fetch roundtrip. Idempotent — overwrites on re-run.
148
+ */
149
+ export function writeEscalationSidecar(approvalId, payload, rootDir = process.cwd()) {
150
+ const dir = join(rootDir, ESCALATION_SIDECAR_DIR);
151
+ if (!existsSync(dir)) {
152
+ mkdirSync(dir, { recursive: true });
153
+ }
154
+ const path = join(dir, `${approvalId}.json`);
155
+ writeFileSync(path, JSON.stringify(payload, null, 2) + '\n', 'utf8');
156
+ return path;
157
+ }
158
+ /**
159
+ * WU-2874: Read sidecar written by writeEscalationSidecar. Returns undefined
160
+ * when missing (e.g. approval was not a human-escalation type).
161
+ */
162
+ export function readEscalationSidecar(approvalId, rootDir = process.cwd()) {
163
+ const path = join(rootDir, ESCALATION_SIDECAR_DIR, `${approvalId}.json`);
164
+ if (!existsSync(path))
165
+ return undefined;
166
+ const raw = readFileSync(path, 'utf8');
167
+ return JSON.parse(raw);
168
+ }
169
+ /** WU-2874: Return the absolute sidecar path for a given approval id. */
170
+ export function getEscalationSidecarPath(approvalId, rootDir = process.cwd()) {
171
+ return join(rootDir, ESCALATION_SIDECAR_DIR, `${approvalId}.json`);
69
172
  }
70
173
  function resolveApprovalContext(workspaceRoot, environment, workspaceIdOverride) {
71
174
  const workspaceFilePath = join(workspaceRoot, WORKSPACE_PATH);
@@ -89,9 +192,39 @@ function formatResultLine(result) {
89
192
  return `${LOG_PREFIX} Created ${result.approval_id} (${result.status}) for type ${result.type}`;
90
193
  }
91
194
  export async function runApprovalRequest(options, input = {}) {
92
- const subject = parseJsonRecord(options.subject, '--subject');
93
- const context = options.context ? parseJsonRecord(options.context, '--context') : undefined;
94
- const { workspaceId, syncPort } = resolveApprovalContext(input.workspaceRoot ?? process.cwd(), input.environment ?? process.env, options.workspaceId);
195
+ const rootDir = input.workspaceRoot ?? process.cwd();
196
+ // WU-2874: human-escalation synthesises subject+context from structured flags
197
+ // so the worker does not have to hand-craft JSON. The existing free-form
198
+ // API (--subject/--context) remains untouched for every other type.
199
+ let subject;
200
+ let context;
201
+ let escalationPayload;
202
+ if (options.type === HUMAN_ESCALATION_APPROVAL_TYPE) {
203
+ escalationPayload = buildHumanEscalationPayload(options);
204
+ subject = { wu: escalationPayload.wu };
205
+ const structuredContext = {
206
+ triggers: escalationPayload.triggers,
207
+ summary: escalationPayload.summary,
208
+ ...(escalationPayload.skipGatesReason
209
+ ? { skip_gates_reason: escalationPayload.skipGatesReason }
210
+ : {}),
211
+ ...(escalationPayload.fixWu ? { fix_wu: escalationPayload.fixWu } : {}),
212
+ };
213
+ context = structuredContext;
214
+ // Free-form --context lets operators add trace metadata, but structured
215
+ // escalation fields remain authoritative.
216
+ if (options.context) {
217
+ context = { ...parseHumanEscalationExtraContext(options.context), ...structuredContext };
218
+ }
219
+ }
220
+ else {
221
+ if (!options.subject) {
222
+ throw new Error('--subject is required (unless --type human-escalation)');
223
+ }
224
+ subject = parseJsonRecord(options.subject, '--subject');
225
+ context = options.context ? parseJsonRecord(options.context, '--context') : undefined;
226
+ }
227
+ const { workspaceId, syncPort } = resolveApprovalContext(rootDir, input.environment ?? process.env, options.workspaceId);
95
228
  if (!syncPort.requestApproval) {
96
229
  throw new Error('Control-plane adapter does not support requestApproval');
97
230
  }
@@ -104,16 +237,29 @@ export async function runApprovalRequest(options, input = {}) {
104
237
  ...(options.expiresAt ? { expires_at: options.expiresAt } : {}),
105
238
  ...(context ? { context } : {}),
106
239
  };
107
- return syncPort.requestApproval(payload);
240
+ const record = await syncPort.requestApproval(payload);
241
+ // WU-2874: write sidecar after the control-plane acknowledges so the
242
+ // reviewer's approval:review --approve path can chain the resolver locally
243
+ // without a lookup. Sidecar-write is side-effect only; we never auto-run
244
+ // wu:escalate/wu:done on the worker's behalf (Acceptance criterion: no
245
+ // auto-run on creation).
246
+ if (escalationPayload) {
247
+ const sidecarRoot = await resolveEscalationSidecarRoot(rootDir);
248
+ writeEscalationSidecar(record.approval_id, escalationPayload, sidecarRoot);
249
+ }
250
+ return record;
108
251
  }
109
252
  export async function main(argv = process.argv) {
110
253
  const options = parseOptions(argv);
111
- const result = (await runApprovalRequest(options));
254
+ const result = await runApprovalRequest(options);
112
255
  if (options.json) {
113
256
  console.log(JSON.stringify(result, null, 2));
114
257
  return;
115
258
  }
116
259
  console.log(formatResultLine(result));
260
+ if (options.type === HUMAN_ESCALATION_APPROVAL_TYPE) {
261
+ console.log(`${LOG_PREFIX} Reviewer resolves via: pnpm approval:review --approve ${result.approval_id}`);
262
+ }
117
263
  }
118
264
  if (import.meta.main) {
119
265
  void runCLI(main);
@@ -1 +1 @@
1
- {"version":3,"file":"approval-request.js","sourceRoot":"","sources":["../src/approval-request.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"approval-request.js","sourceRoot":"","sources":["../src/approval-request.ts"],"names":[],"mappings":"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"}
@@ -1,29 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
  // Copyright (c) 2026 Hellmai Ltd
3
3
  // SPDX-License-Identifier: AGPL-3.0-only
4
- import { existsSync, readFileSync } from 'node:fs';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { existsSync, readFileSync, unlinkSync } from 'node:fs';
5
6
  import { join } from 'node:path';
6
7
  import { Command } from 'commander';
8
+ import { CONTEXT_VALIDATION, resolveLocation } from '@lumenflow/core';
7
9
  import { createHttpControlPlaneSyncPort, parseWorkspaceControlPlaneConfig, } from '@lumenflow/control-plane-sdk';
8
10
  import { CONFIG_FILES } from '@lumenflow/core/wu-constants';
11
+ import { getGitForCwd } from '@lumenflow/core/git-adapter';
9
12
  import { parseYAML } from '@lumenflow/core/wu-yaml';
10
13
  import { runCLI } from './cli-entry-point.js';
14
+ import { HUMAN_ESCALATION_APPROVAL_TYPE, getEscalationSidecarPath, readEscalationSidecar, } from './approval-request.js';
11
15
  const LOG_PREFIX = '[approval:review]';
12
16
  const WORKSPACE_PATH = CONFIG_FILES.WORKSPACE_CONFIG;
13
17
  const DEFAULT_REVIEWER_TYPE = 'user';
14
18
  const REVIEWER_TYPE_VALUES = new Set(['agent', 'user']);
15
19
  const DECISION_VALUES = new Set(['approved', 'rejected', 'expired']);
20
+ const HUMAN_ESCALATION_LOOKUP_LIMIT = 100;
16
21
  function parseOptions(argv = process.argv) {
17
22
  const program = new Command()
18
23
  .name('approval-review')
19
24
  .description('Resolve a pending approval in the configured control-plane')
20
- .requiredOption('--id <approvalId>', 'Approval identifier')
21
- .requiredOption('--decision <decision>', 'Decision: approved, rejected, or expired')
25
+ // WU-2874: `--id` accepted as option, positional, or via --approve/--deny <id>
26
+ .option('--id <approvalId>', 'Approval identifier')
27
+ .option('--decision <decision>', 'Decision: approved, rejected, or expired')
28
+ .option('--approve [approvalId]', 'Convenience for --decision approved')
29
+ .option('--deny [approvalId]', 'Convenience for --decision rejected')
22
30
  .option('--reason <reason>', 'Optional decision reason')
23
31
  .option('--reviewer-id <id>', 'Reviewer identifier')
24
32
  .option('--reviewer-type <type>', 'Reviewer type: agent or user', DEFAULT_REVIEWER_TYPE)
33
+ .option('--reviewer <email>', 'Reviewer email (used as --resolver for human-escalation chains); defaults to git user.email')
25
34
  .option('--workspace-id <id>', 'Override workspace_id from workspace.yaml')
26
35
  .option('--json', 'Output JSON response', false)
36
+ .argument('[approvalId]', 'Approval identifier (positional alternative to --id)')
27
37
  .exitOverride();
28
38
  try {
29
39
  program.parse(argv);
@@ -37,20 +47,42 @@ function parseOptions(argv = process.argv) {
37
47
  throw error;
38
48
  }
39
49
  const parsed = program.opts();
40
- const decision = parsed.decision.trim();
50
+ const positional = program.args[0];
51
+ // WU-2874: derive approval id from --id | --approve <id> | --deny <id> | positional
52
+ const approvalIdRaw = parsed.id ??
53
+ (typeof parsed.approve === 'string' ? parsed.approve : undefined) ??
54
+ (typeof parsed.deny === 'string' ? parsed.deny : undefined) ??
55
+ positional;
56
+ if (!approvalIdRaw) {
57
+ throw new Error('Missing approval id. Pass --id <approvalId> or --approve <approvalId>.');
58
+ }
59
+ // WU-2874: derive decision from --decision or the convenience flags.
60
+ let decisionRaw = parsed.decision;
61
+ if (!decisionRaw && parsed.approve !== undefined)
62
+ decisionRaw = 'approved';
63
+ if (!decisionRaw && parsed.deny !== undefined)
64
+ decisionRaw = 'rejected';
65
+ if (parsed.approve !== undefined && parsed.deny !== undefined) {
66
+ throw new Error('--approve and --deny are mutually exclusive');
67
+ }
68
+ if (!decisionRaw) {
69
+ throw new Error('Missing decision. Use --approve, --deny, or --decision <value>.');
70
+ }
71
+ const decision = decisionRaw.trim();
41
72
  if (!DECISION_VALUES.has(decision)) {
42
- throw new Error(`Invalid --decision: ${parsed.decision}`);
73
+ throw new Error(`Invalid --decision: ${decisionRaw}`);
43
74
  }
44
75
  const reviewerTypeRaw = (parsed.reviewerType ?? DEFAULT_REVIEWER_TYPE).trim();
45
76
  if (!REVIEWER_TYPE_VALUES.has(reviewerTypeRaw)) {
46
77
  throw new Error(`Invalid --reviewer-type: ${reviewerTypeRaw}`);
47
78
  }
48
79
  return {
49
- approvalId: parsed.id.trim(),
80
+ approvalId: approvalIdRaw.trim(),
50
81
  decision,
51
82
  reason: parsed.reason?.trim(),
52
83
  reviewerId: parsed.reviewerId?.trim(),
53
84
  reviewerType: reviewerTypeRaw,
85
+ reviewer: parsed.reviewer?.trim(),
54
86
  workspaceId: parsed.workspaceId?.trim(),
55
87
  json: parsed.json ?? false,
56
88
  };
@@ -77,11 +109,192 @@ function formatResultLine(result) {
77
109
  const reasonSuffix = result.decision_reason ? ` (${result.decision_reason})` : '';
78
110
  return `${LOG_PREFIX} Updated ${result.approval_id} -> ${result.status}${reasonSuffix}`;
79
111
  }
112
+ const defaultRunner = (command, args, options) => {
113
+ const result = spawnSync(command, args, {
114
+ cwd: options.cwd,
115
+ encoding: 'utf8',
116
+ stdio: ['ignore', 'inherit', 'inherit'],
117
+ });
118
+ return {
119
+ status: result.status,
120
+ stdout: typeof result.stdout === 'string' ? result.stdout : undefined,
121
+ stderr: typeof result.stderr === 'string' ? result.stderr : undefined,
122
+ };
123
+ };
124
+ export async function resolveEscalationReviewRoot(rootDir = process.cwd(), location) {
125
+ const resolvedLocation = location ?? (await resolveLocation(rootDir));
126
+ const { LOCATION_TYPES } = CONTEXT_VALIDATION;
127
+ if (resolvedLocation.type === LOCATION_TYPES.WORKTREE) {
128
+ return resolvedLocation.mainCheckout;
129
+ }
130
+ if (resolvedLocation.type === LOCATION_TYPES.MAIN ||
131
+ resolvedLocation.type === LOCATION_TYPES.DETACHED) {
132
+ return resolvedLocation.gitRoot;
133
+ }
134
+ return rootDir;
135
+ }
136
+ /**
137
+ * WU-2874: Chain wu:escalate --resolve + wu:done for an approved human-escalation.
138
+ *
139
+ * Pure-ish: accepts a runner so tests can assert call ordering without spawning
140
+ * real processes. Stops at the first non-zero exit and surfaces the failing step.
141
+ * The approval record is only marked resolved when every step returns status 0.
142
+ */
143
+ export function runEscalationChain(input) {
144
+ const runner = input.runner ?? defaultRunner;
145
+ const cwd = input.cwd;
146
+ const steps = [];
147
+ const escalateArgs = [
148
+ 'wu:escalate',
149
+ '--resolve',
150
+ '--id',
151
+ input.payload.wu,
152
+ '--resolver',
153
+ input.reviewerEmail,
154
+ ];
155
+ const escalate = runner('pnpm', escalateArgs, { cwd });
156
+ const escalateStep = {
157
+ command: 'pnpm',
158
+ args: escalateArgs,
159
+ status: escalate.status,
160
+ };
161
+ steps.push(escalateStep);
162
+ if (escalate.status !== 0) {
163
+ return { steps, success: false, failedStep: escalateStep };
164
+ }
165
+ const doneArgs = ['wu:done', '--id', input.payload.wu];
166
+ if (input.payload.skipGatesReason) {
167
+ doneArgs.push('--skip-gates', '--reason', input.payload.skipGatesReason);
168
+ if (input.payload.fixWu) {
169
+ doneArgs.push('--fix-wu', input.payload.fixWu);
170
+ }
171
+ }
172
+ const done = runner('pnpm', doneArgs, { cwd });
173
+ const doneStep = {
174
+ command: 'pnpm',
175
+ args: doneArgs,
176
+ status: done.status,
177
+ };
178
+ steps.push(doneStep);
179
+ if (done.status !== 0) {
180
+ return { steps, success: false, failedStep: doneStep };
181
+ }
182
+ return { steps, success: true };
183
+ }
184
+ /**
185
+ * WU-2874: Resolve the reviewer's email. Explicit --reviewer wins; otherwise
186
+ * fall back to git user.email so the approver identity is never the approval
187
+ * creator (per acceptance criteria).
188
+ */
189
+ async function resolveReviewerEmail(override) {
190
+ if (override)
191
+ return override;
192
+ const email = await getGitForCwd().getConfigValue('user.email');
193
+ if (!email) {
194
+ throw new Error('Cannot determine reviewer email. Pass --reviewer <email> or configure git user.email.');
195
+ }
196
+ return email;
197
+ }
198
+ function missingHumanEscalationContextMessage(approvalId, detail) {
199
+ return (`${LOG_PREFIX} Missing human-escalation context for approval ${approvalId}: ${detail}. ` +
200
+ 'Re-run pnpm approval:request --type human-escalation from the claimed worktree, ' +
201
+ 'then retry approval:review.');
202
+ }
203
+ function readRequiredString(value, fieldName, approvalId) {
204
+ if (typeof value === 'string' && value.trim().length > 0) {
205
+ return value;
206
+ }
207
+ throw new Error(missingHumanEscalationContextMessage(approvalId, `expected ${fieldName}`));
208
+ }
209
+ function readOptionalString(value) {
210
+ return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
211
+ }
212
+ function readTriggers(value, approvalId) {
213
+ if (Array.isArray(value) && value.every((trigger) => typeof trigger === 'string')) {
214
+ return value;
215
+ }
216
+ throw new Error(missingHumanEscalationContextMessage(approvalId, 'expected context.triggers to be an array of strings'));
217
+ }
218
+ export function humanEscalationPayloadFromApprovalRecord(record) {
219
+ if (record.type !== HUMAN_ESCALATION_APPROVAL_TYPE) {
220
+ return undefined;
221
+ }
222
+ if (!record.context) {
223
+ throw new Error(missingHumanEscalationContextMessage(record.approval_id, 'control-plane record has no context'));
224
+ }
225
+ const payload = {
226
+ wu: readRequiredString(record.subject.wu, 'subject.wu', record.approval_id),
227
+ triggers: readTriggers(record.context.triggers, record.approval_id),
228
+ summary: readRequiredString(record.context.summary, 'context.summary', record.approval_id),
229
+ };
230
+ const skipGatesReason = readOptionalString(record.context.skip_gates_reason);
231
+ if (skipGatesReason)
232
+ payload.skipGatesReason = skipGatesReason;
233
+ const fixWu = readOptionalString(record.context.fix_wu);
234
+ if (fixWu)
235
+ payload.fixWu = fixWu;
236
+ return payload;
237
+ }
238
+ async function findPendingHumanEscalationPayload(input) {
239
+ if (!input.syncPort.listApprovals) {
240
+ return undefined;
241
+ }
242
+ const result = await input.syncPort.listApprovals({
243
+ workspace_id: input.workspaceId,
244
+ status: 'pending',
245
+ type: HUMAN_ESCALATION_APPROVAL_TYPE,
246
+ limit: HUMAN_ESCALATION_LOOKUP_LIMIT,
247
+ });
248
+ const matchingRecord = result.approvals.find((approval) => approval.approval_id === input.approvalId);
249
+ return matchingRecord ? humanEscalationPayloadFromApprovalRecord(matchingRecord) : undefined;
250
+ }
80
251
  export async function runApprovalReview(options, input = {}) {
81
- const { workspaceId, syncPort } = resolveApprovalContext(input.workspaceRoot ?? process.cwd(), input.environment ?? process.env, options.workspaceId);
252
+ const rootDir = input.workspaceRoot ?? process.cwd();
253
+ const reviewRoot = await resolveEscalationReviewRoot(rootDir);
254
+ const { workspaceId, syncPort } = resolveApprovalContext(rootDir, input.environment ?? process.env, options.workspaceId);
82
255
  if (!syncPort.resolveApproval) {
83
256
  throw new Error('Control-plane adapter does not support resolveApproval');
84
257
  }
258
+ // WU-2874: Human-escalation approve-chain runs BEFORE marking the approval
259
+ // resolved. If either wu:escalate or wu:done fails, the approval stays
260
+ // pending so the reviewer can re-approve after fixes (replayable).
261
+ const sidecar = options.decision === 'approved'
262
+ ? readEscalationSidecar(options.approvalId, reviewRoot)
263
+ : undefined;
264
+ const escalationPayload = sidecar ??
265
+ (options.decision === 'approved'
266
+ ? await findPendingHumanEscalationPayload({
267
+ approvalId: options.approvalId,
268
+ workspaceId,
269
+ syncPort,
270
+ })
271
+ : undefined);
272
+ if (escalationPayload) {
273
+ const reviewerEmail = await resolveReviewerEmail(options.reviewer);
274
+ const chainResult = runEscalationChain({
275
+ approvalId: options.approvalId,
276
+ payload: escalationPayload,
277
+ reviewerEmail,
278
+ runner: input.runner,
279
+ cwd: reviewRoot,
280
+ });
281
+ if (!chainResult.success) {
282
+ const step = chainResult.failedStep;
283
+ const joined = step ? `${step.command} ${step.args.join(' ')}` : 'unknown step';
284
+ throw new Error(`${LOG_PREFIX} Resolver chain failed at: ${joined} (exit ${step?.status ?? 'null'}). ` +
285
+ `Approval ${options.approvalId} left pending — fix the underlying issue and re-run ` +
286
+ `pnpm approval:review --approve ${options.approvalId}.`);
287
+ }
288
+ // Successful chain → remove the sidecar so re-approval is a no-op.
289
+ try {
290
+ const sidecarPath = getEscalationSidecarPath(options.approvalId, reviewRoot);
291
+ if (existsSync(sidecarPath))
292
+ unlinkSync(sidecarPath);
293
+ }
294
+ catch {
295
+ // Non-fatal — resolver already succeeded.
296
+ }
297
+ }
85
298
  const payload = {
86
299
  workspace_id: workspaceId,
87
300
  approval_id: options.approvalId,
@@ -92,9 +305,11 @@ export async function runApprovalReview(options, input = {}) {
92
305
  };
93
306
  return syncPort.resolveApproval(payload);
94
307
  }
308
+ /** WU-2874: Exported for integration tests that assert on approval type guards. */
309
+ export { HUMAN_ESCALATION_APPROVAL_TYPE };
95
310
  export async function main(argv = process.argv) {
96
311
  const options = parseOptions(argv);
97
- const result = (await runApprovalReview(options));
312
+ const result = await runApprovalReview(options);
98
313
  if (options.json) {
99
314
  console.log(JSON.stringify(result, null, 2));
100
315
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"approval-review.js","sourceRoot":"","sources":["../src/approval-review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"approval-review.js","sourceRoot":"","sources":["../src/approval-review.ts"],"names":[],"mappings":"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"}
@@ -39,7 +39,7 @@ import { generateScriptsFromManifest } from './public-manifest.js';
39
39
  import { loadTemplate, processTemplate, CORE_DOC_TEMPLATE_PATHS, buildCoreDocTokens, executeDocsSyncInDir, } from './docs-sync.js';
40
40
  // WU-2864: propagate the .gitattributes merge=union rule to consumer repos
41
41
  // on upgrade so rebase conflicts on append-only JSONL state files auto-resolve.
42
- import { scaffoldGitattributes } from './init-safety-scripts.js';
42
+ import { scaffoldGitattributes, scaffoldGitignore } from './init-safety-scripts.js';
43
43
  /** Log prefix for console output */
44
44
  const LOG_PREFIX = '[lumenflow:upgrade]';
45
45
  /** Operation name for micro-worktree */
@@ -813,6 +813,33 @@ async function _runUpgradeTransaction(addCommand, versionSpec, upgradeId) {
813
813
  if (docsSyncResult.created.length > 0) {
814
814
  console.log(`${LOG_PREFIX} Synced ${docsSyncResult.created.length} doc file(s)`);
815
815
  }
816
+ // WU-2876: Reconcile .gitignore against LUMENFLOW_RUNTIME_WRITE_PATHS +
817
+ // REQUIRED_GITIGNORE_EXCLUSIONS for existing consumers. Consumers that
818
+ // initialised before v5.2 never received the `.lumenflow/state/*.jsonl`
819
+ // glob and see runtime-write files (e.g. display-name-reservations.jsonl)
820
+ // show up as dirty in source control. scaffoldGitignore's merge path
821
+ // preserves user-owned lines outside the LumenFlow block byte-for-byte,
822
+ // and when no .gitignore exists it writes the full GITIGNORE_TEMPLATE.
823
+ // Called before scaffoldGitattributes to mirror init.ts ordering.
824
+ const giResult = {
825
+ created: [],
826
+ skipped: [],
827
+ merged: [],
828
+ warnings: [],
829
+ overwritten: [],
830
+ };
831
+ await scaffoldGitignore(worktreePath, { force: false, full: false }, giResult);
832
+ const giTouched = giResult.created.length > 0 ||
833
+ (giResult.merged?.length ?? 0) > 0 ||
834
+ (giResult.overwritten?.length ?? 0) > 0;
835
+ if (giTouched) {
836
+ const action = giResult.created.length > 0
837
+ ? 'Created'
838
+ : (giResult.overwritten?.length ?? 0) > 0
839
+ ? 'Rewrote'
840
+ : 'Updated managed block in';
841
+ console.log(`${LOG_PREFIX} ${action} .gitignore (WU-2876 runtime-write-path reconciliation)`);
842
+ }
816
843
  // WU-2864: Propagate the .gitattributes merge=union rule for append-only
817
844
  // JSONL state files. This is the sibling of syncCoreDocs — a managed
818
845
  // block that consumer repos need to receive on every upgrade so future
@@ -846,6 +873,7 @@ async function _runUpgradeTransaction(addCommand, versionSpec, upgradeId) {
846
873
  UPGRADE_MARKER_RELATIVE_PATH,
847
874
  ...docsSyncResult.allFiles,
848
875
  ...(gaTouched ? ['.gitattributes'] : []),
876
+ ...(giTouched ? ['.gitignore'] : []),
849
877
  ],
850
878
  };
851
879
  },