projscan 4.13.0 → 4.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.
@@ -93,6 +93,26 @@ const BUG_HUNT_WEIGHT_TWO_KEYWORDS = new Set([
93
93
  'win',
94
94
  'wins',
95
95
  ]);
96
+ const PROVE_WEIGHT_TWO_KEYWORDS = new Set([
97
+ 'agent',
98
+ 'allowed',
99
+ 'allow',
100
+ 'permission',
101
+ 'permissions',
102
+ 'proof',
103
+ 'contract',
104
+ 'contracts',
105
+ 'scope',
106
+ 'scoped',
107
+ 'forbidden',
108
+ 'receipt',
109
+ 'receipts',
110
+ 'replay',
111
+ 'ledger',
112
+ 'stale',
113
+ 'fresh',
114
+ 'bounded',
115
+ ]);
96
116
  export function workflowKeywordWeight(tool, keyword) {
97
117
  if (tool === 'projscan_evidence_pack')
98
118
  return evidencePackKeywordWeight(keyword);
@@ -100,6 +120,8 @@ export function workflowKeywordWeight(tool, keyword) {
100
120
  return releaseTrainKeywordWeight(keyword);
101
121
  if (tool === 'projscan_bug_hunt')
102
122
  return bugHuntKeywordWeight(keyword);
123
+ if (tool === 'projscan_prove')
124
+ return proveKeywordWeight(keyword);
103
125
  return undefined;
104
126
  }
105
127
  function evidencePackKeywordWeight(keyword) {
@@ -121,4 +143,11 @@ function bugHuntKeywordWeight(keyword) {
121
143
  return 0.25;
122
144
  return undefined;
123
145
  }
146
+ function proveKeywordWeight(keyword) {
147
+ if (PROVE_WEIGHT_TWO_KEYWORDS.has(keyword))
148
+ return 2;
149
+ if (['change', 'changed', 'edit', 'edits', 'prove'].includes(keyword))
150
+ return 0.5;
151
+ return undefined;
152
+ }
124
153
  //# sourceMappingURL=intentRouterWorkflowKeywordWeights.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"intentRouterWorkflowKeywordWeights.js","sourceRoot":"","sources":["../../src/core/intentRouterWorkflowKeywordWeights.ts"],"names":[],"mappings":"AAAA,MAAM,iCAAiC,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhF,MAAM,iCAAiC,GAAG,IAAI,GAAG,CAAC;IAChD,UAAU;IACV,OAAO;IACP,UAAU;IACV,SAAS;IACT,SAAS;IACT,WAAW;IACX,SAAS;IACT,aAAa;IACb,OAAO;IACP,KAAK;IACL,WAAW;IACX,MAAM;IACN,MAAM;IACN,QAAQ;IACR,OAAO;IACP,UAAU;IACV,WAAW;IACX,SAAS;IACT,QAAQ;IACR,OAAO;IACP,KAAK;IACL,QAAQ;IACR,OAAO;IACP,SAAS;IACT,WAAW;IACX,OAAO;IACP,SAAS;IACT,YAAY;IACZ,MAAM;IACN,SAAS;IACT,QAAQ;IACR,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;IACN,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,iCAAiC,GAAG,IAAI,GAAG,CAAC;IAChD,WAAW;IACX,QAAQ;IACR,WAAW;IACX,UAAU;IACV,YAAY;IACZ,OAAO;IACP,MAAM;IACN,SAAS;IACT,MAAM;IACN,SAAS;IACT,UAAU;IACV,SAAS;IACT,UAAU;IACV,YAAY;IACZ,aAAa;IACb,SAAS;IACT,OAAO;IACP,MAAM;IACN,WAAW;IACX,MAAM;IACN,OAAO;IACP,OAAO;IACP,WAAW;IACX,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAC;IAC3C,KAAK;IACL,MAAM;IACN,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,SAAS;IACT,UAAU;IACV,OAAO;IACP,UAAU;IACV,OAAO;IACP,KAAK;IACL,QAAQ;IACR,SAAS;IACT,aAAa;IACb,QAAQ;IACR,MAAM;IACN,UAAU;IACV,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,OAAO;IACP,MAAM;IACN,SAAS;IACT,OAAO;IACP,KAAK;IACL,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,OAAe;IACjE,IAAI,IAAI,KAAK,wBAAwB;QAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACjF,IAAI,IAAI,KAAK,wBAAwB;QAAE,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACjF,IAAI,IAAI,KAAK,mBAAmB;QAAE,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACvE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,yBAAyB,CAAC,OAAe;IAChD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,iCAAiC,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7D,IAAI,iCAAiC,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,yBAAyB,CAAC,OAAe;IAChD,OAAO,iCAAiC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxE,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAe;IAC3C,IAAI,4BAA4B,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"intentRouterWorkflowKeywordWeights.js","sourceRoot":"","sources":["../../src/core/intentRouterWorkflowKeywordWeights.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ export declare const DEFAULT_PROOF_LEDGER_PATH = ".projscan/proof-ledger.jsonl";
3
3
  export declare function normalizeProofCommand(command: string): string;
4
4
  export declare function changedFileFingerprint(files: string[]): string;
5
5
  export declare function redactProofSummary(value: string | undefined): string;
6
+ export declare function redactProofOutput(value: string): string;
6
7
  export declare function appendProofLedgerRecord(rootPath: string, ledgerPath: string | undefined, input: ProofLedgerWriteInput): Promise<ProofLedgerRecord>;
7
8
  export declare function readProofLedger(rootPath: string, ledgerPath: string | undefined): Promise<ProofLedgerRecord[]>;
8
9
  export declare function latestProofRecordFor(records: ProofLedgerRecord[], command: string): ProofLedgerRecord | undefined;
@@ -7,6 +7,7 @@ const REDACTION_PATTERNS = [
7
7
  /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi,
8
8
  /\b(?:sk|pk|whsec|ghp|gho|github_pat)_[A-Za-z0-9_=-]{8,}/gi,
9
9
  /\b(password|passwd|pwd|token|secret|api[_-]?key)\s*[:=]\s*["']?[^"'\s,;]+/gi,
10
+ /\b(--?(?:password|passwd|pwd|token|secret|api[_-]?key))\s+[^"'\s,;]+/gi,
10
11
  /\b[A-Za-z_][A-Za-z0-9_]*\.env\s*[:=]\s*[^"'\s,;]+/gi,
11
12
  ];
12
13
  export function normalizeProofCommand(command) {
@@ -17,17 +18,21 @@ export function changedFileFingerprint(files) {
17
18
  return crypto.createHash('sha256').update(normalized.join('\n')).digest('hex');
18
19
  }
19
20
  export function redactProofSummary(value) {
20
- let summary = (value ?? '').replace(/\s+/g, ' ').trim();
21
+ let summary = redactProofOutput(value ?? '').replace(/\s+/g, ' ').trim();
21
22
  if (summary.length === 0)
22
23
  summary = 'No proof output summary supplied.';
23
- for (const pattern of REDACTION_PATTERNS) {
24
- summary = summary.replace(pattern, (match, label) => label ? `${label}=[redacted]` : '[redacted]');
25
- }
26
24
  if (summary.length > MAX_SUMMARY_LENGTH) {
27
25
  return `${summary.slice(0, MAX_SUMMARY_LENGTH - 1)}...`;
28
26
  }
29
27
  return summary;
30
28
  }
29
+ export function redactProofOutput(value) {
30
+ let output = value;
31
+ for (const pattern of REDACTION_PATTERNS) {
32
+ output = output.replace(pattern, redactionReplacement);
33
+ }
34
+ return output;
35
+ }
31
36
  export async function appendProofLedgerRecord(rootPath, ledgerPath, input) {
32
37
  const completedAt = input.completedAt ?? new Date().toISOString();
33
38
  const durationMs = Math.max(0, Math.round(input.durationMs));
@@ -125,6 +130,11 @@ function isProofLedgerRecord(value) {
125
130
  function normalizePath(value) {
126
131
  return value.split(path.sep).join('/').replace(/^\.\//, '');
127
132
  }
133
+ function redactionReplacement(_match, ...args) {
134
+ const captures = args.slice(0, -2);
135
+ const label = captures.find((value) => typeof value === 'string' && value.length > 0);
136
+ return label ? `${label}=[redacted]` : '[redacted]';
137
+ }
128
138
  function isNodeErrorCode(error, code) {
129
139
  return (typeof error === 'object' &&
130
140
  error !== null &&
@@ -1 +1 @@
1
- {"version":3,"file":"proofLedger.js","sourceRoot":"","sources":["../../src/core/proofLedger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proofLedger.js","sourceRoot":"","sources":["../../src/core/proofLedger.ts"],"names":[],"mappings":"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"}
@@ -14,5 +14,7 @@ export interface ComputeProveOptions {
14
14
  durationMs?: number;
15
15
  summary?: string;
16
16
  logPath?: string;
17
+ runCommand?: string[];
18
+ runTimeoutMs?: number;
17
19
  }
18
20
  export declare function computeProve(rootPath: string, options?: ComputeProveOptions): Promise<ProveReport>;
@@ -1,11 +1,18 @@
1
+ import crypto from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
1
3
  import fs from 'node:fs/promises';
2
4
  import path from 'node:path';
3
5
  import { readFeedbackFile } from './feedback.js';
4
- import { appendProofLedgerRecord, changedFileFingerprint, latestProofRecordFor, readProofLedger, } from './proofLedger.js';
6
+ import { appendProofLedgerRecord, changedFileFingerprint, latestProofRecordFor, readProofLedger, redactProofOutput, } from './proofLedger.js';
5
7
  import { quoteShellArg } from './startShellArgs.js';
6
8
  import { computeSimulation } from './simulate.js';
7
9
  import { getChangedFiles } from '../utils/changedFiles.js';
8
10
  const DEFAULT_CONTRACT_PATH = '.projscan/proof-contract.json';
11
+ const DEFAULT_RUN_TIMEOUT_MS = 10 * 60 * 1000;
12
+ const PROOF_RUN_TIMEOUT_EXIT_CODE = 124;
13
+ const COMMAND_NOT_FOUND_EXIT_CODE = 127;
14
+ const MAX_PROOF_RUN_OUTPUT_CHARS = 256 * 1024;
15
+ const MAX_PROOF_RUN_LOG_CHARS = 512 * 1024;
9
16
  const GENERATED_FORBIDDEN_PATTERNS = [
10
17
  '.agentflight/**',
11
18
  '.agentloop/**',
@@ -85,16 +92,48 @@ const CONFIG_BASENAMES = new Set([
85
92
  ]);
86
93
  const CONFIG_SUFFIXES = ['.config.js', '.config.cjs', '.config.mjs', '.config.ts'];
87
94
  export async function computeProve(rootPath, options = {}) {
88
- const modeCount = [Boolean(options.intent?.trim()), Boolean(options.changed), Boolean(options.recordCommand?.trim())].filter(Boolean).length;
95
+ const modeCount = [
96
+ Boolean(options.intent?.trim()),
97
+ Boolean(options.changed),
98
+ Boolean(options.recordCommand?.trim()),
99
+ options.runCommand !== undefined,
100
+ ].filter(Boolean).length;
89
101
  if (modeCount > 1) {
90
- throw new Error('prove accepts only one of --intent, --changed, or --record-command');
102
+ throw new Error('prove accepts only one of --intent, --changed, --record-command, or --run');
91
103
  }
104
+ if (options.runCommand !== undefined)
105
+ return computeRunProof(rootPath, options);
92
106
  if (options.recordCommand?.trim())
93
107
  return computeRecordProof(rootPath, options);
94
108
  if (options.changed)
95
109
  return computeChangedProof(rootPath, options);
96
110
  return computeIntentProof(rootPath, options);
97
111
  }
112
+ async function computeRunProof(rootPath, options) {
113
+ const run = await executeProofCommand(rootPath, options.runCommand ?? [], options.runTimeoutMs);
114
+ const changedFiles = await getChangedFiles(rootPath, options.baseRef);
115
+ const record = await appendProofLedgerRecord(rootPath, options.ledgerPath, {
116
+ command: run.command,
117
+ exitCode: run.exitCode,
118
+ durationMs: run.durationMs,
119
+ changedFiles: proofRelevantChangedFiles(changedFiles.files),
120
+ outputSummary: run.outputSummary,
121
+ logPath: run.logPath,
122
+ source: 'prove-run',
123
+ });
124
+ const verdict = record.status === 'passed' ? 'ready' : 'blocked';
125
+ const verifiedWorkflow = verifiedWorkflowForRecord(verdict, record.status);
126
+ return {
127
+ schemaVersion: 1,
128
+ mode: 'run',
129
+ verdict,
130
+ summary: `${verdict}: executed ${record.status} proof for ${record.command}`,
131
+ commands: [record.command],
132
+ warnings: changedFiles.available ? [] : [changedFiles.reason ?? 'Changed-file evidence is unavailable.'],
133
+ verifiedWorkflow,
134
+ ledgerRecord: record,
135
+ };
136
+ }
98
137
  async function computeRecordProof(rootPath, options) {
99
138
  const proof = recordProofInput(options);
100
139
  const changedFiles = await getChangedFiles(rootPath, options.baseRef);
@@ -108,6 +147,7 @@ async function computeRecordProof(rootPath, options) {
108
147
  source: 'prove-record',
109
148
  });
110
149
  const verdict = record.status === 'passed' ? 'ready' : 'blocked';
150
+ const verifiedWorkflow = verifiedWorkflowForRecord(verdict, record.status);
111
151
  return {
112
152
  schemaVersion: 1,
113
153
  mode: 'record',
@@ -115,6 +155,7 @@ async function computeRecordProof(rootPath, options) {
115
155
  summary: `${verdict}: recorded ${record.status} proof for ${record.command}`,
116
156
  commands: [record.command],
117
157
  warnings: changedFiles.available ? [] : [changedFiles.reason ?? 'Changed-file evidence is unavailable.'],
158
+ verifiedWorkflow,
118
159
  ledgerRecord: record,
119
160
  };
120
161
  }
@@ -136,6 +177,167 @@ function recordProofInput(options) {
136
177
  logPath: options.logPath,
137
178
  };
138
179
  }
180
+ async function executeProofCommand(rootPath, command, timeoutMs) {
181
+ const commandVector = normalizeRunCommand(command);
182
+ const displayCommand = redactProofOutput(commandVector.map(quoteShellArg).join(' '));
183
+ const startedAtMs = Date.now();
184
+ const effectiveTimeoutMs = resolveRunTimeoutMs(timeoutMs);
185
+ const result = await spawnProofCommand(rootPath, commandVector, effectiveTimeoutMs);
186
+ const durationMs = Date.now() - startedAtMs;
187
+ const outputSummary = proofRunOutputSummary(result, effectiveTimeoutMs);
188
+ const logPath = await writeProofRunLog(rootPath, {
189
+ command: displayCommand,
190
+ exitCode: result.exitCode,
191
+ durationMs,
192
+ stdout: result.stdout,
193
+ stderr: result.stderr,
194
+ errorMessage: result.errorMessage,
195
+ timedOut: result.timedOut,
196
+ truncated: result.truncated,
197
+ });
198
+ return {
199
+ command: displayCommand,
200
+ exitCode: result.exitCode,
201
+ durationMs,
202
+ outputSummary,
203
+ logPath,
204
+ };
205
+ }
206
+ function normalizeRunCommand(command) {
207
+ const normalized = command.map((part) => String(part));
208
+ if (normalized.length === 0 || normalized[0]?.trim().length === 0) {
209
+ throw new Error('prove --run requires a command after --, for example: projscan prove --run -- npm test');
210
+ }
211
+ return normalized;
212
+ }
213
+ function resolveRunTimeoutMs(value) {
214
+ if (value === undefined)
215
+ return DEFAULT_RUN_TIMEOUT_MS;
216
+ if (!Number.isFinite(value) || value <= 0) {
217
+ throw new Error('prove --run-timeout-ms requires a positive number');
218
+ }
219
+ return Math.round(value);
220
+ }
221
+ function spawnProofCommand(rootPath, command, timeoutMs) {
222
+ return new Promise((resolve) => {
223
+ const [executable, ...args] = command;
224
+ let stdout = '';
225
+ let stderr = '';
226
+ let truncated = false;
227
+ let timedOut = false;
228
+ let finished = false;
229
+ let killTimer;
230
+ const timeout = setTimeout(() => {
231
+ timedOut = true;
232
+ child.kill('SIGTERM');
233
+ killTimer = setTimeout(() => child.kill('SIGKILL'), 1_000);
234
+ killTimer.unref();
235
+ }, timeoutMs);
236
+ timeout.unref();
237
+ const child = spawn(executable, args, {
238
+ cwd: rootPath,
239
+ env: process.env,
240
+ shell: false,
241
+ stdio: ['ignore', 'pipe', 'pipe'],
242
+ });
243
+ const finish = (exitCode, errorMessage) => {
244
+ if (finished)
245
+ return;
246
+ finished = true;
247
+ clearTimeout(timeout);
248
+ if (killTimer)
249
+ clearTimeout(killTimer);
250
+ resolve({
251
+ exitCode: timedOut ? PROOF_RUN_TIMEOUT_EXIT_CODE : exitCode,
252
+ stdout,
253
+ stderr,
254
+ ...(errorMessage ? { errorMessage } : {}),
255
+ timedOut,
256
+ truncated,
257
+ });
258
+ };
259
+ child.stdout?.on('data', (chunk) => {
260
+ const next = appendBoundedOutput(stdout, chunk);
261
+ stdout = next.value;
262
+ truncated ||= next.truncated;
263
+ });
264
+ child.stderr?.on('data', (chunk) => {
265
+ const next = appendBoundedOutput(stderr, chunk);
266
+ stderr = next.value;
267
+ truncated ||= next.truncated;
268
+ });
269
+ child.on('error', (error) => {
270
+ finish(COMMAND_NOT_FOUND_EXIT_CODE, error instanceof Error ? error.message : String(error));
271
+ });
272
+ child.on('close', (code, signal) => {
273
+ if (code === null) {
274
+ finish(signal ? 1 : 0);
275
+ return;
276
+ }
277
+ finish(code);
278
+ });
279
+ });
280
+ }
281
+ function appendBoundedOutput(current, chunk) {
282
+ const text = chunk.toString('utf-8');
283
+ const remaining = MAX_PROOF_RUN_OUTPUT_CHARS - current.length;
284
+ if (remaining <= 0)
285
+ return { value: current, truncated: text.length > 0 };
286
+ if (text.length > remaining) {
287
+ return { value: current + text.slice(0, remaining), truncated: true };
288
+ }
289
+ return { value: current + text, truncated: false };
290
+ }
291
+ function proofRunOutputSummary(result, timeoutMs) {
292
+ const parts = [
293
+ result.timedOut ? `timed out after ${timeoutMs}ms` : undefined,
294
+ result.errorMessage ? `start error: ${result.errorMessage}` : undefined,
295
+ result.stdout.trim() ? `stdout: ${result.stdout.trim()}` : undefined,
296
+ result.stderr.trim() ? `stderr: ${result.stderr.trim()}` : undefined,
297
+ result.truncated ? 'output truncated' : undefined,
298
+ ].filter((part) => Boolean(part));
299
+ return parts.join(' | ');
300
+ }
301
+ async function writeProofRunLog(rootPath, input) {
302
+ const relativePath = proofRunLogPath(input.command);
303
+ const fullPath = path.resolve(rootPath, relativePath);
304
+ const root = path.resolve(rootPath);
305
+ const relativeToRoot = path.relative(root, fullPath);
306
+ if (!relativeToRoot || relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {
307
+ throw new Error('Proof log path must stay inside the project root.');
308
+ }
309
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
310
+ await fs.writeFile(fullPath, redactedProofRunLog(input), 'utf-8');
311
+ return relativePath;
312
+ }
313
+ function proofRunLogPath(command) {
314
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
315
+ const digest = crypto.createHash('sha256').update(command).digest('hex').slice(0, 10);
316
+ return `.projscan/proof-logs/prove-run-${stamp}-${digest}.log`;
317
+ }
318
+ function redactedProofRunLog(input) {
319
+ const raw = [
320
+ `command: ${input.command}`,
321
+ `exitCode: ${input.exitCode}`,
322
+ `durationMs: ${input.durationMs}`,
323
+ `timedOut: ${input.timedOut ? 'yes' : 'no'}`,
324
+ `truncated: ${input.truncated ? 'yes' : 'no'}`,
325
+ input.errorMessage ? `error: ${input.errorMessage}` : undefined,
326
+ '--- stdout ---',
327
+ input.stdout || '(empty)',
328
+ '--- stderr ---',
329
+ input.stderr || '(empty)',
330
+ ]
331
+ .filter((line) => typeof line === 'string')
332
+ .join('\n');
333
+ const redacted = redactProofOutput(raw);
334
+ return `${truncateText(redacted, MAX_PROOF_RUN_LOG_CHARS)}\n`;
335
+ }
336
+ function truncateText(value, maxLength) {
337
+ if (value.length <= maxLength)
338
+ return value;
339
+ return `${value.slice(0, maxLength - 24)}\n[projscan log truncated]\n`;
340
+ }
139
341
  function isNonNegativeFiniteNumber(value) {
140
342
  return typeof value === 'number' && Number.isFinite(value) && value >= 0;
141
343
  }
@@ -163,6 +365,7 @@ async function computeIntentProof(rootPath, options) {
163
365
  contract,
164
366
  commands: contract.proofCommands,
165
367
  warnings: simulation.warnings,
368
+ verifiedWorkflow: contract.verifiedWorkflow,
166
369
  ...(savedContractPath ? { savedContractPath } : {}),
167
370
  };
168
371
  }
@@ -193,6 +396,7 @@ async function computeChangedProof(rootPath, options) {
193
396
  receipt,
194
397
  commands: receipt.proofStatus.commandsRequired,
195
398
  warnings: receipt.evidenceGaps,
399
+ verifiedWorkflow: receipt.verifiedWorkflow,
196
400
  };
197
401
  }
198
402
  function quickProofPreflight(changedFiles) {
@@ -225,7 +429,7 @@ function buildContract(input) {
225
429
  ...(input.trustMemory?.gaps ?? []),
226
430
  ]);
227
431
  const confidence = confidenceForTrustMemory(input.simulation.confidence, input.trustMemory);
228
- return {
432
+ const contract = {
229
433
  schemaVersion: 1,
230
434
  id: `proof-contract-${slug(input.intent)}`,
231
435
  intent: input.intent,
@@ -255,6 +459,10 @@ function buildContract(input) {
255
459
  receiptCommand: `projscan prove --changed --contract ${quoteShellArg(DEFAULT_CONTRACT_PATH)} --format markdown`,
256
460
  riskDelta: input.simulation.riskDelta,
257
461
  };
462
+ return {
463
+ ...contract,
464
+ verifiedWorkflow: verifiedWorkflowForContract(contract),
465
+ };
258
466
  }
259
467
  function contractProofCommands(simulationCommands) {
260
468
  return unique([
@@ -286,7 +494,7 @@ function buildReceipt(input) {
286
494
  scope,
287
495
  preflightVerdict: input.preflightVerdict,
288
496
  });
289
- return {
497
+ const receipt = {
290
498
  summary: summaryForReceipt(commitReadiness, scope),
291
499
  commitReadiness,
292
500
  scope,
@@ -298,6 +506,95 @@ function buildReceipt(input) {
298
506
  evidenceGaps,
299
507
  reviewerGuidance: reviewerGuidanceFor(commitReadiness, scope, reviewerDecision, proofStatus.status),
300
508
  };
509
+ return {
510
+ ...receipt,
511
+ verifiedWorkflow: verifiedWorkflowForReceipt(receipt),
512
+ };
513
+ }
514
+ function verifiedWorkflowForContract(contract) {
515
+ return {
516
+ phase: 'contract',
517
+ status: intentVerdict(contract),
518
+ nextAction: 'save the Proof Contract, make the bounded edit, then record proof commands',
519
+ nextCommand: contract.receiptCommand,
520
+ staleProof: false,
521
+ missingProof: contract.proofCommands.length > 0,
522
+ failedProof: false,
523
+ };
524
+ }
525
+ function verifiedWorkflowForRecord(verdict, recordStatus) {
526
+ const failedProof = recordStatus === 'failed';
527
+ return {
528
+ phase: 'record',
529
+ status: verdict,
530
+ nextAction: failedProof
531
+ ? 'fix the failed proof command, record it again, then replay changed proof'
532
+ : 'run projscan prove --changed to replay the ledger against the current diff',
533
+ nextCommand: 'projscan prove --changed --format markdown',
534
+ staleProof: false,
535
+ missingProof: false,
536
+ failedProof,
537
+ };
538
+ }
539
+ function verifiedWorkflowForReceipt(receipt) {
540
+ const proofStatus = receipt.proofStatus.status;
541
+ const staleProof = proofStatus === 'stale' || receipt.proofStatus.staleCommands.length > 0;
542
+ const missingProof = proofStatus === 'missing' ||
543
+ proofStatus === 'partial' ||
544
+ receipt.proofStatus.missingCommands.length > 0;
545
+ const failedProof = proofStatus === 'failed' || receipt.proofStatus.failedCommands.length > 0;
546
+ return {
547
+ phase: 'receipt',
548
+ status: receipt.commitReadiness,
549
+ nextAction: nextActionForReceipt({
550
+ receipt,
551
+ staleProof,
552
+ missingProof,
553
+ failedProof,
554
+ }),
555
+ nextCommand: nextCommandForReceipt({
556
+ receipt,
557
+ staleProof,
558
+ missingProof,
559
+ failedProof,
560
+ }),
561
+ reviewerDecision: receipt.reviewerDecision,
562
+ scopeStatus: receipt.scope.status,
563
+ proofStatus,
564
+ riskDeltaDirection: receipt.riskDeltaDirection,
565
+ staleProof,
566
+ missingProof,
567
+ failedProof,
568
+ };
569
+ }
570
+ function nextActionForReceipt(input) {
571
+ if (input.failedProof)
572
+ return 'fix failed proof commands before review';
573
+ if (input.staleProof)
574
+ return 'rerun stale proof commands before review';
575
+ if (input.missingProof)
576
+ return 'record missing proof commands before review';
577
+ if (input.receipt.scope.status === 'drifted') {
578
+ return 'resolve scope drift or update the Proof Contract before review';
579
+ }
580
+ if (input.receipt.reviewerDecision === 'safe-to-review') {
581
+ return 'share the Proof Receipt with the reviewer';
582
+ }
583
+ return 'review focused scope and proof gaps before approval';
584
+ }
585
+ function nextCommandForReceipt(input) {
586
+ if (input.failedProof) {
587
+ return `projscan prove --record-command ${quoteShellArg(input.receipt.proofStatus.failedCommands[0] ?? '<command>')} --exit-code 0 --duration-ms <ms>`;
588
+ }
589
+ if (input.staleProof) {
590
+ return `projscan prove --record-command ${quoteShellArg(input.receipt.proofStatus.staleCommands[0] ?? '<command>')} --exit-code 0 --duration-ms <ms>`;
591
+ }
592
+ if (input.missingProof) {
593
+ return 'projscan prove --record-command "<command>" --exit-code 0 --duration-ms <ms>';
594
+ }
595
+ if (input.receipt.scope.status === 'drifted')
596
+ return 'projscan prove --changed --format markdown';
597
+ return 'projscan evidence-pack --pr-comment';
301
598
  }
302
599
  function proofStatusFor(proofCommands, ledger, changedFiles) {
303
600
  const relevantChangedFiles = proofRelevantChangedFiles(changedFiles);
@@ -401,7 +698,7 @@ function scopeFor(contract, contractPath, changedFiles) {
401
698
  ]);
402
699
  const forbiddenTouched = changedFiles.filter((file) => contract.forbiddenFiles.some((pattern) => pathMatches(file, pattern)));
403
700
  const allowedTouched = changedFiles.filter((file) => allowed.has(file));
404
- const outsideAllowed = changedFiles.filter((file) => !allowed.has(file));
701
+ const outsideAllowed = changedFiles.filter((file) => !allowed.has(file) && !isLocalProofArtifactPath(file));
405
702
  const classifications = changedFiles.map((file) => classifyChangedFile({
406
703
  file,
407
704
  forbidden: forbiddenTouched.includes(file),
@@ -772,6 +1069,9 @@ function isGeneratedPath(file) {
772
1069
  file.startsWith('coverage/') ||
773
1070
  file.startsWith('dist/'));
774
1071
  }
1072
+ function isLocalProofArtifactPath(file) {
1073
+ return file.startsWith('.projscan/');
1074
+ }
775
1075
  function isSecuritySensitivePath(file) {
776
1076
  return (file === '.env' ||
777
1077
  file.startsWith('.env.') ||