@open-agent-toolkit/cli 0.2.23 → 0.2.25
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/assets/agents/oat-reviewer.md +12 -1
- package/assets/docs/cli-utilities/configuration.md +2 -4
- package/assets/docs/cli-utilities/workflow-gates.md +27 -3
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/workflows/projects/reviews.md +19 -10
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +3 -3
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +3 -3
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +3 -3
- package/assets/skills/oat-project-implement/references/phase-execution.md +29 -0
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +3 -3
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +3 -3
- package/assets/skills/oat-project-review-provide/SKILL.md +124 -26
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +64 -15
- package/assets/skills/oat-project-review-receive/SKILL.md +21 -1
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +39 -2
- package/assets/skills/oat-review-provide-remote/SKILL.md +55 -12
- package/assets/templates/plan.md +13 -7
- package/dist/commands/config/index.js +8 -8
- package/dist/commands/gate/child-process.d.ts +32 -0
- package/dist/commands/gate/child-process.d.ts.map +1 -0
- package/dist/commands/gate/child-process.js +210 -0
- package/dist/commands/gate/index.d.ts +2 -31
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +24 -150
- package/dist/commands/review/latest.d.ts.map +1 -1
- package/dist/commands/review/latest.js +22 -9
- package/dist/config/resolve.js +1 -1
- package/dist/review-remote/body-builder.d.ts +12 -3
- package/dist/review-remote/body-builder.d.ts.map +1 -1
- package/dist/review-remote/body-builder.js +11 -0
- package/dist/review-remote/marker-parser.d.ts +8 -3
- package/dist/review-remote/marker-parser.d.ts.map +1 -1
- package/dist/review-remote/marker-parser.js +13 -2
- package/dist/review-remote/narrowing.d.ts +44 -8
- package/dist/review-remote/narrowing.d.ts.map +1 -1
- package/dist/review-remote/narrowing.js +106 -10
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFileSync
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
@@ -20,6 +20,7 @@ import { Command } from 'commander';
|
|
|
20
20
|
import YAML from 'yaml';
|
|
21
21
|
import { createGateActivityProbe, } from './activity-probes.js';
|
|
22
22
|
import { createBranchLocalGateCli, currentGateCliLaunch, readGateRouteReceipt, removeBranchLocalGateCli, } from './branch-local-cli.js';
|
|
23
|
+
import { runChildProcess, } from './child-process.js';
|
|
23
24
|
import { parseReviewGateVerdict, severityDisplayName, } from './review-verdict.js';
|
|
24
25
|
import { createGateRouteCommand } from './route.js';
|
|
25
26
|
const DEFAULT_DEPENDENCIES = {
|
|
@@ -75,6 +76,7 @@ const REVIEW_GATE_CONTEXT_NOTE = [
|
|
|
75
76
|
const GATE_CHECK_TIMEOUT_MS = 5_000;
|
|
76
77
|
const GATE_EXEC_TIMEOUT_MS = 15 * 60 * 1_000;
|
|
77
78
|
const GATE_LIVENESS_INTERVAL_MS = 30_000;
|
|
79
|
+
const AMBIENT_ACTIVITY_ATTRIBUTION = 'not attributable to this gate child';
|
|
78
80
|
async function writeGateRunMarker(path, marker, warn) {
|
|
79
81
|
try {
|
|
80
82
|
await mkdir(join(tmpdir(), 'oat-gate-runs'), { recursive: true });
|
|
@@ -239,152 +241,6 @@ function corroborateGateInvocation(expected, actual, targetCorroboration) {
|
|
|
239
241
|
},
|
|
240
242
|
};
|
|
241
243
|
}
|
|
242
|
-
async function runChildProcess(command, args, options) {
|
|
243
|
-
return new Promise((resolve, reject) => {
|
|
244
|
-
let timedOut = false;
|
|
245
|
-
let killTimeout = null;
|
|
246
|
-
let stderrBytes = 0;
|
|
247
|
-
let stdoutBytes = 0;
|
|
248
|
-
let refusal;
|
|
249
|
-
let stdoutLineBuffer = '';
|
|
250
|
-
let stderrLineBuffer = '';
|
|
251
|
-
let latestActivityEvidence;
|
|
252
|
-
let latestActivityProbeStatus;
|
|
253
|
-
let livenessProbePending = false;
|
|
254
|
-
const startedAt = Date.now();
|
|
255
|
-
let lastActivityAt = startedAt;
|
|
256
|
-
const child = spawn(command, args, {
|
|
257
|
-
cwd: options.cwd,
|
|
258
|
-
env: options.env,
|
|
259
|
-
stdio: options.stdio === 'pipe'
|
|
260
|
-
? [options.stdin, 'pipe', 'pipe']
|
|
261
|
-
: options.stdio,
|
|
262
|
-
});
|
|
263
|
-
const recordActivity = () => {
|
|
264
|
-
lastActivityAt = Date.now();
|
|
265
|
-
};
|
|
266
|
-
const scanChunk = (buffer, chunk) => {
|
|
267
|
-
const combined = buffer + chunk.toString('utf8');
|
|
268
|
-
const lines = combined.split('\n');
|
|
269
|
-
const remainder = lines.pop() ?? '';
|
|
270
|
-
if (!refusal) {
|
|
271
|
-
for (const line of lines) {
|
|
272
|
-
refusal = extractStructuredRefusal(line.replace(/\r$/, ''));
|
|
273
|
-
if (refusal) {
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return remainder;
|
|
279
|
-
};
|
|
280
|
-
child.stdout?.on('data', (chunk) => {
|
|
281
|
-
stdoutBytes += chunk.byteLength;
|
|
282
|
-
stdoutLineBuffer = scanChunk(stdoutLineBuffer, chunk);
|
|
283
|
-
recordActivity();
|
|
284
|
-
const destination = options.stdoutDestination === 'stderr'
|
|
285
|
-
? process.stderr
|
|
286
|
-
: process.stdout;
|
|
287
|
-
destination.write(chunk);
|
|
288
|
-
});
|
|
289
|
-
child.stderr?.on('data', (chunk) => {
|
|
290
|
-
stderrBytes += chunk.byteLength;
|
|
291
|
-
stderrLineBuffer = scanChunk(stderrLineBuffer, chunk);
|
|
292
|
-
recordActivity();
|
|
293
|
-
process.stderr.write(chunk);
|
|
294
|
-
});
|
|
295
|
-
const processAlive = () => {
|
|
296
|
-
if (child.pid === undefined)
|
|
297
|
-
return false;
|
|
298
|
-
try {
|
|
299
|
-
process.kill(child.pid, 0);
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
catch {
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
const livenessInterval = options.onLiveness && options.livenessIntervalMs
|
|
307
|
-
? setInterval(() => {
|
|
308
|
-
if (livenessProbePending)
|
|
309
|
-
return;
|
|
310
|
-
livenessProbePending = true;
|
|
311
|
-
const now = Date.now();
|
|
312
|
-
void (async () => {
|
|
313
|
-
const activityProbeStatus = await options.activityProbe?.observe(now);
|
|
314
|
-
const evidence = activityProbeStatus?.evidence;
|
|
315
|
-
if (activityProbeStatus) {
|
|
316
|
-
latestActivityProbeStatus = activityProbeStatus;
|
|
317
|
-
}
|
|
318
|
-
if (evidence)
|
|
319
|
-
latestActivityEvidence = evidence;
|
|
320
|
-
options.onLiveness?.({
|
|
321
|
-
elapsedMs: now - startedAt,
|
|
322
|
-
hardBudgetMs: options.timeoutMs,
|
|
323
|
-
idleMs: now - lastActivityAt,
|
|
324
|
-
processAlive: processAlive(),
|
|
325
|
-
...(latestActivityProbeStatus
|
|
326
|
-
? { activityProbeStatus: latestActivityProbeStatus }
|
|
327
|
-
: {}),
|
|
328
|
-
...(latestActivityEvidence
|
|
329
|
-
? { lastActivityEvidence: latestActivityEvidence }
|
|
330
|
-
: {}),
|
|
331
|
-
});
|
|
332
|
-
})().finally(() => {
|
|
333
|
-
livenessProbePending = false;
|
|
334
|
-
});
|
|
335
|
-
}, options.livenessIntervalMs)
|
|
336
|
-
: null;
|
|
337
|
-
livenessInterval?.unref();
|
|
338
|
-
const timeout = setTimeout(() => {
|
|
339
|
-
timedOut = true;
|
|
340
|
-
child.kill('SIGTERM');
|
|
341
|
-
killTimeout = setTimeout(() => {
|
|
342
|
-
child.kill('SIGKILL');
|
|
343
|
-
}, 5_000);
|
|
344
|
-
killTimeout.unref();
|
|
345
|
-
}, options.timeoutMs);
|
|
346
|
-
timeout.unref();
|
|
347
|
-
child.on('error', (error) => {
|
|
348
|
-
clearTimeout(timeout);
|
|
349
|
-
if (livenessInterval) {
|
|
350
|
-
clearInterval(livenessInterval);
|
|
351
|
-
}
|
|
352
|
-
if (killTimeout) {
|
|
353
|
-
clearTimeout(killTimeout);
|
|
354
|
-
}
|
|
355
|
-
reject(error);
|
|
356
|
-
});
|
|
357
|
-
child.on('close', (code) => {
|
|
358
|
-
clearTimeout(timeout);
|
|
359
|
-
if (livenessInterval) {
|
|
360
|
-
clearInterval(livenessInterval);
|
|
361
|
-
}
|
|
362
|
-
if (killTimeout) {
|
|
363
|
-
clearTimeout(killTimeout);
|
|
364
|
-
}
|
|
365
|
-
refusal ??=
|
|
366
|
-
extractStructuredRefusal(stdoutLineBuffer.replace(/\r$/, '')) ??
|
|
367
|
-
extractStructuredRefusal(stderrLineBuffer.replace(/\r$/, ''));
|
|
368
|
-
resolve({
|
|
369
|
-
...(latestActivityEvidence
|
|
370
|
-
? { activityEvidence: latestActivityEvidence }
|
|
371
|
-
: {}),
|
|
372
|
-
...(latestActivityProbeStatus
|
|
373
|
-
? { activityProbeStatus: latestActivityProbeStatus }
|
|
374
|
-
: {}),
|
|
375
|
-
exitCode: timedOut ? 124 : (code ?? 1),
|
|
376
|
-
...(refusal ? { refusal } : {}),
|
|
377
|
-
stderrBytes,
|
|
378
|
-
stdoutBytes,
|
|
379
|
-
...(timedOut ? { timedOut: true } : {}),
|
|
380
|
-
});
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
function extractStructuredRefusal(output) {
|
|
385
|
-
const match = output.match(/^OAT_GATE_REFUSAL: (.*)$/m);
|
|
386
|
-
return match?.[1]?.replace(/\r$/, '');
|
|
387
|
-
}
|
|
388
244
|
function isGateWriteLayer(value) {
|
|
389
245
|
return VALID_WRITE_LAYERS.includes(value);
|
|
390
246
|
}
|
|
@@ -1184,7 +1040,7 @@ async function executeTarget(selected, prompt, context, dependencies, timeout) {
|
|
|
1184
1040
|
else {
|
|
1185
1041
|
const activityDescription = lastActivityEvidence
|
|
1186
1042
|
? lastActivityEvidence.scope === 'ambient-runtime'
|
|
1187
|
-
?
|
|
1043
|
+
? `ambient runtime activity (${AMBIENT_ACTIVITY_ATTRIBUTION})`
|
|
1188
1044
|
: 'project-directory activity'
|
|
1189
1045
|
: `${activityProbeStatus?.status ?? 'unavailable'} (${activityProbeStatus?.attemptedPath ?? 'no path'})`;
|
|
1190
1046
|
context.logger.info(`Gate liveness: target=${selected.id} elapsed_ms=${elapsedMs} idle_ms=${idleMs} hard_budget_ms=${hardBudgetMs} process_alive=${processAlive} activity_evidence=${activityDescription}.`);
|
|
@@ -1606,6 +1462,25 @@ function writeReviewGateExecutionFailure(context, payload) {
|
|
|
1606
1462
|
return;
|
|
1607
1463
|
}
|
|
1608
1464
|
context.logger.error(message);
|
|
1465
|
+
if (payload.activityEvidence && !payload.refusal) {
|
|
1466
|
+
context.logger.error(formatGateActivityEvidenceDiagnostic(payload.activityEvidence));
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
function formatGateActivityEvidenceDiagnostic(evidence, now = Date.now()) {
|
|
1470
|
+
const transcriptScope = evidence.scope === 'ambient-runtime'
|
|
1471
|
+
? `${evidence.runtime} ambient transcript`
|
|
1472
|
+
: `${evidence.runtime} project transcript`;
|
|
1473
|
+
const baselineState = evidence.changedSinceBaseline
|
|
1474
|
+
? 'changed since baseline'
|
|
1475
|
+
: 'did not change since baseline';
|
|
1476
|
+
const observedAgoMs = Math.max(0, now - evidence.observedAt);
|
|
1477
|
+
const lastChangeTiming = evidence.lastChangeAt === null
|
|
1478
|
+
? 'latest transcript change time unavailable'
|
|
1479
|
+
: `latest transcript change was ${Math.max(0, evidence.observedAt - evidence.lastChangeAt)}ms before observation`;
|
|
1480
|
+
const attributionWarning = evidence.scope === 'ambient-runtime'
|
|
1481
|
+
? ` This activity is ${AMBIENT_ACTIVITY_ATTRIBUTION}.`
|
|
1482
|
+
: '';
|
|
1483
|
+
return `Activity evidence: ${transcriptScope} metadata ${baselineState}; observed ${observedAgoMs}ms ago; ${lastChangeTiming}.${attributionWarning}`;
|
|
1609
1484
|
}
|
|
1610
1485
|
function writeReviewGateUnexpectedFailure(context, payload) {
|
|
1611
1486
|
const message = payload.error instanceof Error
|
|
@@ -2085,8 +1960,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2085
1960
|
...routeReceipt,
|
|
2086
1961
|
})}\n`);
|
|
2087
1962
|
const childExitCode = childResult.exitCode;
|
|
2088
|
-
const refusal = childResult.refusal
|
|
2089
|
-
extractStructuredRefusal(childResult.capturedOutput ?? '');
|
|
1963
|
+
const refusal = childResult.refusal;
|
|
2090
1964
|
const writeRefusalFailure = () => {
|
|
2091
1965
|
if (!refusal) {
|
|
2092
1966
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latest.d.ts","sourceRoot":"","sources":["../../../src/commands/review/latest.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;CACrB;AASD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,UAAU,wBAAwB;IAChC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACnE;
|
|
1
|
+
{"version":3,"file":"latest.d.ts","sourceRoot":"","sources":["../../../src/commands/review/latest.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;CACrB;AASD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,UAAU,wBAAwB;IAChC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACnE;AAiPD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAkG9B;AAmDD,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CA2BT"}
|
|
@@ -99,20 +99,33 @@ function parseReviewLedgerEvents(planContent) {
|
|
|
99
99
|
const remaining = planContent.slice(heading.index + heading[0].length);
|
|
100
100
|
const nextHeadingIndex = remaining.search(/^##(?!#)[ \t]+\S.*\r?$/m);
|
|
101
101
|
const section = nextHeadingIndex === -1 ? remaining : remaining.slice(0, nextHeadingIndex);
|
|
102
|
-
|
|
102
|
+
const rows = section
|
|
103
103
|
.split('\n')
|
|
104
104
|
.filter((line) => line.trim().startsWith('|'))
|
|
105
|
-
.slice(2)
|
|
106
105
|
.map((line) => line
|
|
107
106
|
.split('|')
|
|
108
107
|
.slice(1, -1)
|
|
109
|
-
.map((cell) => cell.trim()))
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
.map((cell) => cell.trim()));
|
|
109
|
+
const headers = rows[0];
|
|
110
|
+
if (!headers) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const normalizedHeaders = headers.map((header) => header.toLowerCase());
|
|
114
|
+
const scopeIndex = normalizedHeaders.indexOf('scope');
|
|
115
|
+
const typeIndex = normalizedHeaders.indexOf('type');
|
|
116
|
+
const statusIndex = normalizedHeaders.indexOf('status');
|
|
117
|
+
const artifactIndex = normalizedHeaders.indexOf('artifact');
|
|
118
|
+
if (scopeIndex === -1 ||
|
|
119
|
+
typeIndex === -1 ||
|
|
120
|
+
statusIndex === -1 ||
|
|
121
|
+
artifactIndex === -1) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
return rows.slice(2).map((cells) => ({
|
|
125
|
+
scope: cells[scopeIndex] ?? '',
|
|
126
|
+
type: cells[typeIndex] ?? '',
|
|
127
|
+
status: cells[statusIndex] ?? '',
|
|
128
|
+
artifact: cells[artifactIndex] ?? '',
|
|
116
129
|
}));
|
|
117
130
|
}
|
|
118
131
|
async function correlateProjectActionability(repoRoot, projectPath, candidates) {
|
package/dist/config/resolve.js
CHANGED
|
@@ -68,7 +68,7 @@ const DEFAULT_WORKFLOW_CONFIG = {
|
|
|
68
68
|
postImplementSequence: null,
|
|
69
69
|
reviewExecutionModel: null,
|
|
70
70
|
autoReviewAtHillCheckpoints: null,
|
|
71
|
-
autoNarrowReReviewScope:
|
|
71
|
+
autoNarrowReReviewScope: true,
|
|
72
72
|
autoArtifactReview: {
|
|
73
73
|
plan: true,
|
|
74
74
|
analysis: true,
|
|
@@ -38,15 +38,13 @@ export interface OutOfDiffFinding {
|
|
|
38
38
|
/** Finding description / rationale — preserved verbatim, never dropped. */
|
|
39
39
|
body: string;
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
interface BuildInputBase {
|
|
42
42
|
/** Full 40-char hex SHA of the reviewed PR HEAD. */
|
|
43
43
|
headSha: string;
|
|
44
44
|
/** Scope token (`pNN`, `final`, …) or the `ad-hoc` sentinel. */
|
|
45
45
|
scope: string;
|
|
46
46
|
/** Project path — set only on the project rail; omitted on ad-hoc. */
|
|
47
47
|
project?: string;
|
|
48
|
-
/** How the review was invoked. */
|
|
49
|
-
invocation: ReviewInvocation;
|
|
50
48
|
/** 2-3 sentence human-readable summary. */
|
|
51
49
|
summary: string;
|
|
52
50
|
findings: BuilderFinding[];
|
|
@@ -63,6 +61,16 @@ export interface BuildInput {
|
|
|
63
61
|
/** Commands the user can run to verify fixes; omitted when absent/empty. */
|
|
64
62
|
verificationCommands?: string[];
|
|
65
63
|
}
|
|
64
|
+
interface LifecycleBuildInput {
|
|
65
|
+
invocation: Exclude<ReviewInvocation, 'gate'>;
|
|
66
|
+
gateTarget?: never;
|
|
67
|
+
}
|
|
68
|
+
interface GateBuildInput {
|
|
69
|
+
invocation: 'gate';
|
|
70
|
+
/** Exact non-empty target that owns this gate review lineage. */
|
|
71
|
+
gateTarget: string;
|
|
72
|
+
}
|
|
73
|
+
export type BuildInput = BuildInputBase & (LifecycleBuildInput | GateBuildInput);
|
|
66
74
|
/**
|
|
67
75
|
* Map a finding set to the GitHub review verdict: `REQUEST_CHANGES` when any
|
|
68
76
|
* critical or important finding is present, otherwise `COMMENT` (including the
|
|
@@ -76,4 +84,5 @@ export declare function buildReviewBody(input: BuildInput): {
|
|
|
76
84
|
body: string;
|
|
77
85
|
verdict: ReviewVerdict;
|
|
78
86
|
};
|
|
87
|
+
export {};
|
|
79
88
|
//# sourceMappingURL=body-builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body-builder.d.ts","sourceRoot":"","sources":["../../src/review-remote/body-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE3E,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE5E,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,
|
|
1
|
+
{"version":3,"file":"body-builder.d.ts","sourceRoot":"","sources":["../../src/review-remote/body-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE3E,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE5E,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,eAAe,CAAC;IAC1B,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,cAAc;IACtB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACvC,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,UAAU,mBAAmB;IAC3B,UAAU,EAAE,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAC9C,UAAU,CAAC,EAAE,KAAK,CAAC;CACpB;AAED,UAAU,cAAc;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAAG,cAAc,GACrC,CAAC,mBAAmB,GAAG,cAAc,CAAC,CAAC;AAEzC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,aAAa,CAKpE;AAgFD;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;CACxB,CAkCA"}
|
|
@@ -45,6 +45,17 @@ function buildMarkerBlock(input) {
|
|
|
45
45
|
lines.push(`oat_project: ${input.project}`);
|
|
46
46
|
}
|
|
47
47
|
lines.push(`oat_review_invocation: ${input.invocation}`);
|
|
48
|
+
if (input.invocation === 'gate') {
|
|
49
|
+
if (input.gateTarget.trim() === '' ||
|
|
50
|
+
input.gateTarget.trim() !== input.gateTarget) {
|
|
51
|
+
throw new Error('gate invocation requires an exact non-empty gateTarget');
|
|
52
|
+
}
|
|
53
|
+
lines.push(`oat_gate_target: ${input.gateTarget}`);
|
|
54
|
+
}
|
|
55
|
+
else if ('gateTarget' in input &&
|
|
56
|
+
input.gateTarget !== undefined) {
|
|
57
|
+
throw new Error('lifecycle invocation must not include gateTarget');
|
|
58
|
+
}
|
|
48
59
|
return `<!-- ${MARKER_BLOCK_OPEN}\n${lines.join('\n')}\n-->`;
|
|
49
60
|
}
|
|
50
61
|
/**
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
/** Token that opens the marker comment block. */
|
|
15
15
|
export declare const MARKER_BLOCK_OPEN = "oat-review-metadata";
|
|
16
|
-
export type ReviewInvocation = 'manual' | 'auto';
|
|
16
|
+
export type ReviewInvocation = 'manual' | 'auto' | 'gate';
|
|
17
17
|
export interface MarkerBlock {
|
|
18
18
|
/** Always `true` for an OAT provide-remote review; the discriminator. */
|
|
19
19
|
oat_provide_remote: true;
|
|
@@ -26,8 +26,13 @@ export interface MarkerBlock {
|
|
|
26
26
|
* `null` value) discriminates project rail from ad-hoc rail.
|
|
27
27
|
*/
|
|
28
28
|
oat_project?: string;
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* How the review was invoked. Missing and unknown values stay undefined so
|
|
31
|
+
* legacy markers cannot silently acquire lifecycle lineage.
|
|
32
|
+
*/
|
|
33
|
+
oat_review_invocation?: ReviewInvocation;
|
|
34
|
+
/** Exact gate target. Required before a gate marker can establish lineage. */
|
|
35
|
+
oat_gate_target?: string;
|
|
31
36
|
/** Forward-compat bag for unknown marker keys. Omitted when none seen. */
|
|
32
37
|
extras?: Record<string, string>;
|
|
33
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marker-parser.d.ts","sourceRoot":"","sources":["../../src/review-remote/marker-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAavD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"marker-parser.d.ts","sourceRoot":"","sources":["../../src/review-remote/marker-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAavD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,kBAAkB,EAAE,IAAI,CAAC;IACzB,oDAAoD;IACpD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,gBAAgB,CAAC;IACzC,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAWD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CA+EjE"}
|
|
@@ -26,6 +26,7 @@ const KNOWN_KEYS = new Set([
|
|
|
26
26
|
'oat_review_scope',
|
|
27
27
|
'oat_project',
|
|
28
28
|
'oat_review_invocation',
|
|
29
|
+
'oat_gate_target',
|
|
29
30
|
]);
|
|
30
31
|
/**
|
|
31
32
|
* Parse the first OAT marker block out of a posted-review body.
|
|
@@ -73,17 +74,27 @@ export function parseMarkerBlock(body) {
|
|
|
73
74
|
return null;
|
|
74
75
|
}
|
|
75
76
|
const invocationRaw = raw['oat_review_invocation'];
|
|
76
|
-
const oat_review_invocation = invocationRaw === '
|
|
77
|
+
const oat_review_invocation = invocationRaw === 'manual' ||
|
|
78
|
+
invocationRaw === 'auto' ||
|
|
79
|
+
invocationRaw === 'gate'
|
|
80
|
+
? invocationRaw
|
|
81
|
+
: undefined;
|
|
77
82
|
const block = {
|
|
78
83
|
oat_provide_remote: true,
|
|
79
84
|
oat_review_head_sha: headSha,
|
|
80
85
|
oat_review_scope: scope,
|
|
81
|
-
oat_review_invocation,
|
|
82
86
|
};
|
|
87
|
+
if (oat_review_invocation !== undefined) {
|
|
88
|
+
block.oat_review_invocation = oat_review_invocation;
|
|
89
|
+
}
|
|
83
90
|
// Project key existence (not a null value) discriminates the rail.
|
|
84
91
|
if ('oat_project' in raw && raw['oat_project'] !== '') {
|
|
85
92
|
block.oat_project = raw['oat_project'];
|
|
86
93
|
}
|
|
94
|
+
const gateTarget = raw['oat_gate_target']?.trim();
|
|
95
|
+
if (gateTarget) {
|
|
96
|
+
block.oat_gate_target = gateTarget;
|
|
97
|
+
}
|
|
87
98
|
const extras = {};
|
|
88
99
|
for (const [key, value] of Object.entries(raw)) {
|
|
89
100
|
if (!KNOWN_KEYS.has(key)) {
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
* range, so a rebased/force-pushed/shallow prior SHA never produces a
|
|
10
10
|
* misleading partial range.
|
|
11
11
|
*/
|
|
12
|
-
import type { ReviewInvocation } from './marker-parser.js';
|
|
12
|
+
import type { MarkerBlock, ReviewInvocation } from './marker-parser.js';
|
|
13
13
|
export type ReviewRail = 'ad-hoc' | 'project';
|
|
14
|
+
export type ReviewLineage = {
|
|
15
|
+
kind: 'lifecycle';
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'gate';
|
|
18
|
+
target: string;
|
|
19
|
+
};
|
|
20
|
+
export type ReviewInvocationKind = ReviewInvocation;
|
|
14
21
|
/** A prior provide-remote review, distilled from its parsed marker block. */
|
|
15
22
|
export interface PriorReview {
|
|
16
23
|
/** Full 40-char SHA recorded by the prior review. */
|
|
@@ -19,10 +26,34 @@ export interface PriorReview {
|
|
|
19
26
|
scope: string;
|
|
20
27
|
/** Project path — present only for project-rail reviews. */
|
|
21
28
|
project?: string;
|
|
22
|
-
invocation
|
|
29
|
+
invocation?: ReviewInvocationKind;
|
|
30
|
+
/** Review lineage; absent on legacy records, which are not eligible. */
|
|
31
|
+
lineage?: ReviewLineage;
|
|
23
32
|
/** ISO-8601 review submission timestamp, used for descending sort. */
|
|
24
33
|
submittedAt: string;
|
|
25
34
|
}
|
|
35
|
+
/** Provenance persisted on one tracked Reviews ledger row. */
|
|
36
|
+
export interface ReviewLedgerProvenance {
|
|
37
|
+
reviewedHead: string;
|
|
38
|
+
scope: string;
|
|
39
|
+
project?: string;
|
|
40
|
+
invocation?: string;
|
|
41
|
+
gateTarget?: string;
|
|
42
|
+
artifact: string;
|
|
43
|
+
submittedAt: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Normalize one parsed GitHub marker into a narrowing candidate. Unknown
|
|
47
|
+
* invocation and target-less gate markers preserve their metadata but carry no
|
|
48
|
+
* lineage, so tuple matching rejects them and fails open.
|
|
49
|
+
*/
|
|
50
|
+
export declare function priorReviewFromMarker(marker: MarkerBlock, submittedAt: string): PriorReview;
|
|
51
|
+
/**
|
|
52
|
+
* Normalize durable ledger provenance into the same candidate shape used by
|
|
53
|
+
* artifact-sourced reviews. Both sources then pass through {@link matchesTuple}
|
|
54
|
+
* and cannot drift into separate lineage predicates.
|
|
55
|
+
*/
|
|
56
|
+
export declare function priorReviewFromLedger(row: ReviewLedgerProvenance): PriorReview;
|
|
26
57
|
/**
|
|
27
58
|
* Narrow git surface needed by the guard. Callers pass a worktree-bound
|
|
28
59
|
* implementation (rich-context mode) or a fetch-capable one (diff-only mode);
|
|
@@ -35,6 +66,8 @@ export interface GitInvoker {
|
|
|
35
66
|
isAncestor(sha: string, head: string): Promise<boolean>;
|
|
36
67
|
/** `git fetch origin <sha>:<ref>` — fetch a single ref (diff-only mode). */
|
|
37
68
|
fetchRef(sha: string): Promise<boolean>;
|
|
69
|
+
/** Files changed in `<priorSha>..<headSha>`. */
|
|
70
|
+
changedFiles(priorSha: string, headSha: string): Promise<string[]>;
|
|
38
71
|
}
|
|
39
72
|
export interface NarrowingInput {
|
|
40
73
|
reviews: PriorReview[];
|
|
@@ -43,30 +76,33 @@ export interface NarrowingInput {
|
|
|
43
76
|
project: string | null;
|
|
44
77
|
/** Current scope token, or `ad-hoc`. */
|
|
45
78
|
scope: string;
|
|
79
|
+
/** Current review lineage, including the target for gate invocations. */
|
|
80
|
+
lineage: ReviewLineage;
|
|
46
81
|
/** Current PR HEAD SHA. */
|
|
47
82
|
headSha: string;
|
|
48
83
|
git: GitInvoker;
|
|
49
84
|
/** `--narrow` was passed explicitly: guard failure becomes a hard error. */
|
|
50
85
|
forceNarrow?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
|
|
86
|
+
/** Resolved preference: unset and true narrow; false forces full scope. */
|
|
87
|
+
narrowingPreference?: boolean;
|
|
53
88
|
/** Diff-only mode (no ephemeral worktree): fetch the single ref first. */
|
|
54
89
|
diffOnly?: boolean;
|
|
55
90
|
}
|
|
56
|
-
export type FullScopeReason = 'no-prior-review' | 'stale-sha';
|
|
91
|
+
export type FullScopeReason = 'narrowing-disabled' | 'no-prior-review' | 'stale-sha';
|
|
92
|
+
export type RangeClassification = 'empty' | 'bookkeeping-only' | 'substantive';
|
|
57
93
|
export interface NarrowRangeResult {
|
|
58
94
|
kind: 'narrow-range';
|
|
59
95
|
priorSha: string;
|
|
60
96
|
headSha: string;
|
|
61
|
-
|
|
62
|
-
|
|
97
|
+
classification: RangeClassification;
|
|
98
|
+
/** Why classification conservatively fell back, when enumeration failed. */
|
|
99
|
+
classificationReason?: 'changed-files-unavailable';
|
|
63
100
|
}
|
|
64
101
|
export interface FullScopeFallbackResult {
|
|
65
102
|
kind: 'full-scope-fallback';
|
|
66
103
|
reason: FullScopeReason;
|
|
67
104
|
/** The stale SHA that triggered the fallback, when applicable. */
|
|
68
105
|
priorSha?: string;
|
|
69
|
-
prompted: boolean;
|
|
70
106
|
}
|
|
71
107
|
export interface HardErrorResult {
|
|
72
108
|
kind: 'hard-error';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"narrowing.d.ts","sourceRoot":"","sources":["../../src/review-remote/narrowing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"narrowing.d.ts","sourceRoot":"","sources":["../../src/review-remote/narrowing.ts"],"names":[],"mappings":"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"}
|