projscan 4.2.0 → 4.3.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.
@@ -0,0 +1,219 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export async function loadMissionOutcome(rootPath, missionDir) {
4
+ const resolved = path.resolve(rootPath, missionDir);
5
+ const relativeMissionDir = path.relative(rootPath, resolved) || '.';
6
+ try {
7
+ const [summary, rows, decisions] = await Promise.all([
8
+ readJson(path.join(resolved, 'proof-logs', 'summary.json')),
9
+ readJsonl(path.join(resolved, 'proof-logs', 'status.jsonl')),
10
+ readReviewDecisions(resolved),
11
+ ]);
12
+ const status = missionStatus(summary.status);
13
+ const failedRows = rows.filter((row) => typeof row.exitCode === 'number' && row.exitCode !== 0);
14
+ const completedCommands = rows.length > 0
15
+ ? rows.length
16
+ : typeof summary.totalCommands === 'number'
17
+ ? summary.totalCommands
18
+ : 0;
19
+ const failedStep = stringValue(summary.failedStep) ?? failedRows[0]?.id;
20
+ const failedLog = stringValue(summary.log) ?? failedRows[0]?.log;
21
+ const proof = {
22
+ completedCommands,
23
+ failedCommands: failedRows.length,
24
+ reruns: countReruns(rows),
25
+ ...(typeof summary.totalCommands === 'number' ? { totalCommands: summary.totalCommands } : {}),
26
+ ...(failedStep ? { failedStep } : {}),
27
+ ...(failedLog ? { failedLog } : {}),
28
+ ...(typeof summary.exitCode === 'number' ? { exitCode: summary.exitCode } : {}),
29
+ rows,
30
+ };
31
+ const review = summarizeReview(decisions);
32
+ const whatChanged = buildWhatChanged(status, proof.completedCommands, proof.failedCommands, proof.reruns, failedStep);
33
+ const nextAction = stringValue(summary.nextAction);
34
+ const whatRemains = buildWhatRemains(status, nextAction, failedLog, failedStep);
35
+ const versionCandidate = versionCandidateFor(status, proof.failedCommands);
36
+ const resumePrompt = buildResumePrompt(status, whatChanged, whatRemains, versionCandidate.summary);
37
+ return {
38
+ schemaVersion: 1,
39
+ available: true,
40
+ missionDir: relativeMissionDir,
41
+ status,
42
+ ...(nextAction ? { nextAction } : {}),
43
+ proof,
44
+ review,
45
+ whatChanged,
46
+ whatRemains,
47
+ versionCandidate,
48
+ resumePrompt,
49
+ };
50
+ }
51
+ catch (err) {
52
+ const reason = err instanceof Error ? err.message : String(err);
53
+ return unavailableOutcome(relativeMissionDir, reason);
54
+ }
55
+ }
56
+ async function readReviewDecisions(missionDir) {
57
+ const candidates = [
58
+ path.join(missionDir, 'proof-logs', 'review-decisions.jsonl'),
59
+ path.join(missionDir, 'review-decisions.jsonl'),
60
+ ];
61
+ const all = [];
62
+ for (const candidate of candidates) {
63
+ all.push(...await readJsonl(candidate));
64
+ }
65
+ const single = await readJson(path.join(missionDir, 'review-decision.json')).catch(() => null);
66
+ if (single && typeof single.decision === 'string')
67
+ all.push(single);
68
+ return all.filter((record) => typeof record.decision === 'string');
69
+ }
70
+ async function readJson(filePath) {
71
+ return JSON.parse(await fs.readFile(filePath, 'utf8'));
72
+ }
73
+ async function readJsonl(filePath) {
74
+ let raw;
75
+ try {
76
+ raw = await fs.readFile(filePath, 'utf8');
77
+ }
78
+ catch {
79
+ return [];
80
+ }
81
+ const values = [];
82
+ for (const line of raw.split('\n')) {
83
+ const trimmed = line.trim();
84
+ if (!trimmed)
85
+ continue;
86
+ try {
87
+ values.push(JSON.parse(trimmed));
88
+ }
89
+ catch {
90
+ // Ignore malformed local review/proof rows; the summary still carries the mission state.
91
+ }
92
+ }
93
+ return values;
94
+ }
95
+ function missionStatus(value) {
96
+ return value === 'not_run' || value === 'running' || value === 'passed' || value === 'failed'
97
+ ? value
98
+ : 'unknown';
99
+ }
100
+ function stringValue(value) {
101
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
102
+ }
103
+ function countReruns(rows) {
104
+ const seen = new Set();
105
+ let reruns = 0;
106
+ for (const row of rows) {
107
+ if (!row.id)
108
+ continue;
109
+ if (seen.has(row.id))
110
+ reruns += 1;
111
+ else
112
+ seen.add(row.id);
113
+ }
114
+ return reruns;
115
+ }
116
+ function summarizeReview(decisions) {
117
+ const approvals = decisions.filter((decision) => decision.decision === 'approve_next_slice' || decision.decision === 'review_version_candidate').length;
118
+ return {
119
+ decisions,
120
+ approvals,
121
+ changeRequests: decisions.filter((decision) => decision.decision === 'request_changes').length,
122
+ versionCandidateReviews: decisions.filter((decision) => decision.decision === 'review_version_candidate').length,
123
+ };
124
+ }
125
+ function buildWhatChanged(status, completedCommands, failedCommands, reruns, failedStep) {
126
+ const lines = [];
127
+ if (status === 'passed') {
128
+ lines.push(`Mission proof passed after ${completedCommands} command(s).`);
129
+ }
130
+ else if (status === 'failed') {
131
+ lines.push(`Mission proof failed${failedStep ? ` at ${failedStep}` : ''}.`);
132
+ }
133
+ else if (status === 'running') {
134
+ lines.push('Mission proof is still marked running.');
135
+ }
136
+ else if (status === 'not_run') {
137
+ lines.push('Mission proof has not run yet.');
138
+ }
139
+ else {
140
+ lines.push('Mission proof state is unknown.');
141
+ }
142
+ if (failedCommands > 0)
143
+ lines.push(`${failedCommands} proof command(s) exited non-zero.`);
144
+ if (reruns > 0)
145
+ lines.push(`${reruns} proof command rerun(s) recorded.`);
146
+ return lines;
147
+ }
148
+ function buildWhatRemains(status, nextAction, failedLog, failedStep) {
149
+ if (status === 'passed')
150
+ return [capitalizeAction(nextAction ?? 'run ./review.sh and choose a reviewer reply.')];
151
+ if (status === 'failed') {
152
+ const log = failedLog ?? (failedStep ? `proof-logs/${failedStep}.log` : 'the failed proof log');
153
+ return [`Inspect ${log}, fix the failure, then rerun ./mission.sh.`];
154
+ }
155
+ if (status === 'running')
156
+ return [capitalizeAction(nextAction ?? 'wait for ./mission.sh to finish, or inspect proof-logs/status.jsonl.')];
157
+ if (status === 'not_run')
158
+ return [capitalizeAction(nextAction ?? 'run ./mission.sh to generate proof.')];
159
+ return ['Inspect proof-logs/summary.json and proof-logs/status.jsonl.'];
160
+ }
161
+ function capitalizeAction(value) {
162
+ return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1);
163
+ }
164
+ function versionCandidateFor(status, failedCommands) {
165
+ if (status === 'passed' && failedCommands === 0) {
166
+ return {
167
+ recommendation: 'review_candidate',
168
+ summary: 'Mission proof passed; review a version candidate before release, publish, or version bump.',
169
+ };
170
+ }
171
+ if (status === 'failed' || failedCommands > 0) {
172
+ return {
173
+ recommendation: 'do_not_cut',
174
+ summary: 'Do not cut a version until failed proof is fixed and rerun.',
175
+ };
176
+ }
177
+ if (status === 'running') {
178
+ return {
179
+ recommendation: 'wait',
180
+ summary: 'Wait for mission proof to finish before reviewing a version candidate.',
181
+ };
182
+ }
183
+ return {
184
+ recommendation: 'run_proof',
185
+ summary: 'Run mission proof before reviewing a version candidate.',
186
+ };
187
+ }
188
+ function buildResumePrompt(status, whatChanged, whatRemains, versionSummary) {
189
+ return `Mission proof ${status}. ${whatChanged.join(' ')} Next: ${whatRemains.join(' ')} Version: ${versionSummary}`;
190
+ }
191
+ function unavailableOutcome(missionDir, reason) {
192
+ return {
193
+ schemaVersion: 1,
194
+ available: false,
195
+ missionDir,
196
+ status: 'unknown',
197
+ reason,
198
+ proof: {
199
+ completedCommands: 0,
200
+ failedCommands: 0,
201
+ reruns: 0,
202
+ rows: [],
203
+ },
204
+ review: {
205
+ decisions: [],
206
+ approvals: 0,
207
+ changeRequests: 0,
208
+ versionCandidateReviews: 0,
209
+ },
210
+ whatChanged: ['Mission outcome is unavailable.'],
211
+ whatRemains: [`Inspect ${missionDir}/proof-logs/summary.json.`],
212
+ versionCandidate: {
213
+ recommendation: 'do_not_cut',
214
+ summary: 'Do not cut a version until mission proof can be read.',
215
+ },
216
+ resumePrompt: `Mission proof unavailable: ${reason}`,
217
+ };
218
+ }
219
+ //# sourceMappingURL=missionOutcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missionOutcome.js","sourceRoot":"","sources":["../../src/core/missionOutcome.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { MissionProofReport } from '../types.js';
2
+ export interface ComputeMissionProofOptions {
3
+ missions?: string[];
4
+ baselineFile?: string;
5
+ }
6
+ export declare function computeMissionProofReport(rootPath: string, options?: ComputeMissionProofOptions): Promise<MissionProofReport>;
@@ -0,0 +1,123 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { loadMissionOutcome } from './missionOutcome.js';
4
+ export async function computeMissionProofReport(rootPath, options = {}) {
5
+ const missions = normalizeMissions(options.missions);
6
+ const outcomes = await Promise.all(missions.map((mission) => loadMissionOutcome(rootPath, mission)));
7
+ const missionTotals = totalsFromOutcomes(outcomes);
8
+ const baseline = options.baselineFile
9
+ ? await loadBaseline(rootPath, options.baselineFile)
10
+ : undefined;
11
+ const comparison = baseline ? {
12
+ completionRateDelta: round(missionTotals.proofCompletionRate - baseline.totals.proofCompletionRate),
13
+ rerunsAvoided: Math.max(0, baseline.totals.reruns - missionTotals.reruns),
14
+ failedGatesAvoided: Math.max(0, baseline.totals.failedGates - missionTotals.failedGates),
15
+ minutesSaved: Math.max(0, baseline.totals.minutesSpent),
16
+ } : undefined;
17
+ const riskAvoided = buildRiskAvoided(missionTotals, comparison);
18
+ return {
19
+ schemaVersion: 1,
20
+ readOnly: true,
21
+ rootPath,
22
+ summary: summarize(missionTotals, baseline?.totals),
23
+ missionControl: {
24
+ missions: outcomes,
25
+ totals: missionTotals,
26
+ },
27
+ ...(baseline ? { baseline } : {}),
28
+ ...(comparison ? { comparison } : {}),
29
+ riskAvoided,
30
+ nextActions: buildNextActions(outcomes),
31
+ };
32
+ }
33
+ function normalizeMissions(missions) {
34
+ const values = missions && missions.length > 0 ? missions : ['.projscan/mission'];
35
+ return [...new Set(values)];
36
+ }
37
+ function totalsFromOutcomes(outcomes) {
38
+ const available = outcomes.filter((outcome) => outcome.available);
39
+ return {
40
+ missions: outcomes.length,
41
+ passed: countStatus(available, 'passed'),
42
+ failed: countStatus(available, 'failed'),
43
+ running: countStatus(available, 'running'),
44
+ notRun: countStatus(available, 'not_run'),
45
+ unavailable: outcomes.length - available.length,
46
+ proofCompletionRate: outcomes.length > 0 ? round(countStatus(available, 'passed') / outcomes.length) : 0,
47
+ reruns: available.reduce((sum, outcome) => sum + outcome.proof.reruns, 0),
48
+ failedGates: available.reduce((sum, outcome) => sum + outcome.proof.failedCommands, 0),
49
+ reviewerApprovals: available.reduce((sum, outcome) => sum + outcome.review.approvals, 0),
50
+ };
51
+ }
52
+ function countStatus(outcomes, status) {
53
+ return outcomes.filter((outcome) => outcome.status === status).length;
54
+ }
55
+ async function loadBaseline(rootPath, baselineFile) {
56
+ const resolved = path.resolve(rootPath, baselineFile);
57
+ const input = JSON.parse(await fs.readFile(resolved, 'utf8'));
58
+ const runs = Array.isArray(input.runs) ? input.runs : [];
59
+ const totals = totalsFromBaselineRuns(runs);
60
+ return {
61
+ path: path.relative(rootPath, resolved) || resolved,
62
+ runs,
63
+ totals,
64
+ };
65
+ }
66
+ function totalsFromBaselineRuns(runs) {
67
+ const passed = runs.filter((run) => run.status === 'passed').length;
68
+ return {
69
+ missions: runs.length,
70
+ passed,
71
+ failed: runs.filter((run) => run.status === 'failed').length,
72
+ running: runs.filter((run) => run.status === 'running').length,
73
+ notRun: runs.filter((run) => run.status === 'not_run').length,
74
+ unavailable: runs.filter((run) => run.status === 'unknown').length,
75
+ proofCompletionRate: runs.length > 0 ? round(passed / runs.length) : 0,
76
+ reruns: sum(runs.map((run) => run.reruns)),
77
+ failedGates: sum(runs.map((run) => run.failedGates)),
78
+ reviewerApprovals: sum(runs.map((run) => run.reviewerApprovals)),
79
+ minutesSpent: sum(runs.map((run) => run.minutesSpent)),
80
+ };
81
+ }
82
+ function sum(values) {
83
+ return values.reduce((total, value) => total + (typeof value === 'number' && Number.isFinite(value) ? value : 0), 0);
84
+ }
85
+ function buildRiskAvoided(totals, comparison) {
86
+ const lines = [];
87
+ if (totals.failedGates > 0) {
88
+ lines.push(`${totals.failedGates} failed mission gate(s) stopped before release or publish.`);
89
+ }
90
+ if (comparison && comparison.failedGatesAvoided > 0) {
91
+ lines.push(`${comparison.failedGatesAvoided} failed gate(s) avoided versus the manual baseline.`);
92
+ }
93
+ if (comparison && comparison.rerunsAvoided > 0) {
94
+ lines.push(`${comparison.rerunsAvoided} rerun(s) avoided versus the manual baseline.`);
95
+ }
96
+ if (lines.length === 0)
97
+ lines.push('No failed mission gates recorded yet.');
98
+ return lines;
99
+ }
100
+ function buildNextActions(outcomes) {
101
+ const actions = outcomes.map((outcome) => ({
102
+ label: `Review mission ${outcome.missionDir}`,
103
+ command: `projscan start --mission ${shellToken(outcome.missionDir)}`,
104
+ }));
105
+ actions.push({
106
+ label: 'Capture reviewer feedback',
107
+ command: 'projscan feedback add --file .projscan-feedback.json --repo <repo> --pr <url> --reviewer <handle> --useful true --minutes-saved 10',
108
+ });
109
+ return actions;
110
+ }
111
+ function summarize(totals, baseline) {
112
+ const base = `${totals.missions} mission bundle(s): ${totals.passed} passed, ${totals.failed} failed, ${totals.running} running, ${totals.notRun} not run.`;
113
+ if (!baseline)
114
+ return base;
115
+ return `${base} Baseline: ${baseline.missions} run(s), ${baseline.reruns} rerun(s), ${baseline.failedGates} failed gate(s), ${baseline.minutesSpent} minute(s).`;
116
+ }
117
+ function shellToken(value) {
118
+ return /^[A-Za-z0-9_./:-]+$/.test(value) ? value : JSON.stringify(value);
119
+ }
120
+ function round(value) {
121
+ return Math.round(value * 100) / 100;
122
+ }
123
+ //# sourceMappingURL=missionProof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missionProof.js","sourceRoot":"","sources":["../../src/core/missionProof.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { StartReport, WorkplanMode } from '../types.js';
2
2
  export interface ComputeStartOptions {
3
3
  mode?: WorkplanMode;
4
4
  intent?: string;
5
+ missionDir?: string;
5
6
  maxTasks?: number;
6
7
  maxRisks?: number;
7
8
  includeHandoff?: boolean;
@@ -5,6 +5,7 @@ import { buildFirstTenMinutes } from './onboarding.js';
5
5
  import { computeQualityScorecard } from './qualityScorecard.js';
6
6
  import { buildWorkplanHandoff, computeWorkplan, isWorkplanMode } from './workplan.js';
7
7
  import { routeIntent } from './intentRouter.js';
8
+ import { loadMissionOutcome } from './missionOutcome.js';
8
9
  import { getChangedFiles } from '../utils/changedFiles.js';
9
10
  const DEFAULT_MAX_TASKS = 5;
10
11
  const DEFAULT_MAX_RISKS = 5;
@@ -15,11 +16,12 @@ export async function computeStartReport(rootPath, options = {}) {
15
16
  const mode = modeResolution.mode;
16
17
  const maxTasks = normalizeLimit(options.maxTasks, DEFAULT_MAX_TASKS, 12);
17
18
  const maxRisks = normalizeLimit(options.maxRisks, DEFAULT_MAX_RISKS, 12);
18
- const [setup, workplan, quality, riskSources] = await Promise.all([
19
+ const [setup, workplan, quality, riskSources, missionOutcome] = await Promise.all([
19
20
  computeFirstRunDiagnostics(rootPath),
20
21
  computeWorkplan(rootPath, { mode, maxTasks }),
21
22
  computeQualityScorecard(rootPath, { maxRisks }),
22
23
  buildStartRiskSources(rootPath),
24
+ options.missionDir ? loadMissionOutcome(rootPath, options.missionDir) : Promise.resolve(undefined),
23
25
  ]);
24
26
  const workflow = chooseWorkflow(mode, getWorkflowRecipes().recipes);
25
27
  const topRisks = combineRisks(workplan, quality.topRisks, maxRisks);
@@ -46,6 +48,7 @@ export async function computeStartReport(rootPath, options = {}) {
46
48
  adoptionGaps,
47
49
  coordinationHints,
48
50
  riskSources,
51
+ missionOutcome,
49
52
  });
50
53
  const nextActions = dedupeActions([
51
54
  missionControl.primaryAction,
@@ -276,7 +279,7 @@ function buildMissionControl(input) {
276
279
  successCriteria,
277
280
  proofCommands,
278
281
  });
279
- const resume = missionResume(executionPlan);
282
+ const resume = missionResume(executionPlan, input.missionOutcome);
280
283
  const reviewProof = buildMissionReviewProof(resume, proofCommands);
281
284
  const whyNow = routed
282
285
  ? routedWhyNow(routed, actionPlan)
@@ -333,6 +336,7 @@ function buildMissionControl(input) {
333
336
  runbook,
334
337
  reviewGate,
335
338
  taskCard,
339
+ ...(input.missionOutcome ? { outcome: input.missionOutcome } : {}),
336
340
  handoffPrompt,
337
341
  };
338
342
  }
@@ -680,7 +684,7 @@ function formatTaskCardToolCall(toolCall) {
680
684
  ? `${toolCall.tool} ${JSON.stringify(toolCall.args)}`
681
685
  : toolCall.tool;
682
686
  }
683
- function missionResume(plan) {
687
+ function missionResume(plan, outcome) {
684
688
  const cursor = plan.cursor;
685
689
  const commandBlock = cursor.command && isRunnableCommand(cursor.command) ? cursor.command : undefined;
686
690
  const toolCall = resumeToolCall(plan, cursor);
@@ -700,11 +704,12 @@ function missionResume(plan) {
700
704
  const prompt = commandBlock
701
705
  ? `Resume at ${cursor.stepId} in ${cursor.phaseId}: run \`${commandBlock}\`.${resumeUnlocksSentence(unlocks, cursor.unlocks)}`
702
706
  : `Resume at ${cursor.stepId} in ${cursor.phaseId}: ${instruction}${resumeBlockersSentence(blockedBy, cursor.blockedBy)}`;
707
+ const finalPrompt = outcome?.available ? `${outcome.resumePrompt} ${prompt}` : prompt;
703
708
  return {
704
709
  currentStep: cursor,
705
710
  status: cursor.status,
706
711
  instruction,
707
- prompt,
712
+ prompt: finalPrompt,
708
713
  ...(commandBlock ? { commandBlock } : {}),
709
714
  ...(toolCall ? { toolCall } : {}),
710
715
  ...(followUps.length > 0 ? { followUps } : {}),