@open-agent-toolkit/cli 0.3.15 → 0.3.16
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/NOTICES.md +48 -0
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/provider-sync/commands.md +21 -0
- package/assets/docs/reference/cli-reference.md +2 -1
- package/assets/docs/reference/configuration.md +10 -2
- package/assets/docs/skills/index.md +1 -0
- package/assets/docs/skills/recon.md +8 -5
- package/assets/docs/workflows/advanced/autonomy.md +10 -0
- package/assets/docs/workflows/advanced/workflow-gates.md +109 -10
- package/assets/docs/workflows/approvals-and-automation.md +3 -1
- package/assets/docs/workflows/projects/closeout/closeout-skills.md +62 -1
- package/assets/docs/workflows/projects/execution/implementation-execution.md +23 -3
- package/assets/docs/workflows/projects/execution/project-log.md +6 -0
- package/assets/docs/workflows/projects/lifecycle.md +7 -0
- package/assets/docs/workflows/projects/reviews/index.md +63 -1
- package/assets/docs/workflows/waves/wave-workflows.md +11 -3
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-autonomous/SKILL.md +1 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +172 -165
- package/assets/skills/oat-project-complete-auto/SKILL.md +388 -0
- package/assets/skills/oat-project-document/SKILL.md +1 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +172 -165
- package/assets/skills/oat-project-implement/SKILL.md +6 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +28 -1
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +172 -165
- package/assets/skills/oat-project-implement/references/docs/complexity-review-fallback.md +262 -0
- package/assets/skills/oat-project-implement/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-implement/references/phase-execution.md +18 -0
- package/assets/skills/oat-project-lite/SKILL.md +1 -1
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +172 -165
- package/assets/skills/oat-project-next/SKILL.md +19 -2
- package/assets/skills/oat-project-next/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-pr-final/SKILL.md +4 -3
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +172 -165
- package/assets/skills/oat-project-progress/SKILL.md +15 -1
- package/assets/skills/oat-project-progress/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-quick-start/SKILL.md +31 -3
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +172 -165
- package/assets/skills/oat-project-quick-start/references/docs/complexity-review-fallback.md +262 -0
- package/assets/skills/oat-project-quick-start/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-review-receive/SKILL.md +21 -3
- package/assets/skills/oat-project-review-receive/references/docs/complexity-review-fallback.md +262 -0
- package/assets/skills/oat-wave-execute/SKILL.md +49 -28
- package/assets/skills/oat-wave-program/SKILL.md +15 -6
- package/assets/skills/oat-wrap-up/SKILL.md +9 -3
- package/assets/skills/oat-wrap-up/references/report-template.md +1 -1
- package/assets/skills/recon/SKILL.md +1 -1
- package/assets/skills/recon/references/packet-contract.md +18 -3
- package/assets/skills/recon/scripts/lib/contracts.mjs +16 -2
- package/assets/skills/recon/scripts/reconcile-ledger.mjs +20 -10
- package/assets/skills/recon/scripts/render-packet.mjs +31 -3
- package/assets/skills/recon/scripts/validate-packet.mjs +5 -5
- package/assets/templates/state.md +7 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +13 -0
- package/dist/commands/gate/index.d.ts +5 -0
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +270 -9
- package/dist/commands/shared/frontmatter.d.ts +1 -1
- package/dist/commands/shared/frontmatter.d.ts.map +1 -1
- package/dist/commands/shared/frontmatter.js +1 -0
- package/dist/commands/state/generate.d.ts +1 -1
- package/dist/commands/state/generate.d.ts.map +1 -1
- package/dist/commands/state/generate.js +87 -8
- package/dist/commands/sync/apply.d.ts.map +1 -1
- package/dist/commands/sync/apply.js +31 -5
- package/dist/commands/sync/dry-run.d.ts.map +1 -1
- package/dist/commands/sync/dry-run.js +28 -11
- package/dist/commands/sync/sync.utils.d.ts +13 -0
- package/dist/commands/sync/sync.utils.d.ts.map +1 -1
- package/dist/commands/sync/sync.utils.js +21 -1
- package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-manifest.js +1 -0
- package/dist/config/oat-config.d.ts +1 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +3 -0
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +1 -0
- package/dist/drift/detector.js +6 -6
- package/dist/engine/compute-plan.d.ts.map +1 -1
- package/dist/engine/compute-plan.js +87 -2
- package/dist/engine/engine.types.d.ts +10 -1
- package/dist/engine/engine.types.d.ts.map +1 -1
- package/dist/engine/engine.types.js +4 -0
- package/dist/engine/execute-plan.d.ts.map +1 -1
- package/dist/engine/execute-plan.js +42 -5
- package/dist/engine/managed-copy-hash.d.ts +4 -3
- package/dist/engine/managed-copy-hash.d.ts.map +1 -1
- package/dist/engine/managed-copy-hash.js +4 -3
- package/dist/fs/assets.d.ts.map +1 -1
- package/dist/fs/assets.js +7 -1
- package/dist/manifest/hash.d.ts +8 -10
- package/dist/manifest/hash.d.ts.map +1 -1
- package/dist/ui/output.js +1 -1
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +13 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
-
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { link, mkdir, readdir, readFile, realpath, rmdir, rm, writeFile, } from 'node:fs/promises';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve, } from 'node:path';
|
|
6
6
|
import { buildCommandContext, } from '../../app/command-context.js';
|
|
@@ -46,6 +46,7 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
46
46
|
processEnv: process.env,
|
|
47
47
|
writeGateRunMarker,
|
|
48
48
|
removeGateRunMarker,
|
|
49
|
+
isProcessAlive,
|
|
49
50
|
writeGateProjectLogReceipt,
|
|
50
51
|
sleep: async (ms) => {
|
|
51
52
|
await new Promise((settle) => setTimeout(settle, ms));
|
|
@@ -92,9 +93,20 @@ const GATE_CHECK_TIMEOUT_MS = 5_000;
|
|
|
92
93
|
const GATE_EXEC_TIMEOUT_MS = 15 * 60 * 1_000;
|
|
93
94
|
const GATE_LIVENESS_INTERVAL_MS = 30_000;
|
|
94
95
|
const AMBIENT_ACTIVITY_ATTRIBUTION = 'not attributable to this gate child';
|
|
96
|
+
/**
|
|
97
|
+
* Run markers and duplicate-run claims share one directory. A gate exports it
|
|
98
|
+
* to its reviewer child as `OAT_GATE_RUN_MARKER_DIR`, so a nested gate finds
|
|
99
|
+
* its parent's claim even when the child's TMPDIR differs.
|
|
100
|
+
*/
|
|
101
|
+
function gateRunMarkerDir(env) {
|
|
102
|
+
const exported = env.OAT_GATE_RUN_MARKER_DIR?.trim();
|
|
103
|
+
return exported && isAbsolute(exported)
|
|
104
|
+
? exported
|
|
105
|
+
: join(tmpdir(), 'oat-gate-runs');
|
|
106
|
+
}
|
|
95
107
|
async function writeGateRunMarker(path, marker, warn) {
|
|
96
108
|
try {
|
|
97
|
-
await mkdir(
|
|
109
|
+
await mkdir(dirname(path), { recursive: true });
|
|
98
110
|
await writeFile(path, `${JSON.stringify(marker, null, 2)}\n`, 'utf8');
|
|
99
111
|
return true;
|
|
100
112
|
}
|
|
@@ -156,6 +168,171 @@ function warnWhenReceiptIsTracked(path, warn) {
|
|
|
156
168
|
warn(`Gate project log receipt ${path} is not ignored by git. Add a gate-receipts ignore rule for this projects root, or the receipt will be committed by the next repository-wide add.`);
|
|
157
169
|
}
|
|
158
170
|
}
|
|
171
|
+
function isProcessAlive(pid) {
|
|
172
|
+
try {
|
|
173
|
+
process.kill(pid, 0);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
// EPERM: the process exists but belongs to another user.
|
|
178
|
+
return isErrorCode(error, 'EPERM');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function isErrorCode(error, code) {
|
|
182
|
+
return (!!error &&
|
|
183
|
+
typeof error === 'object' &&
|
|
184
|
+
'code' in error &&
|
|
185
|
+
error.code === code);
|
|
186
|
+
}
|
|
187
|
+
function normalizeGateRunKey(value) {
|
|
188
|
+
return value?.trim().toLowerCase() ?? '';
|
|
189
|
+
}
|
|
190
|
+
function gateRunClaimPath(dir, identity) {
|
|
191
|
+
const key = createHash('sha256')
|
|
192
|
+
.update([
|
|
193
|
+
identity.projectRoot,
|
|
194
|
+
normalizeGateRunKey(identity.reviewType),
|
|
195
|
+
normalizeGateRunKey(identity.reviewScope),
|
|
196
|
+
].join('\0'))
|
|
197
|
+
.digest('hex')
|
|
198
|
+
.slice(0, 32);
|
|
199
|
+
return join(dir, `claim-${key}.lock`);
|
|
200
|
+
}
|
|
201
|
+
async function readGateRunClaim(path) {
|
|
202
|
+
let text;
|
|
203
|
+
try {
|
|
204
|
+
text = await readFile(path, 'utf8');
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (isErrorCode(error, 'ENOENT')) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
let record = null;
|
|
213
|
+
try {
|
|
214
|
+
record = rawRecord(JSON.parse(text));
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
record = null;
|
|
218
|
+
}
|
|
219
|
+
const holder = record &&
|
|
220
|
+
typeof record.runId === 'string' &&
|
|
221
|
+
typeof record.pid === 'number' &&
|
|
222
|
+
Number.isInteger(record.pid) &&
|
|
223
|
+
record.pid > 0
|
|
224
|
+
? { runId: record.runId, pid: record.pid, path }
|
|
225
|
+
: null;
|
|
226
|
+
return { text, holder };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Atomically claims the duplicate-run slot for one project root, review type,
|
|
230
|
+
* and scope. The claim content is written to a private file created with
|
|
231
|
+
* `wx`, then hard-linked to the shared claim path; `link` fails with EEXIST
|
|
232
|
+
* when another run holds the slot, so two simultaneous gates can never both
|
|
233
|
+
* acquire it and a reader never sees a half-written claim. A claim whose
|
|
234
|
+
* owner is dead, or which cannot be parsed, is replaced once. Destructive
|
|
235
|
+
* recovery and release share an exclusive mutation directory; recovery reads
|
|
236
|
+
* ownership again under that guard before removing anything. A crashed guard
|
|
237
|
+
* is deliberately not reclaimed automatically: competing cleanup cannot safely
|
|
238
|
+
* remove a replacement owner of the guard itself.
|
|
239
|
+
*/
|
|
240
|
+
async function acquireGateRunClaim(input) {
|
|
241
|
+
await mkdir(input.dir, { recursive: true });
|
|
242
|
+
const path = gateRunClaimPath(input.dir, input);
|
|
243
|
+
const content = `${JSON.stringify({
|
|
244
|
+
runId: input.runId,
|
|
245
|
+
pid: process.pid,
|
|
246
|
+
projectRoot: input.projectRoot,
|
|
247
|
+
reviewType: input.reviewType?.trim() || null,
|
|
248
|
+
reviewScope: input.reviewScope?.trim() || null,
|
|
249
|
+
claimedAt: new Date().toISOString(),
|
|
250
|
+
})}\n`;
|
|
251
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
252
|
+
const draft = join(input.dir, `.claim-${input.runId}.tmp`);
|
|
253
|
+
await writeFile(draft, content, { encoding: 'utf8', flag: 'wx' });
|
|
254
|
+
try {
|
|
255
|
+
await link(draft, path);
|
|
256
|
+
return { status: 'acquired', path };
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
if (!isErrorCode(error, 'EEXIST')) {
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
await rm(draft, { force: true });
|
|
265
|
+
}
|
|
266
|
+
const existing = await readGateRunClaim(path);
|
|
267
|
+
if (!existing) {
|
|
268
|
+
continue; // Released between the link and the read.
|
|
269
|
+
}
|
|
270
|
+
if (existing.holder && input.isProcessAlive(existing.holder.pid)) {
|
|
271
|
+
return { status: 'held', holder: existing.holder };
|
|
272
|
+
}
|
|
273
|
+
if (attempt > 0) {
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
const mutationPath = `${path}.mutation`;
|
|
277
|
+
try {
|
|
278
|
+
await mkdir(mutationPath);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
if (isErrorCode(error, 'EEXIST')) {
|
|
282
|
+
throw new Error(`Gate claim mutation is in progress at ${mutationPath}. Retry after it finishes; remove this guard only after verifying no gate is still using it.`, { cause: error });
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
// A faster recovery may have replaced the stale claim before we took
|
|
288
|
+
// the guard. Never move the shared path aside to discover its owner.
|
|
289
|
+
const current = await readGateRunClaim(path);
|
|
290
|
+
if (current?.holder && input.isProcessAlive(current.holder.pid)) {
|
|
291
|
+
return { status: 'held', holder: current.holder };
|
|
292
|
+
}
|
|
293
|
+
if (current) {
|
|
294
|
+
await rm(path);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
finally {
|
|
298
|
+
await rmdir(mutationPath);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const final = await readGateRunClaim(path);
|
|
302
|
+
return {
|
|
303
|
+
status: 'held',
|
|
304
|
+
holder: final?.holder ?? { runId: 'unknown', pid: 0, path },
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/** Removes a claim only while it still belongs to this run. */
|
|
308
|
+
async function releaseGateRunClaim(path, runId, warn) {
|
|
309
|
+
const mutationPath = `${path}.mutation`;
|
|
310
|
+
try {
|
|
311
|
+
await mkdir(mutationPath);
|
|
312
|
+
try {
|
|
313
|
+
const existing = await readGateRunClaim(path);
|
|
314
|
+
if (existing?.holder?.runId === runId) {
|
|
315
|
+
await rm(path);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
await rmdir(mutationPath);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
324
|
+
warn(`Unable to release gate run claim ${path}: ${detail}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async function resolveGateProjectRoot(repoRoot, projectPath) {
|
|
328
|
+
const absolute = resolve(repoRoot, projectPath);
|
|
329
|
+
try {
|
|
330
|
+
return await realpath(absolute);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return absolute;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
159
336
|
async function removeGateRunMarker(path, warn) {
|
|
160
337
|
try {
|
|
161
338
|
await rm(path);
|
|
@@ -476,7 +653,8 @@ function resolveGateExecTimeout(input) {
|
|
|
476
653
|
}
|
|
477
654
|
const scope = input.reviewScope?.trim().toLowerCase() ?? '';
|
|
478
655
|
if (reviewType === 'artifact') {
|
|
479
|
-
|
|
656
|
+
// Artifact and plan reviews read the full surface; they default to 30 minutes.
|
|
657
|
+
return { timeoutMs: 1_800_000, source: 'scope-default' };
|
|
480
658
|
}
|
|
481
659
|
if (reviewType === 'code') {
|
|
482
660
|
if (/^p\d+-t\d+$/.test(scope)) {
|
|
@@ -1523,11 +1701,15 @@ function writeReviewGateResult(context, payload) {
|
|
|
1523
1701
|
context.logger.info(payload.handoff);
|
|
1524
1702
|
}
|
|
1525
1703
|
function writeReviewGateExecutionFailure(context, payload) {
|
|
1526
|
-
const message = payload.
|
|
1527
|
-
? `Review did not
|
|
1528
|
-
: payload.
|
|
1529
|
-
? `Review did not
|
|
1530
|
-
:
|
|
1704
|
+
const message = payload.claimFailure
|
|
1705
|
+
? `Review did not start: duplicate-run exclusion could not be established (${payload.claimFailure}).`
|
|
1706
|
+
: payload.duplicateRun
|
|
1707
|
+
? `Review did not start: gate run ${payload.duplicateRun.runId} (pid ${payload.duplicateRun.pid}) is already reviewing ${payload.project} (${payload.duplicateRun.reviewType} review, scope ${payload.duplicateRun.scope}). Wait for it to finish, or remove its claim ${payload.duplicateRun.path} if that process is not a gate.`
|
|
1708
|
+
: payload.refusal
|
|
1709
|
+
? `Review did not complete: reviewer refused the headless route (${payload.refusal}).`
|
|
1710
|
+
: payload.timedOut
|
|
1711
|
+
? `Review did not complete: target ${payload.target} timed out after ${payload.timeoutMs}ms.`
|
|
1712
|
+
: `Review did not complete: target ${payload.target} exited with code ${payload.exitCode}.`;
|
|
1531
1713
|
if (context.json) {
|
|
1532
1714
|
context.logger.json({
|
|
1533
1715
|
status: 'review_failed',
|
|
@@ -1556,6 +1738,7 @@ function writeReviewGateExecutionFailure(context, payload) {
|
|
|
1556
1738
|
...(payload.activityEvidence
|
|
1557
1739
|
? { activityEvidence: payload.activityEvidence }
|
|
1558
1740
|
: {}),
|
|
1741
|
+
...(payload.recursion ? { recursion: payload.recursion } : {}),
|
|
1559
1742
|
message,
|
|
1560
1743
|
});
|
|
1561
1744
|
return;
|
|
@@ -2482,6 +2665,8 @@ async function disposeValidatedReviewArtifact(snapshot, identity, dependencies,
|
|
|
2482
2665
|
}
|
|
2483
2666
|
async function runReviewGate(prompt, options, context, dependencies) {
|
|
2484
2667
|
const runId = randomUUID();
|
|
2668
|
+
let recursion;
|
|
2669
|
+
let runClaimPath;
|
|
2485
2670
|
let runMarkerPath;
|
|
2486
2671
|
let runMarkerWritten = false;
|
|
2487
2672
|
let branchLocalGateCli;
|
|
@@ -2525,6 +2710,71 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2525
2710
|
rawPersisted,
|
|
2526
2711
|
});
|
|
2527
2712
|
const dispatchReport = buildGateDispatchReport(gateInvocation, options.reviewScope?.trim() || 'gate-review');
|
|
2713
|
+
// Claim the project, review type, and scope atomically before anything is
|
|
2714
|
+
// launched or logged, so a second live gate (a reviewer that launches its
|
|
2715
|
+
// own gate, or two simultaneous launches) is rejected.
|
|
2716
|
+
const runMarkerDir = gateRunMarkerDir(dependencies.processEnv);
|
|
2717
|
+
const projectRoot = await resolveGateProjectRoot(repoRoot, projectPath);
|
|
2718
|
+
let duplicateRun = null;
|
|
2719
|
+
let claimFailure;
|
|
2720
|
+
try {
|
|
2721
|
+
const claim = await acquireGateRunClaim({
|
|
2722
|
+
dir: runMarkerDir,
|
|
2723
|
+
projectRoot,
|
|
2724
|
+
reviewType: options.reviewType,
|
|
2725
|
+
reviewScope: options.reviewScope,
|
|
2726
|
+
runId,
|
|
2727
|
+
isProcessAlive: dependencies.isProcessAlive,
|
|
2728
|
+
});
|
|
2729
|
+
if (claim.status === 'acquired') {
|
|
2730
|
+
runClaimPath = claim.path;
|
|
2731
|
+
recursion = { decision: 'none' };
|
|
2732
|
+
}
|
|
2733
|
+
else {
|
|
2734
|
+
duplicateRun = claim.holder;
|
|
2735
|
+
recursion = { decision: 'rejected', matchedRunId: claim.holder.runId };
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
catch (error) {
|
|
2739
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2740
|
+
context.logger.warn(`Unable to claim this gate run in ${runMarkerDir}: ${detail}; review will not start.`);
|
|
2741
|
+
recursion = { decision: 'unchecked' };
|
|
2742
|
+
claimFailure = detail;
|
|
2743
|
+
}
|
|
2744
|
+
if (context.json) {
|
|
2745
|
+
dependencies.writeDiagnostic(`${JSON.stringify({
|
|
2746
|
+
type: 'gate-recursion',
|
|
2747
|
+
runId,
|
|
2748
|
+
...recursion,
|
|
2749
|
+
...(duplicateRun
|
|
2750
|
+
? { matchedPid: duplicateRun.pid, claimPath: duplicateRun.path }
|
|
2751
|
+
: {}),
|
|
2752
|
+
})}\n`);
|
|
2753
|
+
}
|
|
2754
|
+
if (duplicateRun || claimFailure) {
|
|
2755
|
+
writeReviewGateExecutionFailure(context, {
|
|
2756
|
+
runId,
|
|
2757
|
+
target: selected.id,
|
|
2758
|
+
project: projectPath,
|
|
2759
|
+
projectResolutionSource: reviewProject.source,
|
|
2760
|
+
exitCode: 1,
|
|
2761
|
+
...(duplicateRun
|
|
2762
|
+
? {
|
|
2763
|
+
duplicateRun: {
|
|
2764
|
+
...duplicateRun,
|
|
2765
|
+
reviewType: options.reviewType?.trim() || 'untyped',
|
|
2766
|
+
scope: options.reviewScope?.trim() || 'none',
|
|
2767
|
+
},
|
|
2768
|
+
}
|
|
2769
|
+
: {}),
|
|
2770
|
+
claimFailure,
|
|
2771
|
+
gateInvocation,
|
|
2772
|
+
dispatchReport,
|
|
2773
|
+
recursion,
|
|
2774
|
+
});
|
|
2775
|
+
process.exitCode = 1;
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2528
2778
|
postSelectionContext = {
|
|
2529
2779
|
project: projectPath,
|
|
2530
2780
|
projectResolutionSource: reviewProject.source,
|
|
@@ -2575,7 +2825,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2575
2825
|
: []),
|
|
2576
2826
|
prompt.join(' '),
|
|
2577
2827
|
]);
|
|
2578
|
-
runMarkerPath = join(
|
|
2828
|
+
runMarkerPath = join(runMarkerDir, `${runId}.json`);
|
|
2579
2829
|
runMarkerWritten = await dependencies.writeGateRunMarker(runMarkerPath, {
|
|
2580
2830
|
runId,
|
|
2581
2831
|
targetId: selected.id,
|
|
@@ -2583,6 +2833,8 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2583
2833
|
reviewType: options.reviewType?.trim() || null,
|
|
2584
2834
|
reviewScope: options.reviewScope?.trim() || null,
|
|
2585
2835
|
project: projectPath,
|
|
2836
|
+
projectRoot,
|
|
2837
|
+
pid: process.pid,
|
|
2586
2838
|
startedAt: new Date().toISOString(),
|
|
2587
2839
|
budgetMs: timeout.timeoutMs,
|
|
2588
2840
|
budgetSource: timeout.source,
|
|
@@ -2615,6 +2867,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2615
2867
|
OAT_GATE_CLI_PATH: branchLocalGateCli.cliPath,
|
|
2616
2868
|
OAT_GATE_CLI_ROOT: branchLocalGateCli.cliRoot,
|
|
2617
2869
|
OAT_GATE_ROUTE_RECEIPT_PATH: branchLocalGateCli.routeReceiptPath,
|
|
2870
|
+
OAT_GATE_RUN_MARKER_DIR: runMarkerDir,
|
|
2618
2871
|
},
|
|
2619
2872
|
}, timeout);
|
|
2620
2873
|
const routeReceipt = await dependencies.readGateRouteReceipt(branchLocalGateCli.routeReceiptPath, branchLocalGateCli.cliRoot, selected.target.runtime);
|
|
@@ -2648,6 +2901,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2648
2901
|
: {}),
|
|
2649
2902
|
gateInvocation,
|
|
2650
2903
|
dispatchReport,
|
|
2904
|
+
recursion,
|
|
2651
2905
|
});
|
|
2652
2906
|
process.exitCode = 1;
|
|
2653
2907
|
return true;
|
|
@@ -2693,6 +2947,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2693
2947
|
: {}),
|
|
2694
2948
|
gateInvocation,
|
|
2695
2949
|
dispatchReport,
|
|
2950
|
+
recursion,
|
|
2696
2951
|
});
|
|
2697
2952
|
process.exitCode = childExitCode;
|
|
2698
2953
|
return;
|
|
@@ -2719,6 +2974,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2719
2974
|
: {}),
|
|
2720
2975
|
gateInvocation,
|
|
2721
2976
|
dispatchReport,
|
|
2977
|
+
recursion,
|
|
2722
2978
|
});
|
|
2723
2979
|
process.exitCode = childExitCode;
|
|
2724
2980
|
return;
|
|
@@ -2850,6 +3106,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2850
3106
|
dispatchReport,
|
|
2851
3107
|
corroboration,
|
|
2852
3108
|
...(childResult.timedOut ? { lateCompletion: true } : {}),
|
|
3109
|
+
recursion,
|
|
2853
3110
|
});
|
|
2854
3111
|
// Record the disposition only once the envelope is written, so a failed
|
|
2855
3112
|
// emission cannot leave the log claiming a result no caller received.
|
|
@@ -2939,6 +3196,7 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2939
3196
|
? { lateCompletion: true }
|
|
2940
3197
|
: {}),
|
|
2941
3198
|
postSelectionRecovery: true,
|
|
3199
|
+
recursion,
|
|
2942
3200
|
});
|
|
2943
3201
|
buffered.flush();
|
|
2944
3202
|
recoveryEmitted = true;
|
|
@@ -2994,6 +3252,9 @@ async function runReviewGate(prompt, options, context, dependencies) {
|
|
|
2994
3252
|
if (runMarkerPath) {
|
|
2995
3253
|
await dependencies.removeGateRunMarker(runMarkerPath, (message) => context.logger.warn(message));
|
|
2996
3254
|
}
|
|
3255
|
+
if (runClaimPath) {
|
|
3256
|
+
await releaseGateRunClaim(runClaimPath, runId, (message) => context.logger.warn(message));
|
|
3257
|
+
}
|
|
2997
3258
|
}
|
|
2998
3259
|
finally {
|
|
2999
3260
|
if (projectLogFinalization && !projectLogFinalized) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const PROJECT_STATE_KINDS: readonly ['implementation', 'coordination'];
|
|
2
2
|
export declare const PROJECT_STATE_PHASES: readonly ['discovery', 'spec', 'design', 'plan', 'implement', 'decomposition'];
|
|
3
|
-
export declare const PROJECT_STATE_FRONTMATTER_FIELDS: readonly ['oat_kind', 'oat_parent', 'oat_siblings', 'oat_depends_on', 'oat_children', 'oat_inherited_context_revalidated', 'oat_phase', 'oat_phase_status', 'oat_status', 'oat_workflow_mode', 'oat_lifecycle', 'oat_current_task', 'oat_last_commit', 'oat_blockers', 'oat_hill_checkpoints', 'oat_hill_completed', 'oat_parallel_execution', 'oat_implement_exit_gate', 'oat_skill_gate_overrides', 'oat_pr_status', 'oat_pr_url', 'oat_project_created', 'oat_project_completed', 'oat_project_state_updated', 'oat_docs_updated', 'oat_generated', 'oat_template', 'oat_template_name'];
|
|
3
|
+
export declare const PROJECT_STATE_FRONTMATTER_FIELDS: readonly ['oat_kind', 'oat_parent', 'oat_siblings', 'oat_depends_on', 'oat_children', 'oat_inherited_context_revalidated', 'oat_phase', 'oat_phase_status', 'oat_status', 'oat_workflow_mode', 'oat_lifecycle', 'oat_current_task', 'oat_last_commit', 'oat_blockers', 'oat_hill_checkpoints', 'oat_hill_completed', 'oat_parallel_execution', 'oat_implement_exit_gate', 'oat_quick_start_gate', 'oat_skill_gate_overrides', 'oat_pr_status', 'oat_pr_url', 'oat_project_created', 'oat_project_completed', 'oat_project_state_updated', 'oat_docs_updated', 'oat_generated', 'oat_template', 'oat_template_name'];
|
|
4
4
|
export type ProjectStateKind = (typeof PROJECT_STATE_KINDS)[number];
|
|
5
5
|
export type ProjectStatePhase = (typeof PROJECT_STATE_PHASES)[number];
|
|
6
6
|
export type ProjectStateFrontmatterField = (typeof PROJECT_STATE_FRONTMATTER_FIELDS)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,6 @@ export interface GenerateStateResult {
|
|
|
18
18
|
recommendedStep: string;
|
|
19
19
|
recommendedReason: string;
|
|
20
20
|
}
|
|
21
|
-
export declare function phaseInHillList(phase: string,
|
|
21
|
+
export declare function phaseInHillList(phase: string, list: string | readonly string[]): boolean;
|
|
22
22
|
export declare function generateStateDashboard(options: GenerateStateOptions): Promise<GenerateStateResult>;
|
|
23
23
|
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAmB9E,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AA0ID,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAC/B,OAAO,CAET;AAomBD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
4
|
import { classifyLegacySyncedRecord, probeAuthoritativeCompletedRef, } from '../project/list.js';
|
|
5
5
|
import { defaultGitRunner } from '../project/sync/git.js';
|
|
6
6
|
import { listSyncedRecords } from '../project/sync/record.js';
|
|
7
7
|
import { buildSyncTarget } from '../project/sync/ref-sync.js';
|
|
8
8
|
import { probeSyncedTerminalRefs } from '../project/sync/resolve-target.js';
|
|
9
|
-
import { parseFrontmatterField } from '../shared/frontmatter.js';
|
|
9
|
+
import { getFrontmatterBlock, parseFrontmatterField, } from '../shared/frontmatter.js';
|
|
10
10
|
import { resolveProjectsRoot } from '../shared/oat-paths.js';
|
|
11
11
|
import { resolveScopeRoot } from '../shared/project-scope.js';
|
|
12
12
|
import { readOatLocalConfig } from '../../config/oat-config.js';
|
|
13
13
|
import { ensureDir, fileExists } from '../../fs/io.js';
|
|
14
|
+
import { evaluateQuickPlanReadiness, quickPlanNotReadyReason, } from '@open-agent-toolkit/control-plane';
|
|
15
|
+
import YAML from 'yaml';
|
|
14
16
|
const defaultGit = {
|
|
15
17
|
isGitRepo(root) {
|
|
16
18
|
try {
|
|
@@ -81,8 +83,53 @@ async function readActiveProject(repoRoot) {
|
|
|
81
83
|
result.status = 'active';
|
|
82
84
|
return result;
|
|
83
85
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Reads a HiLL phase list in any YAML form the router's parsed state accepts:
|
|
88
|
+
* a parsed array, or flow text with double, single, or no quotes.
|
|
89
|
+
*/
|
|
90
|
+
function toPhaseList(value) {
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
return value.map((item) => String(item).trim()).filter(Boolean);
|
|
93
|
+
}
|
|
94
|
+
if (typeof value !== 'string')
|
|
95
|
+
return [];
|
|
96
|
+
return value
|
|
97
|
+
.trim()
|
|
98
|
+
.replace(/^\[|\]$/g, '')
|
|
99
|
+
.split(',')
|
|
100
|
+
.map((item) => item
|
|
101
|
+
.trim()
|
|
102
|
+
.replace(/^(["'])(.*)\1$/, '$2')
|
|
103
|
+
.trim())
|
|
104
|
+
.filter(Boolean);
|
|
105
|
+
}
|
|
106
|
+
export function phaseInHillList(phase, list) {
|
|
107
|
+
return toPhaseList(list).includes(phase);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parses the HiLL lists from the state frontmatter as YAML, so block arrays
|
|
111
|
+
* are read whole (a line-oriented field read sees only the first item). A
|
|
112
|
+
* frontmatter block that is not valid YAML falls back to the textual lists.
|
|
113
|
+
*/
|
|
114
|
+
async function readHillLists(stateFile) {
|
|
115
|
+
try {
|
|
116
|
+
const block = getFrontmatterBlock(await readFile(stateFile, 'utf8'));
|
|
117
|
+
const parsed = block ? YAML.parse(block) : null;
|
|
118
|
+
if (parsed && typeof parsed === 'object') {
|
|
119
|
+
const record = parsed;
|
|
120
|
+
return {
|
|
121
|
+
checkpoints: toPhaseList(record.oat_hill_checkpoints),
|
|
122
|
+
completed: toPhaseList(record.oat_hill_completed),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Fall through to the textual read.
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
checkpoints: toPhaseList(await parseFrontmatterField(stateFile, 'oat_hill_checkpoints')),
|
|
131
|
+
completed: toPhaseList(await parseFrontmatterField(stateFile, 'oat_hill_completed')),
|
|
132
|
+
};
|
|
86
133
|
}
|
|
87
134
|
async function readProjectState(repoRoot, projectPath) {
|
|
88
135
|
const stateFile = join(repoRoot, projectPath, 'state.md');
|
|
@@ -101,12 +148,14 @@ async function readProjectState(repoRoot, projectPath) {
|
|
|
101
148
|
? await parseFrontmatterField(stateFile, 'oat_pause_reason')
|
|
102
149
|
: '';
|
|
103
150
|
const blockers = (await parseFrontmatterField(stateFile, 'oat_blockers')) || '[]';
|
|
104
|
-
const hillCheckpoints =
|
|
105
|
-
const hillCompleted = (await parseFrontmatterField(stateFile, 'oat_hill_completed')) || '[]';
|
|
151
|
+
const { checkpoints: hillCheckpoints, completed: hillCompleted } = await readHillLists(stateFile);
|
|
106
152
|
const workflowMode = (await parseFrontmatterField(stateFile, 'oat_workflow_mode')) ||
|
|
107
153
|
'spec-driven';
|
|
108
154
|
const docsUpdatedRaw = await parseFrontmatterField(stateFile, 'oat_docs_updated');
|
|
109
155
|
const docsUpdated = docsUpdatedRaw && docsUpdatedRaw !== 'null' ? docsUpdatedRaw : '';
|
|
156
|
+
const quickPlanReadiness = workflowMode === 'quick' && phase === 'plan'
|
|
157
|
+
? evaluateQuickPlanReadiness(await readOptionalFile(join(repoRoot, projectPath, 'plan.md')))
|
|
158
|
+
: null;
|
|
110
159
|
let hillStatus;
|
|
111
160
|
if (phaseInHillList(phase, hillCheckpoints)) {
|
|
112
161
|
hillStatus = phaseInHillList(phase, hillCompleted) ? 'passed' : 'pending';
|
|
@@ -128,8 +177,19 @@ async function readProjectState(repoRoot, projectPath) {
|
|
|
128
177
|
hillStatus,
|
|
129
178
|
workflowMode,
|
|
130
179
|
docsUpdated,
|
|
180
|
+
quickPlanReadiness,
|
|
131
181
|
};
|
|
132
182
|
}
|
|
183
|
+
async function readOptionalFile(path) {
|
|
184
|
+
try {
|
|
185
|
+
return await readFile(path, 'utf8');
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (error.code === 'ENOENT')
|
|
189
|
+
return null;
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
133
193
|
async function readKnowledgeStatus(repoRoot) {
|
|
134
194
|
const indexFile = join(repoRoot, '.oat', 'repo', 'knowledge', 'project-index.md');
|
|
135
195
|
const result = {
|
|
@@ -212,8 +272,10 @@ function computeNextStep(project, hasProjects, state) {
|
|
|
212
272
|
reason: 'Project is paused — open to resume',
|
|
213
273
|
};
|
|
214
274
|
}
|
|
215
|
-
// HiLL checkpoint gating
|
|
216
|
-
|
|
275
|
+
// HiLL checkpoint gating. A quick plan phase checks a pending checkpoint at
|
|
276
|
+
// any status before quick plan readiness, as the control-plane router does.
|
|
277
|
+
const quickPlanPhase = state.workflowMode === 'quick' && state.phase === 'plan';
|
|
278
|
+
if ((state.phaseStatus === 'complete' || quickPlanPhase) &&
|
|
217
279
|
phaseInHillList(state.phase, state.hillCheckpoints) &&
|
|
218
280
|
!phaseInHillList(state.phase, state.hillCompleted)) {
|
|
219
281
|
// Spec is no longer a discrete HiLL checkpoint in newly-scaffolded projects —
|
|
@@ -242,6 +304,23 @@ function computeNextStep(project, hasProjects, state) {
|
|
|
242
304
|
reason: 'Continue the promoted quick workflow',
|
|
243
305
|
};
|
|
244
306
|
}
|
|
307
|
+
// A quick plan phase routes by quick plan readiness at every status, as the
|
|
308
|
+
// control-plane router does: a plan that is not ready resumes quick-start in
|
|
309
|
+
// place, and a ready plan goes to implementation (readiness implies a
|
|
310
|
+
// complete plan whose `oat_ready_for` names it, so the router gives the
|
|
311
|
+
// explicit-pointer reason). The shared `plan:*` routes below serve the other
|
|
312
|
+
// modes. generate.test.ts pins parity with `recommendSkill`.
|
|
313
|
+
if (quickPlanPhase && state.quickPlanReadiness) {
|
|
314
|
+
return state.quickPlanReadiness.ready
|
|
315
|
+
? {
|
|
316
|
+
step: 'oat-project-implement',
|
|
317
|
+
reason: 'Current artifact is complete and explicitly points to the next skill',
|
|
318
|
+
}
|
|
319
|
+
: {
|
|
320
|
+
step: 'oat-project-quick-start',
|
|
321
|
+
reason: quickPlanNotReadyReason(state.quickPlanReadiness),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
245
324
|
// Workflow mode routing
|
|
246
325
|
const key = `${state.workflowMode}:${state.phase}:${state.phaseStatus}`;
|
|
247
326
|
const routeMap = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAoB3D,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EAExB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAoB3D,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EAExB,MAAM,cAAc,CAAC;AAwatB,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,aAAa,EAAE,EAC3B,YAAY,EAAE,uBAAuB,GACpC,OAAO,CAAC,IAAI,CAAC,CAiNf"}
|