agent-orchestrator-kit 0.13.0 → 0.14.1
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/CHANGELOG.md +26 -0
- package/README.md +24 -4
- package/bin/agent-orchestrator.js +469 -136
- package/bin/claude-cost-estimate.js +51 -0
- package/bin/spend-collect.js +76 -13
- package/package.json +2 -1
- package/profiles/generic/orchestrator.yaml +3 -0
- package/profiles/mvp/orchestrator.yaml +3 -0
- package/profiles/node/orchestrator.yaml +3 -0
- package/profiles/vue3/orchestrator.yaml +3 -0
- package/templates/.agents/commands/opsx-propose.md +6 -0
- package/templates/.agents/commands/opsx-review.md +35 -11
- package/templates/.agents/rules/session-handoff.mdc +2 -2
- package/templates/.agents/skills/agent-orchestration/SKILL.md +5 -4
- package/templates/.agents/skills/openspec-propose/SKILL.md +6 -0
- package/templates/.agents/subagents/openspec-guide.md +3 -2
- package/templates/.agents/subagents/session-handoff.md +2 -1
- package/templates/.agents/subagents/spec-architect.md +6 -1
- package/templates/.agents/subagents/spec-archiver.md +2 -1
- package/templates/.agents/subagents/spec-reviewer.md +5 -1
- package/templates/AGENTS.md +1 -1
- package/templates/orchestrator.yaml +3 -0
- package/templates/scripts/cursor-spend-collect.cjs +129 -67
- package/templates/scripts/sync-local-agent-skills.sh +27 -0
- package/templates/.cursor/memory.json +0 -11
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function numOrNull(value) {
|
|
2
|
+
if (value == null || value === '') return null;
|
|
3
|
+
const n = Number(value);
|
|
4
|
+
return Number.isFinite(n) ? n : null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const RATES = {
|
|
8
|
+
'claude-fable-5': { input: 10, cacheRead: 0.25, cacheWrite: 12.5, output: 50 },
|
|
9
|
+
'claude-opus': { input: 5, cacheRead: 0.5, cacheWrite: 6.25, output: 25 },
|
|
10
|
+
'claude-sonnet-5': { input: 2, cacheRead: 0.2, cacheWrite: 2.5, output: 10 },
|
|
11
|
+
'claude-sonnet-4-6': { input: 3, cacheRead: 0.3, cacheWrite: 3.75, output: 15 },
|
|
12
|
+
'claude-haiku-4-5': { input: 1, cacheRead: 0.1, cacheWrite: 1.25, output: 5 },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function ratesForModel(model) {
|
|
16
|
+
const id = String(model || '').toLowerCase();
|
|
17
|
+
return Object.entries(RATES)
|
|
18
|
+
.sort(([a], [b]) => b.length - a.length)
|
|
19
|
+
.find(([prefix]) => id.startsWith(prefix))?.[1] || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function estimateClaudeCostUsd({
|
|
23
|
+
model,
|
|
24
|
+
inputTokens,
|
|
25
|
+
cacheReadTokens,
|
|
26
|
+
cacheCreationTokens,
|
|
27
|
+
outputTokens,
|
|
28
|
+
} = {}) {
|
|
29
|
+
const input = numOrNull(inputTokens);
|
|
30
|
+
const cacheRead = numOrNull(cacheReadTokens);
|
|
31
|
+
const cacheWrite = numOrNull(cacheCreationTokens);
|
|
32
|
+
const output = numOrNull(outputTokens);
|
|
33
|
+
if (input == null && cacheRead == null && cacheWrite == null && output == null) return null;
|
|
34
|
+
const rates = ratesForModel(model);
|
|
35
|
+
const usd = rates
|
|
36
|
+
? ((input ?? 0) * rates.input
|
|
37
|
+
+ (cacheRead ?? 0) * rates.cacheRead
|
|
38
|
+
+ (cacheWrite ?? 0) * rates.cacheWrite
|
|
39
|
+
+ (output ?? 0) * rates.output) / 1e6
|
|
40
|
+
: (((input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0)) * 3 + (output ?? 0) * 15) / 1e6;
|
|
41
|
+
return Math.round(usd * 10000) / 10000;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function describeClaudeCostEstimate(args = {}) {
|
|
45
|
+
const usd = estimateClaudeCostUsd(args);
|
|
46
|
+
if (usd == null) return null;
|
|
47
|
+
return {
|
|
48
|
+
usd,
|
|
49
|
+
costSource: ratesForModel(args.model) ? 'api-estimate' : 'api-estimate-fallback',
|
|
50
|
+
};
|
|
51
|
+
}
|
package/bin/spend-collect.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
2
|
-
import { join, basename } from 'path';
|
|
2
|
+
import { join, basename, sep } from 'path';
|
|
3
3
|
import { homedir as osHomedir } from 'os';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import { listRecentAmpThreadIds } from './session-client.js';
|
|
6
6
|
import { formatUtcIso, parseFlexibleIso } from './metrics-time.js';
|
|
7
7
|
import { describeCursorCostEstimate } from './cursor-cost-estimate.js';
|
|
8
|
+
import { describeClaudeCostEstimate } from './claude-cost-estimate.js';
|
|
8
9
|
import { ampAgentMode, matchAmpUsageModel, parseAmpUsageDetails } from './amp-usage.js';
|
|
9
10
|
|
|
10
11
|
const PLATFORMS = ['cursor', 'claude', 'amp'];
|
|
@@ -216,17 +217,31 @@ function collectClaude({ cwd, windowStart, windowEnd, existing, env, homedir, no
|
|
|
216
217
|
notes.push('claude: project folder missing');
|
|
217
218
|
return sources;
|
|
218
219
|
}
|
|
219
|
-
let
|
|
220
|
+
let entries;
|
|
220
221
|
try {
|
|
221
|
-
|
|
222
|
+
entries = readdirSync(projectDir, { withFileTypes: true });
|
|
222
223
|
} catch {
|
|
223
224
|
notes.push('claude: cannot read project folder');
|
|
224
225
|
return sources;
|
|
225
226
|
}
|
|
227
|
+
const files = entries
|
|
228
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.jsonl'))
|
|
229
|
+
.map((entry) => join(projectDir, entry.name));
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
if (!entry.isDirectory()) continue;
|
|
232
|
+
const subagentsDir = join(projectDir, entry.name, 'subagents');
|
|
233
|
+
if (!existsSync(subagentsDir)) continue;
|
|
234
|
+
try {
|
|
235
|
+
for (const name of readdirSync(subagentsDir)) {
|
|
236
|
+
if (name.endsWith('.jsonl')) files.push(join(subagentsDir, name));
|
|
237
|
+
}
|
|
238
|
+
} catch {}
|
|
239
|
+
}
|
|
240
|
+
const bestById = new Map();
|
|
226
241
|
for (const file of files) {
|
|
227
242
|
let text;
|
|
228
243
|
try {
|
|
229
|
-
text = readFileSync(
|
|
244
|
+
text = readFileSync(file, 'utf-8');
|
|
230
245
|
} catch {
|
|
231
246
|
continue;
|
|
232
247
|
}
|
|
@@ -245,9 +260,18 @@ function collectClaude({ cwd, windowStart, windowEnd, existing, env, homedir, no
|
|
|
245
260
|
const id = message.id;
|
|
246
261
|
if (id == null || id === '') continue;
|
|
247
262
|
if (existing.has(String(id))) continue;
|
|
248
|
-
if (row.cwd !== cwd) continue;
|
|
263
|
+
if (row.cwd !== cwd && !String(row.cwd || '').startsWith(`${cwd}${sep}`)) continue;
|
|
249
264
|
if (!inWindow(row.timestamp, windowStart, windowEnd)) continue;
|
|
250
|
-
|
|
265
|
+
const cacheReadTokens = numOrNull(usage.cache_read_input_tokens ?? usage.cacheReadInputTokens);
|
|
266
|
+
const cacheCreationTokens = numOrNull(usage.cache_creation_input_tokens ?? usage.cacheCreationInputTokens);
|
|
267
|
+
const described = describeClaudeCostEstimate({
|
|
268
|
+
model: message.model,
|
|
269
|
+
inputTokens: usage.input_tokens ?? usage.inputTokens,
|
|
270
|
+
cacheReadTokens,
|
|
271
|
+
cacheCreationTokens,
|
|
272
|
+
outputTokens: usage.output_tokens ?? usage.outputTokens,
|
|
273
|
+
});
|
|
274
|
+
const record = sourceRecord({
|
|
251
275
|
id,
|
|
252
276
|
platform: 'claude',
|
|
253
277
|
model: message.model,
|
|
@@ -256,9 +280,21 @@ function collectClaude({ cwd, windowStart, windowEnd, existing, env, homedir, no
|
|
|
256
280
|
costUsd: claudeCostUsd(row, usage),
|
|
257
281
|
ampCredits: null,
|
|
258
282
|
at: row.timestamp,
|
|
259
|
-
|
|
283
|
+
cacheReadTokens,
|
|
284
|
+
costUsdEstimated: described?.usd ?? null,
|
|
285
|
+
costSource: described?.costSource ?? null,
|
|
286
|
+
});
|
|
287
|
+
const previous = bestById.get(String(id));
|
|
288
|
+
if (!previous || (record.totalTokens ?? 0) > (previous.totalTokens ?? 0)) {
|
|
289
|
+
bestById.set(String(id), record);
|
|
290
|
+
} else if ((record.totalTokens ?? 0) === (previous.totalTokens ?? 0)) {
|
|
291
|
+
const at = parseTime(record.at);
|
|
292
|
+
const previousAt = parseTime(previous.at);
|
|
293
|
+
if (Number.isFinite(at) && (!Number.isFinite(previousAt) || at < previousAt)) previous.at = record.at;
|
|
294
|
+
}
|
|
260
295
|
}
|
|
261
296
|
}
|
|
297
|
+
sources.push(...bestById.values());
|
|
262
298
|
return sources;
|
|
263
299
|
}
|
|
264
300
|
|
|
@@ -409,10 +445,14 @@ export function sourcesFromAmpThread(thread, ctx, fileName = '', via = null) {
|
|
|
409
445
|
return sources;
|
|
410
446
|
}
|
|
411
447
|
|
|
412
|
-
function collectAmp({ cwd, windowStart, windowEnd, existing, env, homedir, notes }) {
|
|
448
|
+
function collectAmp({ cwd, windowStart, windowEnd, existing, env, homedir, notes, ampThreadId, collectAll }) {
|
|
413
449
|
const root = ampRoot(env, homedir);
|
|
414
450
|
const threadsDir = join(root, 'threads');
|
|
415
451
|
const sources = [];
|
|
452
|
+
if (!ampThreadId && collectAll !== true) {
|
|
453
|
+
notes.push('amp: skipped local threads without thread id');
|
|
454
|
+
return sources;
|
|
455
|
+
}
|
|
416
456
|
if (!existsSync(threadsDir)) {
|
|
417
457
|
notes.push('amp: threads folder missing');
|
|
418
458
|
return sources;
|
|
@@ -432,6 +472,8 @@ function collectAmp({ cwd, windowStart, windowEnd, existing, env, homedir, notes
|
|
|
432
472
|
} catch {
|
|
433
473
|
continue;
|
|
434
474
|
}
|
|
475
|
+
const threadKey = thread && thread.id ? String(thread.id) : basename(file, '.json');
|
|
476
|
+
if (ampThreadId && threadKey !== String(ampThreadId)) continue;
|
|
435
477
|
sources.push(...sourcesFromAmpThread(thread, ctx, file));
|
|
436
478
|
}
|
|
437
479
|
return sources;
|
|
@@ -504,10 +546,10 @@ function collectAmpCli(ctx) {
|
|
|
504
546
|
if (id && !ids.includes(id)) ids.push(id);
|
|
505
547
|
};
|
|
506
548
|
push(ampThreadId);
|
|
507
|
-
if (ctx.listRecentAmpThreads
|
|
549
|
+
if (ctx.listRecentAmpThreads === true) {
|
|
508
550
|
push(ampCurrentThreadId(env));
|
|
509
551
|
}
|
|
510
|
-
if (!ids.length && ctx.listRecentAmpThreads
|
|
552
|
+
if (!ids.length && ctx.listRecentAmpThreads === true) {
|
|
511
553
|
for (const id of listRecentAmpThreadIds(ctx)) push(id);
|
|
512
554
|
}
|
|
513
555
|
const sources = [];
|
|
@@ -529,6 +571,10 @@ function collectAmpCli(ctx) {
|
|
|
529
571
|
...row,
|
|
530
572
|
model: matchAmpUsageModel(row.model, sourceModels),
|
|
531
573
|
}));
|
|
574
|
+
const alreadyBilled = ctx.existingThreadIds.has(id) && ctx.rebillThreadId !== id;
|
|
575
|
+
if (alreadyBilled) {
|
|
576
|
+
for (const row of usageModels) row.costUsd = null;
|
|
577
|
+
}
|
|
532
578
|
if (usage && usage.costUsd != null) {
|
|
533
579
|
for (const src of extracted) {
|
|
534
580
|
src.costSource = 'amp-usage';
|
|
@@ -537,7 +583,7 @@ function collectAmpCli(ctx) {
|
|
|
537
583
|
threads.push({
|
|
538
584
|
id,
|
|
539
585
|
agentMode,
|
|
540
|
-
costUsd: usage ? numOrNull(usage.costUsd) : null,
|
|
586
|
+
costUsd: usage && !alreadyBilled ? numOrNull(usage.costUsd) : null,
|
|
541
587
|
inputTokens: usage ? numOrNull(usage.inputTokens) : null,
|
|
542
588
|
outputTokens: usage ? numOrNull(usage.outputTokens) : null,
|
|
543
589
|
totalTokens: usage ? numOrNull(usage.totalTokens) : null,
|
|
@@ -708,7 +754,6 @@ function collectCursor({ cwd, windowStart, windowEnd, existing, existingSources,
|
|
|
708
754
|
if (!row || typeof row !== 'object') continue;
|
|
709
755
|
const id = row.id == null || row.id === '' ? null : String(row.id);
|
|
710
756
|
if (!id) continue;
|
|
711
|
-
if (existing.has(id)) continue;
|
|
712
757
|
if (filterId) {
|
|
713
758
|
const rowConversationId = row.conversationId == null || row.conversationId === ''
|
|
714
759
|
? ''
|
|
@@ -809,6 +854,9 @@ export function collectSpend(options = {}) {
|
|
|
809
854
|
? [...options.existingSourceIds]
|
|
810
855
|
: [],
|
|
811
856
|
);
|
|
857
|
+
const existingSourceTotals = options.existingSourceTotals && typeof options.existingSourceTotals === 'object'
|
|
858
|
+
? options.existingSourceTotals
|
|
859
|
+
: {};
|
|
812
860
|
const windowStart = options.windowStart;
|
|
813
861
|
const windowEnd = options.windowEnd;
|
|
814
862
|
const notes = [];
|
|
@@ -823,6 +871,7 @@ export function collectSpend(options = {}) {
|
|
|
823
871
|
windowEnd,
|
|
824
872
|
existing,
|
|
825
873
|
existingSources,
|
|
874
|
+
existingSourceTotals,
|
|
826
875
|
env,
|
|
827
876
|
homedir,
|
|
828
877
|
notes,
|
|
@@ -834,6 +883,9 @@ export function collectSpend(options = {}) {
|
|
|
834
883
|
usageAmpThread: options.usageAmpThread,
|
|
835
884
|
ampBin: options.ampBin,
|
|
836
885
|
timeoutMs: options.timeoutMs,
|
|
886
|
+
collectAll: options.collectAll === true || options.platforms == null,
|
|
887
|
+
existingThreadIds: new Set(options.existingThreadIds || []),
|
|
888
|
+
rebillThreadId: options.rebillThreadId || null,
|
|
837
889
|
};
|
|
838
890
|
let sources = [];
|
|
839
891
|
const ampThreads = [];
|
|
@@ -867,9 +919,20 @@ export function collectSpend(options = {}) {
|
|
|
867
919
|
notes.push('cursor: adapter failed');
|
|
868
920
|
}
|
|
869
921
|
}
|
|
922
|
+
sources = sources.filter((source) => {
|
|
923
|
+
if (!source || !existing.has(String(source.id))) return true;
|
|
924
|
+
if (!Object.hasOwn(existingSourceTotals, source.id)) return false;
|
|
925
|
+
return (numOrNull(source.totalTokens) ?? 0) > (numOrNull(existingSourceTotals[source.id]) ?? 0);
|
|
926
|
+
});
|
|
870
927
|
const { byPlatform, byModel } = aggregate(sources);
|
|
871
928
|
applyAmpThreadSpend(byPlatform, byModel, ampThreads);
|
|
872
|
-
|
|
929
|
+
const ids = [...new Set(sources.map((source) => String(source.id)))];
|
|
930
|
+
const totals = {};
|
|
931
|
+
for (const source of sources) {
|
|
932
|
+
const id = String(source.id);
|
|
933
|
+
totals[id] = Math.max(numOrNull(totals[id]) ?? 0, numOrNull(source.totalTokens) ?? 0);
|
|
934
|
+
}
|
|
935
|
+
return { sources, ids, totals, byPlatform, byModel, notes, ampThreads };
|
|
873
936
|
}
|
|
874
937
|
|
|
875
938
|
function applyAmpThreadSpend(byPlatform, byModel, threads) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-orchestrator-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "Universal AI agent orchestration kit for Cursor, Claude Code, and Amp Code — spec-driven OpenSpec pipeline, conductor subagents, durable session handoff, factory gates and MCP setup, cloud-agent handoff, and optional local Figma PAT setup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"files": [
|
|
36
36
|
"bin/",
|
|
37
37
|
"templates/",
|
|
38
|
+
"!templates/.cursor",
|
|
38
39
|
"profiles/",
|
|
39
40
|
"README.md",
|
|
40
41
|
"CHANGELOG.md",
|
|
@@ -11,6 +11,9 @@ pipeline:
|
|
|
11
11
|
max_active_changes: 1
|
|
12
12
|
archive_after_merge: true
|
|
13
13
|
task_contract: warn
|
|
14
|
+
# Paths gate-check treats as product code. Widen it when code lives
|
|
15
|
+
# outside src/ (e.g. "{src,lib,app}/") or the review gate never fires.
|
|
16
|
+
src_glob: "src/"
|
|
14
17
|
|
|
15
18
|
roles:
|
|
16
19
|
explorer:
|
|
@@ -16,6 +16,9 @@ pipeline:
|
|
|
16
16
|
archive_after_merge: false
|
|
17
17
|
quick_mode_enabled: true
|
|
18
18
|
task_contract: off
|
|
19
|
+
# Paths gate-check treats as product code. Widen it when code lives
|
|
20
|
+
# outside src/ (e.g. "{src,lib,app}/") or the review gate never fires.
|
|
21
|
+
src_glob: "src/"
|
|
19
22
|
|
|
20
23
|
roles:
|
|
21
24
|
explorer:
|
|
@@ -14,6 +14,9 @@ pipeline:
|
|
|
14
14
|
max_active_changes: 1
|
|
15
15
|
archive_after_merge: true
|
|
16
16
|
task_contract: warn
|
|
17
|
+
# Paths gate-check treats as product code. Widen it when code lives
|
|
18
|
+
# outside src/ (e.g. "{src,lib,app}/") or the review gate never fires.
|
|
19
|
+
src_glob: "src/"
|
|
17
20
|
|
|
18
21
|
roles:
|
|
19
22
|
explorer:
|
|
@@ -14,6 +14,9 @@ pipeline:
|
|
|
14
14
|
max_active_changes: 1
|
|
15
15
|
archive_after_merge: true
|
|
16
16
|
task_contract: warn
|
|
17
|
+
# Paths gate-check treats as product code. Widen it when code lives
|
|
18
|
+
# outside src/ (e.g. "{src,lib,app}/") or the review gate never fires.
|
|
19
|
+
src_glob: "src/"
|
|
17
20
|
|
|
18
21
|
roles:
|
|
19
22
|
explorer:
|
|
@@ -35,6 +35,12 @@ When ready to implement, run /opsx:apply
|
|
|
35
35
|
|
|
36
36
|
Each task must be self-contained for a blind implementer — executable without reading design.md. `Files:` paths must exist unless prefixed with `new file:`. Lint: `npx agent-orchestrator-kit gate-check --tasks <name>` (mode via `pipeline.task_contract: warn|strict|off`).
|
|
37
37
|
|
|
38
|
+
On re-propose after `review.md` Verdict REQUEST CHANGES, the conductor MUST pass `review.md` (path + verdict + Required Before Apply list) in the `spec-architect` spawn prompt and verify the report addresses every item; the parent MUST NOT itself edit proposal/design/specs/tasks. Exception: the structure-only propose trigger is the exact line
|
|
39
|
+
|
|
40
|
+
**Source:** gate-check
|
|
41
|
+
|
|
42
|
+
plus the absence of `## Checklist`; then fix only those gate-check errors.
|
|
43
|
+
|
|
38
44
|
**Steps**
|
|
39
45
|
|
|
40
46
|
1. **If no input provided, ask what they want to build**
|
|
@@ -38,11 +38,15 @@ npx agent-orchestrator-kit gate-check --review <name>
|
|
|
38
38
|
|
|
39
39
|
The script runs `openspec validate --strict --type change`, the task-contract lint (Files/Do/Done-when), the `Non-goals` / `Acceptance criteria` proposal sections check, and non-empty ADDED/MODIFIED/REMOVED delta-spec sections check. Add `--json` for a `{pass, errors[]}` report.
|
|
40
40
|
|
|
41
|
-
**If Tier 1 fails (exit ≠ 0):** do NOT spawn `spec-reviewer` and do NOT read the artifacts. Write `openspec/changes/<name>/review.md` with `Verdict: REQUEST CHANGES` listing the gate-check errors
|
|
41
|
+
**If Tier 1 fails (exit ≠ 0):** do NOT spawn `spec-reviewer` and do NOT read the artifacts. Write `openspec/changes/<name>/review.md` with `Verdict: REQUEST CHANGES` listing the gate-check errors. The T1 file MUST include this exact line:
|
|
42
|
+
|
|
43
|
+
**Source:** gate-check
|
|
44
|
+
|
|
45
|
+
The T1 `review.md` has no `## Checklist` section. This parent-written T1 `review.md` is an **accepted exception** to pipeline-subagents «parent MUST NOT write the verdict». Output the Request Changes verdict in chat. After an accepted RC one line MUST contain `Verdict: REQUEST CHANGES` and `/opsx:propose`. Go straight to Session Exit.
|
|
42
46
|
|
|
43
47
|
### 3. Tier 2 — spawn the specialist
|
|
44
48
|
|
|
45
|
-
Only after Tier 1 passes: spawn `spec-reviewer` with the complete change paths, project constraints, and the shortened checklist below. Require `## Subagent report: spec-reviewer`. Do not perform the review in the parent session.
|
|
49
|
+
Only after Tier 1 passes: spawn `spec-reviewer` with the complete change paths, project constraints, and the shortened checklist below. The parent MUST paste the full Tier 2 checklist from this file into the `spec-reviewer` prompt, including the Vue 3 items when `project.stack: vue3`. Before spawn, the parent MUST record whether `openspec/changes/<name>/review.md` already existed, and pass that fact plus the path in the spawn prompt. Require `## Subagent report: spec-reviewer`. Do not perform the review in the parent session.
|
|
46
50
|
|
|
47
51
|
### 4. Review checklist (Tier 2 — LLM-only)
|
|
48
52
|
|
|
@@ -68,6 +72,10 @@ Do NOT re-check what Tier 1 already covered (strict validation, contract field p
|
|
|
68
72
|
- [ ] Tasks reference concrete component/store paths under `src/`
|
|
69
73
|
- [ ] No scope creep into unrelated UI refactors
|
|
70
74
|
|
|
75
|
+
MUST NOT stop at the first blocking finding. Finish the full LLM checklist and a complete scan of proposal.md, design.md, tasks.md, all delta specs, and referenced main specs/repo paths before writing the verdict. One ✗ still means REQUEST CHANGES, but list every blocking issue of that pass.
|
|
76
|
+
|
|
77
|
+
Re-review MUST scan the same defect class — LLM-only only (another task whose `Do:` is not executable without design.md; another design behaviour with no delta requirement; another proposal↔tasks drift; another referenced heading/path that does not exist). Tier 1 classes NEVER enter this rescan. MUST NOT emit a one-item RC that names only the first leftover.
|
|
78
|
+
|
|
71
79
|
### 5. Write and report the verdict
|
|
72
80
|
|
|
73
81
|
#### If all ✓ (or only minor notes):
|
|
@@ -110,12 +118,16 @@ Create or update `openspec/changes/<name>/review.md`:
|
|
|
110
118
|
|
|
111
119
|
On **APPROVE**, `spec-reviewer` also writes `openspec/changes/<name>/apply-notes.md` (≤ 20 lines): critical constraints, pitfalls, what NOT to touch, verification commands. It is the distilled input for `/opsx:apply` and the **second allowed file** next to `review.md`.
|
|
112
120
|
|
|
113
|
-
For **REQUEST CHANGES**, write only `review.md` with `Verdict: REQUEST CHANGES` and the
|
|
121
|
+
For **REQUEST CHANGES**, write only `review.md` with `Verdict: REQUEST CHANGES` and the required sections: Checklist (each T2 item ✓/✗), Findings (Blocker / Major / Minor; empty buckets allowed), Required Before Apply (blocking only), Previous findings. Cosmetics stay out of Required Before Apply.
|
|
122
|
+
|
|
123
|
+
The `Previous findings` heading is ALWAYS present after any Tier 2 pass. If no prior `review.md` existed, the body is the literal line `none — first review cycle`. If a prior file existed, each prior Required Before Apply item → `resolved` | `unresolved` plus one-line evidence.
|
|
114
124
|
|
|
115
125
|
`review.md` (always) and `apply-notes.md` (on APPROVE) are the **only files** you may write during review (not `src/`, not `tasks.md` checkboxes).
|
|
116
126
|
|
|
117
127
|
The conductor verifies the subagent's `Status: done`, checks that `review.md` exists with the reported verdict (and `apply-notes.md` on APPROVE), and relays the result without editing them.
|
|
118
128
|
|
|
129
|
+
After Tier 2, the conductor MUST reject an RC `review.md` that lacks those headings or has an empty Checklist and MUST NOT rewrite the file; then re-spawn `spec-reviewer` once with the rejection reason and required headings; if the second file is still non-conforming, close with `## Blocked` naming the missing headings and next command `/opsx:review <name>` (a rejected file is not an accepted verdict). NEXT-AFTER-RC applies only to an accepted (schema-conforming) RC.
|
|
130
|
+
|
|
119
131
|
#### If any ✗:
|
|
120
132
|
|
|
121
133
|
```
|
|
@@ -123,25 +135,37 @@ The conductor verifies the subagent's `Status: done`, checks that `review.md` ex
|
|
|
123
135
|
|
|
124
136
|
**Change:** <name>
|
|
125
137
|
|
|
126
|
-
###
|
|
138
|
+
### Checklist
|
|
139
|
+
- proposal ↔ design ↔ tasks: ✓ or ✗
|
|
140
|
+
- Delta specs cover design: ✓ or ✗
|
|
141
|
+
- No conflicts with main specs: ✓ or ✗
|
|
142
|
+
- No scope creep vs Non-goals: ✓ or ✗
|
|
143
|
+
- Task self-sufficiency: ✓ or ✗
|
|
144
|
+
- Vue 3 items (when `project.stack: vue3`): ✓ or ✗ each
|
|
145
|
+
|
|
146
|
+
### Findings
|
|
147
|
+
|
|
148
|
+
#### Blocker
|
|
149
|
+
- <or empty>
|
|
127
150
|
|
|
128
|
-
####
|
|
129
|
-
-
|
|
151
|
+
#### Major
|
|
152
|
+
- <or empty>
|
|
130
153
|
|
|
131
|
-
####
|
|
132
|
-
-
|
|
154
|
+
#### Minor
|
|
155
|
+
- <or empty>
|
|
133
156
|
|
|
134
157
|
### Required Before Apply
|
|
135
|
-
<
|
|
158
|
+
- <blocking only>
|
|
136
159
|
|
|
137
|
-
|
|
160
|
+
### Previous findings
|
|
161
|
+
none — first review cycle
|
|
138
162
|
```
|
|
139
163
|
|
|
140
164
|
---
|
|
141
165
|
|
|
142
166
|
## Session Exit (HARD STOP)
|
|
143
167
|
|
|
144
|
-
Close via the canonical Session Exit protocol in `.agents/rules/session-handoff.mdc`.
|
|
168
|
+
Close via the canonical Session Exit protocol in `.agents/rules/session-handoff.mdc`. After an accepted REQUEST CHANGES one line contains `Verdict: REQUEST CHANGES` and `/opsx:propose`; after APPROVE `/opsx:apply <name>`. NEXT-AFTER-RC applies only to an accepted (schema-conforming) RC. Do not start the next phase in this chat.
|
|
145
169
|
|
|
146
170
|
## Guardrails
|
|
147
171
|
|
|
@@ -20,13 +20,13 @@ Agents (local or cloud) write session artifacts only to git-tracked paths — ne
|
|
|
20
20
|
|
|
21
21
|
## Session Exit (order)
|
|
22
22
|
1. The parent writes `openspec/changes/<name>/handoff.md` itself: Closed role, Change, Done, Decisions, Blocked, Next command, Next role, Attach, Subagents to spawn, Constraints, Runtime, Metrics.
|
|
23
|
-
2. Fill `## Metrics` before
|
|
23
|
+
2. Fill `## Metrics` before persist: `platform`, `model`, `input_tokens`, `output_tokens`, `cost_usd`, `amp_credits`, `spend_source`. `platform` and product-id `model` are required and never `unknown`; unknown numbers use `unknown`, never invented `0`. Do not set self-report when all numbers are unknown. Put decisions in `## Decisions`; only the CLI writes `decisions.md`.
|
|
24
24
|
3. `npx agent-orchestrator-kit handoff <name> --model <llm-product-id>` — require exit 0 (appends non-empty Decisions into append-only `openspec/changes/<name>/decisions.md`, upserts absolute-path Memory JSON, records the session into `openspec/changes/<name>/metrics.json`, prints the expanded prompt on stdout). `--model` is the LLM product id of this chat (`claude-opus-5`, `claude-fable-5`, `gpt-5.6-sol`, `cursor-grok-4.6-xhigh-fast`, `accounts/fireworks/models/glm-5p2`) — NEVER pass a Closed role, a subagent name, or an Amp **mode** (`low`, `medium`, `high`, `ultra`) as `--model`. The parent SHOULD still pass `--model`. The parent MUST NOT guess tokens. Persist collects spend for the client locked at restore (Amp: `amp threads export` + local threads; Cursor: hook file; Claude: JSONL). `--input-tokens` / `--output-tokens` / `--total-tokens` / `--cost-usd` override session-level totals only and do not wipe platform maps; they do not rewrite `## Metrics`. Optional `--platform cursor|claude|amp` or `AOK_PLATFORM`. Optional `--collect` runs all three adapters, not only the locked client. The same `npx agent-orchestrator-kit handoff <name>` works in Cursor, Claude Code, and Amp. `decisions.md` is the git canon of change decisions; Memory `Decision:*` is a file→Memory mirror only. Cloud sessions pass `--runtime cloud` (or set `AOK_RUNTIME=cloud` / `AOK_AGENT_ID` in the cloud-agent environment).
|
|
25
25
|
4. Spawn `session-handoff` in persist mode ONLY if step 3 failed (Amp: isolated `subagent-session-handoff`). Fallback, never routine.
|
|
26
26
|
5. Memory MCP is an optional mirror: if tools are available, update `Change:<name>`, `Handoff:<name>`, `Decision:*` in one call; unavailability never blocks closing.
|
|
27
27
|
6. Paste CLI stdout as one fenced block. First line `/opsx:…`. Body uses `project.agent_language`. Self-contained (Done/Decisions/Blocked/spawn/HARD STOP). No banner.
|
|
28
28
|
7. If runtime is cloud: after persist, `git add openspec/changes/<name>/` → `git commit` → `git push` → `npx agent-orchestrator-kit handoff <name> --cloud-check` (exit 0 required). Closing without this is an incomplete handoff. Persist prints these steps on stderr; the CLI never runs `git commit` / `git push`.
|
|
29
|
-
8. Stop. Next role = new chat.
|
|
29
|
+
8. Stop. Next role and any out-of-OpenSpec hotfix = new chat. Never run full persist twice; regenerate the prompt only with `handoff <name> --no-metrics`.
|
|
30
30
|
|
|
31
31
|
## Archive exception
|
|
32
32
|
`npx agent-orchestrator-kit archive <name>` writes the final `handoff.md` (`next_command: none`) in the archive folder and upserts memory itself. After a successful archive no fenced next-prompt is required — the pipeline is complete.
|
|
@@ -109,7 +109,7 @@ Before apply, check `.agents/orchestrator.yaml`:
|
|
|
109
109
|
- `require_spec_review: true` → apply MUST find `review.md` with `Verdict: APPROVE` or Approve in session
|
|
110
110
|
- `require_spec_review: false` → apply allowed directly (mvp / quick mode)
|
|
111
111
|
|
|
112
|
-
If Request Changes — fix
|
|
112
|
+
If Request Changes — run `/opsx:propose <name>` to fix the punch list, then a new `/opsx:review`.
|
|
113
113
|
|
|
114
114
|
This is no longer only a chat convention: `npx agent-orchestrator-kit gate-check` runs in CI (both `agent-verify.yml` fragments) and fails the pipeline if `src/` changed without an approved `review.md` — a forgotten or skipped review is caught at merge time, not just at apply time. When `require_design_brief: true`, the same command also requires `design-brief.md` (or `Design: none` in `proposal.md`).
|
|
115
115
|
|
|
@@ -146,11 +146,11 @@ Archive is one deterministic CLI call — `npx agent-orchestrator-kit archive <n
|
|
|
146
146
|
|
|
147
147
|
**End of each session (HARD STOP — you are NOT done):**
|
|
148
148
|
1. Write `openspec/changes/<name>/handoff.md` in the parent using the template below, including `## Metrics`.
|
|
149
|
-
2. Fill `## Metrics`
|
|
149
|
+
2. Fill `## Metrics` before persist. `platform` and product-id `model` are required and never `unknown`; unknown numbers use `unknown`, never invented `0`. Put decisions in `## Decisions`; only the CLI writes `decisions.md`.
|
|
150
150
|
3. Run `npx agent-orchestrator-kit handoff <name> --model <llm-product-id>` and require exit 0. `--model` is the LLM product id of this chat (`claude-opus-5`, `claude-fable-5`, `gpt-5.6-sol`, `cursor-grok-4.6-xhigh-fast`) — NEVER pass a Closed role (`Architect`, `Implementer`, `Explorer`) or a subagent name (`spec-architect`, `session-handoff`) as `--model`. The parent SHOULD still pass `--model`. The parent MUST NOT guess tokens. `--input-tokens` / `--output-tokens` / `--total-tokens` / `--cost-usd` override session-level totals only and do not wipe platform maps or rewrite `## Metrics`. Optional `--platform cursor|claude|amp` or `AOK_PLATFORM`. Optional `--collect` also runs local spend adapters. The same command works in Cursor, Claude Code, and Amp and MUST NOT require Cursor SDK, a Claude `/cost` parser, or an Amp billing API as a required step. The CLI appends non-empty Decisions into append-only `openspec/changes/<name>/decisions.md` (the git canon), upserts Memory JSON with an absolute path (`Decision:*` is a file→Memory mirror only), and prints the expanded self-contained prompt on stdout. Spawn `session-handoff` in persist mode ONLY if this CLI step failed.
|
|
151
151
|
4. If Memory MCP tools are available, mirror `Change:<name>`, `Handoff:<name>`, and new `Decision:<topic>` entities in one call — optional; its absence never blocks closing.
|
|
152
152
|
5. Paste the CLI stdout as one fenced next-session prompt. First line is `/opsx:<next> <name>`; body uses `project.agent_language`; keep Done/Decisions/Blocked/spawn/HARD STOP complete. No banner. Do not emit a thin “read Memory” stub.
|
|
153
|
-
6. Do not start the next phase in this chat. If apply, include build/lint status in
|
|
153
|
+
6. Do not start the next phase in this chat. Never run full persist twice; regenerate the prompt only with `handoff <name> --no-metrics`. Any out-of-OpenSpec hotfix after persist requires a new chat. If apply, include build/lint status in Done.
|
|
154
154
|
|
|
155
155
|
`handoff.md` template:
|
|
156
156
|
|
|
@@ -257,12 +257,13 @@ Before declaring a session closed, the parent MUST, in order: (1) write `openspe
|
|
|
257
257
|
| No archive after merge | Next propose has stale domain specs |
|
|
258
258
|
| Strong model on lint fixes | 5–10x cost with no quality gain |
|
|
259
259
|
| Skip Memory MCP / skip `handoff` CLI | Next thread has no context; Amp looks like it “ignored the rules” |
|
|
260
|
+
| one-finding review loop | fragments defects across many propose/review sessions |
|
|
260
261
|
|
|
261
262
|
## Metrics (health check per change)
|
|
262
263
|
|
|
263
264
|
- Sessions: 4–8 (not 1 marathon, not 20 micro-sessions)
|
|
264
265
|
- Apply iterations to PR: ≤ 2
|
|
265
|
-
- Spec review loops: ≤ 1
|
|
266
|
+
- Spec review discovery loops: ≤ 2 (optional Tier 1 structural RC plus one semantic Tier 2 RC; a confirmation APPROVE after an exhaustive propose does not count as a discovery loop)
|
|
266
267
|
- Tasks rework: ≤ 10%
|
|
267
268
|
|
|
268
269
|
If apply iterations > 2 → problem is in Architect or Reviewer, not Implementer.
|
|
@@ -35,6 +35,12 @@ When ready to implement, run /opsx:apply
|
|
|
35
35
|
|
|
36
36
|
Each task must be self-contained for a blind implementer — executable without reading design.md. `Files:` paths must exist unless prefixed with `new file:`. Lint: `npx agent-orchestrator-kit gate-check --tasks <name>` (mode via `pipeline.task_contract: warn|strict|off`).
|
|
37
37
|
|
|
38
|
+
On re-propose after `review.md` Verdict REQUEST CHANGES, the conductor MUST pass `review.md` (path + verdict + Required Before Apply list) in the `spec-architect` spawn prompt and verify the report addresses every item; the parent MUST NOT itself edit proposal/design/specs/tasks. Exception: the structure-only propose trigger is the exact line
|
|
39
|
+
|
|
40
|
+
**Source:** gate-check
|
|
41
|
+
|
|
42
|
+
plus the absence of `## Checklist`; then fix only those gate-check errors.
|
|
43
|
+
|
|
38
44
|
**Steps**
|
|
39
45
|
|
|
40
46
|
1. **If no clear input provided, ask what they want to build**
|
|
@@ -15,8 +15,9 @@ On every invocation:
|
|
|
15
15
|
4. Map what you find to the correct next command:
|
|
16
16
|
- No `proposal.md` yet → `/opsx:propose <name>`
|
|
17
17
|
- `require_design_brief: true`, UI-touching change, no `design-brief.md`, no `Design: none` in `proposal.md` → `/opsx:design <name>`
|
|
18
|
-
- `proposal.md` exists but no `review.md`
|
|
19
|
-
- `review.md`
|
|
18
|
+
- `proposal.md` exists but no `review.md` → `/opsx:review <name>` (must run in a separate read-only session)
|
|
19
|
+
- `review.md` contains `Verdict: REQUEST CHANGES` → `/opsx:propose <name>`
|
|
20
|
+
- `review.md` has `Verdict: APPROVE` but `tasks.md` has unchecked `- [ ]` items → `/opsx:apply <name>`
|
|
20
21
|
- All tasks `[x]` and review approved → ready to archive, suggest `/opsx:archive <name>` (or note that GitLab/GitHub CI auto-archives after merge if `archive_after_merge: true`)
|
|
21
22
|
5. If a CI gate (`gate-check`, `verify-openspec-pr`) is failing, reproduce the check locally (`npx agent-orchestrator-kit gate-check <name>`, `npm run verify:openspec:pr`) and quote the exact failing reason from its output — don't guess.
|
|
22
23
|
6. If `pipeline.max_active_changes` is exceeded, say so explicitly and name which changes are over the limit.
|
|
@@ -22,7 +22,7 @@ Use when the parent's restore failed (CLI restore and handoff.md both unavailabl
|
|
|
22
22
|
Use when the parent's persist failed (`npx agent-orchestrator-kit handoff <name>` did not exit 0). A session is not closed until persist succeeds.
|
|
23
23
|
|
|
24
24
|
1. Write or update `openspec/changes/<name>/handoff.md` with every required section: Closed role, Change, Done, Decisions, Blocked, Next command, Next role, Attach, Subagents to spawn, Constraints, Runtime, Metrics.
|
|
25
|
-
2. Fill `## Metrics`
|
|
25
|
+
2. Fill `## Metrics` before persist. `platform` and product-id `model` are required and never `unknown`; unknown numbers use `unknown`, never invented `0`. Put decisions in `## Decisions`; only the CLI writes `decisions.md`.
|
|
26
26
|
3. Run `npx agent-orchestrator-kit handoff <name> --model <llm-product-id>` and require exit 0. `--model` is the LLM product id of this chat (`claude-opus-5`, `claude-fable-5`, `gpt-5.6-sol`, `cursor-grok-4.6-xhigh-fast`) — NEVER pass a Closed role (`Architect`, `Implementer`, `Explorer`) or a subagent name (`spec-architect`, `session-handoff`) as `--model`. The parent SHOULD still pass `--model`. The parent MUST NOT guess tokens. `--input-tokens` / `--output-tokens` / `--total-tokens` / `--cost-usd` override session-level totals only and do not wipe platform maps. Optional `--platform cursor|claude|amp` or `AOK_PLATFORM`. Optional `--collect` also runs local spend adapters. The same command works in Cursor, Claude Code, and Amp and MUST NOT require Cursor SDK, a Claude `/cost` parser, or an Amp billing API as a required step. This appends non-empty Decisions into append-only `openspec/changes/<name>/decisions.md` (git canon), upserts `.cursor/memory.json` using an absolute path (`Decision:*` mirrors that file, never the reverse), and prints the expanded next-session prompt on stdout. Cloud sessions pass `--runtime cloud` (or `AOK_RUNTIME` / `AOK_AGENT_ID`).
|
|
27
27
|
4. If Memory MCP tools are available, also create/update `Change:<name>`, `Handoff:<name>`, and each `Decision:<topic>` to match `decisions.md`. MCP failure is not a blocker after the CLI succeeds.
|
|
28
28
|
5. Put the CLI stdout prompt (first line `/opsx:…`) into **Next prompt** unchanged. Do not shorten it. Do not add a banner.
|
|
@@ -33,6 +33,7 @@ Use when the parent's persist failed (`npx agent-orchestrator-kit handoff <name>
|
|
|
33
33
|
- Write session artifacts only to git-tracked paths (never `/tmp`, never gitignored caches).
|
|
34
34
|
- Do NOT edit `src/`, tests, main specs, `tasks.md` checkboxes, or phase artifacts (`proposal.md`, `review.md`, `design-brief.md`) except `handoff.md`.
|
|
35
35
|
- Do NOT start the next OpenSpec phase.
|
|
36
|
+
- Never run full persist twice. Regenerate a prompt only with `handoff <name> --no-metrics`; after persist, move any next-role or out-of-OpenSpec work to a new chat.
|
|
36
37
|
- Do NOT return a thin prompt. The next thread must be able to run if Memory MCP is ignored.
|
|
37
38
|
- Stop as blocked when the change name or next command cannot be resolved.
|
|
38
39
|
|
|
@@ -11,7 +11,12 @@ Workflow:
|
|
|
11
11
|
2. Create or update `proposal.md`, `design.md`, `specs/<capability>/spec.md`, and `tasks.md` using the repository's OpenSpec schema and conventions.
|
|
12
12
|
3. Keep requirements testable: each requirement uses SHALL/MUST language and includes concrete scenarios.
|
|
13
13
|
4. Make tasks ordered, independently verifiable, and traceable to the design and delta specs. Every task MUST follow the task contract: indented `Files:` (existing paths, or `new file:` prefix for new ones), `Do:` (concrete change, no vague wording like "as needed" / "if necessary" / "as appropriate"), and `Done-when:` (verifiable condition or command). Each task must be self-contained for a blind implementer without reading design.md.
|
|
14
|
-
5.
|
|
14
|
+
5. On re-propose after REQUEST CHANGES, the architect MUST read `review.md`, fix every Required Before Apply item, and re-scan the same defect class in proposal.md, design.md, tasks.md, and all delta specs (LLM-only classes only: another task whose `Do:` is not executable without design.md; another design behaviour with no delta requirement; another proposal↔tasks drift; another referenced heading/path that does not exist); do not stop after the listed items; Tier 1 classes NEVER enter this rescan. Exception: the structure-only propose trigger is the exact line
|
|
15
|
+
|
|
16
|
+
**Source:** gate-check
|
|
17
|
+
|
|
18
|
+
plus the absence of `## Checklist`; then fix only those gate-check errors.
|
|
19
|
+
6. Report which validation command the conductor should run; do not cross into review or implementation.
|
|
15
20
|
|
|
16
21
|
Rules:
|
|
17
22
|
|
|
@@ -9,7 +9,7 @@ Workflow:
|
|
|
9
9
|
|
|
10
10
|
1. Read `.agents/orchestrator.yaml`, the complete change, review verdict, task state, and verification/merge evidence supplied by the conductor.
|
|
11
11
|
2. Refuse to archive unless required review is approved, all tasks are complete, and the configured merge/CI gate is satisfied.
|
|
12
|
-
3. Fill `## Metrics`
|
|
12
|
+
3. Fill `## Metrics` with Archiver-only numbers before archive. `platform` and product-id `model` are required and never `unknown`; unknown numbers use `unknown`, never invented `0`. Put decisions in `## Decisions`; only the CLI writes `decisions.md`. Do not copy the apply session.
|
|
13
13
|
4. Run `npx agent-orchestrator-kit archive <name>` so delta requirements are merged into main specs, the change moves to the dated archive path, and stdout prints the change-wide metrics summary (by phase / by platform / by model).
|
|
14
14
|
5. Run strict validation after the move and report the resulting archive path and modified main specs.
|
|
15
15
|
|
|
@@ -18,6 +18,7 @@ Rules:
|
|
|
18
18
|
- Do NOT edit `src/`, tests, CI, or implementation files.
|
|
19
19
|
- Do NOT add new features, redesign requirements, or repair incomplete implementation during archive.
|
|
20
20
|
- Do NOT manually discard delta requirements to make validation pass.
|
|
21
|
+
- Do not run archive/persist twice; after success, stop and move out-of-OpenSpec work to a new chat. Prompt regeneration before archive uses `handoff <name> --no-metrics`.
|
|
21
22
|
- If archive prerequisites are missing, return `blocked` with the exact unmet gate.
|
|
22
23
|
|
|
23
24
|
Return exactly this report contract:
|
|
@@ -13,7 +13,8 @@ Workflow:
|
|
|
13
13
|
- conflicts with existing `openspec/specs/` requirements;
|
|
14
14
|
- scope creep vs proposal Non-goals;
|
|
15
15
|
- task self-sufficiency: a blind implementer can execute each task from Files/Do/Done-when alone, without design.md.
|
|
16
|
-
|
|
16
|
+
MUST NOT stop at the first blocking finding. Finish the full LLM checklist and a complete artifact scan of proposal.md, design.md, tasks.md, all delta specs, and referenced main specs/repo paths before writing the verdict. One ✗ still means REQUEST CHANGES, but list every blocking issue of that pass.
|
|
17
|
+
3. MUST read the existing `review.md` before overwriting it. Write `review.md` with findings ordered by severity and exactly one verdict: `APPROVE` or `REQUEST CHANGES`. REQUEST CHANGES and re-review MUST use headings Checklist, Findings (Blocker / Major / Minor), Required Before Apply, Previous findings.
|
|
17
18
|
4. On APPROVE, also write `apply-notes.md` (≤ 20 lines): critical constraints, pitfalls, what NOT to touch, verification commands. It is the second and last file you may write.
|
|
18
19
|
5. Approve only when artifacts are implementable without material guessing.
|
|
19
20
|
|
|
@@ -23,6 +24,9 @@ Rules:
|
|
|
23
24
|
- Do NOT implement fixes found during review.
|
|
24
25
|
- Do NOT substitute for `code-reviewer`; that agent reviews the implementation diff after apply.
|
|
25
26
|
- Do NOT approve based only on Tier 1 passing; verify semantics and repository references.
|
|
27
|
+
- The `Previous findings` heading is ALWAYS present after any Tier 2 pass. If no prior `review.md` existed, the body is the literal line `none — first review cycle`. If a prior file existed, each prior Required Before Apply item → `resolved` | `unresolved` plus one-line evidence.
|
|
28
|
+
- Cosmetics stay out of Required Before Apply.
|
|
29
|
+
- Later review MUST carry prior Required Before Apply into Previous findings and MUST still scan the same defect class (LLM-only only: another task whose `Do:` is not executable without design.md; another design behaviour with no delta requirement; another proposal↔tasks drift; another referenced heading/path that does not exist). Tier 1 classes NEVER enter this rescan. MUST NOT emit a one-item RC that lists only the first leftover.
|
|
26
30
|
|
|
27
31
|
Return exactly this report contract:
|
|
28
32
|
|
package/templates/AGENTS.md
CHANGED
|
@@ -25,7 +25,7 @@ Routing table, HARD STOP, and CLI forms: `.agents/rules/` (`agent-orchestration`
|
|
|
25
25
|
|
|
26
26
|
Session Start / Exit are **parent-driven** — canonical protocol in `.agents/rules/session-handoff.mdc`. Start: `status` → `handoff --restore` → `handoff.md` fallback. Exit HARD STOP: parent writes `handoff.md` including `## Metrics` (use `unknown` when a value is missing) → `npx agent-orchestrator-kit handoff <name>` (exit 0; optional `--collect`) → paste the CLI `/opsx:*` prompt. `session-handoff` subagent = fallback only. Do not start the next phase here.
|
|
27
27
|
|
|
28
|
-
Quality gates: `gate-check --tasks <name>` lints the task contract (Files/Do/Done-when, `pipeline.task_contract: warn|strict|off`); `gate-check --review <name>` is deterministic Tier 1 of review — spec-reviewer (Tier 2) is spawned only after it passes and writes `apply-notes.md` on APPROVE.
|
|
28
|
+
Quality gates: `gate-check --tasks <name>` lints the task contract (Files/Do/Done-when, `pipeline.task_contract: warn|strict|off`); `gate-check --review <name>` is deterministic Tier 1 of review — spec-reviewer (Tier 2) is spawned only after it passes and writes `apply-notes.md` on APPROVE. Spec review discovery loops ≤ 2 (optional Tier 1 structural RC plus one semantic Tier 2 RC; a confirmation APPROVE after an exhaustive propose does not count). Anti-pattern: one-finding review loop — fragments defects across many propose/review sessions.
|
|
29
29
|
|
|
30
30
|
## Hard rules
|
|
31
31
|
- One active change (unless mvp profile).
|
|
@@ -13,6 +13,9 @@ pipeline:
|
|
|
13
13
|
max_active_changes: 1
|
|
14
14
|
archive_after_merge: true
|
|
15
15
|
task_contract: warn
|
|
16
|
+
# Paths gate-check treats as product code. Widen it when code lives
|
|
17
|
+
# outside src/ (e.g. "{src,lib,app}/") or the review gate never fires.
|
|
18
|
+
src_glob: "src/"
|
|
16
19
|
|
|
17
20
|
roles:
|
|
18
21
|
explorer:
|