specrails-core 5.3.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime/capabilities.d.ts +27 -0
- package/dist/agent-runtime/capabilities.js +73 -0
- package/dist/agent-runtime/capabilities.js.map +1 -0
- package/dist/agent-runtime/cli-executor.d.ts +9 -0
- package/dist/agent-runtime/cli-executor.js +100 -30
- package/dist/agent-runtime/cli-executor.js.map +1 -1
- package/dist/agent-runtime/cli.d.ts +1 -0
- package/dist/agent-runtime/cli.js +59 -11
- package/dist/agent-runtime/cli.js.map +1 -1
- package/dist/agent-runtime/codex-schema.d.ts +4 -0
- package/dist/agent-runtime/codex-schema.js +31 -0
- package/dist/agent-runtime/codex-schema.js.map +1 -0
- package/dist/agent-runtime/config.d.ts +4 -0
- package/dist/agent-runtime/config.js +110 -9
- package/dist/agent-runtime/config.js.map +1 -1
- package/dist/agent-runtime/core-host.d.ts +9 -1
- package/dist/agent-runtime/core-host.js +55 -20
- package/dist/agent-runtime/core-host.js.map +1 -1
- package/dist/agent-runtime/efficiency-summary.d.ts +67 -0
- package/dist/agent-runtime/efficiency-summary.js +52 -0
- package/dist/agent-runtime/efficiency-summary.js.map +1 -0
- package/dist/agent-runtime/efficiency-types.d.ts +60 -0
- package/dist/agent-runtime/efficiency-types.js +8 -0
- package/dist/agent-runtime/efficiency-types.js.map +1 -0
- package/dist/agent-runtime/efficiency.d.ts +5 -0
- package/dist/agent-runtime/efficiency.js +35 -0
- package/dist/agent-runtime/efficiency.js.map +1 -0
- package/dist/agent-runtime/evaluation-corpus.d.ts +13 -0
- package/dist/agent-runtime/evaluation-corpus.js +8 -0
- package/dist/agent-runtime/evaluation-corpus.js.map +1 -0
- package/dist/agent-runtime/evaluation.d.ts +43 -0
- package/dist/agent-runtime/evaluation.js +171 -0
- package/dist/agent-runtime/evaluation.js.map +1 -0
- package/dist/agent-runtime/executor-types.d.ts +35 -2
- package/dist/agent-runtime/executor-types.js.map +1 -1
- package/dist/agent-runtime/executors.d.ts +1 -0
- package/dist/agent-runtime/executors.js +4 -0
- package/dist/agent-runtime/executors.js.map +1 -1
- package/dist/agent-runtime/graph/artifacts.d.ts +8 -22
- package/dist/agent-runtime/graph/artifacts.js +99 -73
- package/dist/agent-runtime/graph/artifacts.js.map +1 -1
- package/dist/agent-runtime/graph/nodes.d.ts +3 -0
- package/dist/agent-runtime/graph/nodes.js +73 -26
- package/dist/agent-runtime/graph/nodes.js.map +1 -1
- package/dist/agent-runtime/graph/roles.d.ts +4 -1
- package/dist/agent-runtime/graph/roles.js +100 -36
- package/dist/agent-runtime/graph/roles.js.map +1 -1
- package/dist/agent-runtime/graph/state.d.ts +8 -0
- package/dist/agent-runtime/graph/state.js.map +1 -1
- package/dist/agent-runtime/index.d.ts +7 -1
- package/dist/agent-runtime/index.js +7 -1
- package/dist/agent-runtime/index.js.map +1 -1
- package/dist/agent-runtime/kimi-acp.d.ts +4 -0
- package/dist/agent-runtime/kimi-acp.js +20 -9
- package/dist/agent-runtime/kimi-acp.js.map +1 -1
- package/dist/agent-runtime/openai-executor.d.ts +8 -0
- package/dist/agent-runtime/openai-executor.js +28 -10
- package/dist/agent-runtime/openai-executor.js.map +1 -1
- package/dist/agent-runtime/openspec-tool-server.d.ts +1 -0
- package/dist/agent-runtime/openspec-tool-server.js +37 -0
- package/dist/agent-runtime/openspec-tool-server.js.map +1 -0
- package/dist/agent-runtime/openspec.d.ts +123 -0
- package/dist/agent-runtime/openspec.js +263 -0
- package/dist/agent-runtime/openspec.js.map +1 -0
- package/dist/agent-runtime/prompts.d.ts +5 -1
- package/dist/agent-runtime/prompts.js +59 -40
- package/dist/agent-runtime/prompts.js.map +1 -1
- package/dist/agent-runtime/provider-diagnostic.d.ts +2 -0
- package/dist/agent-runtime/provider-diagnostic.js +25 -0
- package/dist/agent-runtime/provider-diagnostic.js.map +1 -0
- package/dist/agent-runtime/repository-context.d.ts +25 -0
- package/dist/agent-runtime/repository-context.js +100 -0
- package/dist/agent-runtime/repository-context.js.map +1 -0
- package/dist/agent-runtime/review-context.d.ts +14 -0
- package/dist/agent-runtime/review-context.js +41 -0
- package/dist/agent-runtime/review-context.js.map +1 -0
- package/dist/agent-runtime/role-routing.d.ts +10 -0
- package/dist/agent-runtime/role-routing.js +29 -0
- package/dist/agent-runtime/role-routing.js.map +1 -0
- package/dist/agent-runtime/role-state.d.ts +19 -0
- package/dist/agent-runtime/role-state.js +24 -0
- package/dist/agent-runtime/role-state.js.map +1 -0
- package/dist/agent-runtime/runtime-identity.d.ts +11 -0
- package/dist/agent-runtime/runtime-identity.js +33 -0
- package/dist/agent-runtime/runtime-identity.js.map +1 -0
- package/dist/agent-runtime/tool-event.d.ts +3 -0
- package/dist/agent-runtime/tool-event.js +24 -0
- package/dist/agent-runtime/tool-event.js.map +1 -0
- package/dist/agent-runtime/verification-plan.d.ts +55 -0
- package/dist/agent-runtime/verification-plan.js +206 -0
- package/dist/agent-runtime/verification-plan.js.map +1 -0
- package/dist/agent-runtime/workflow-types.d.ts +17 -1
- package/dist/agent-runtime/workflow.js +28 -1
- package/dist/agent-runtime/workflow.js.map +1 -1
- package/dist/agent-runtime/workspace-tools.d.ts +4 -0
- package/dist/agent-runtime/workspace-tools.js +182 -20
- package/dist/agent-runtime/workspace-tools.js.map +1 -1
- package/dist/installer/phases/scaffold.js +39 -109
- package/dist/installer/phases/scaffold.js.map +1 -1
- package/dist/installer/runtime/pipeline-state.d.ts +142 -1
- package/dist/installer/runtime/pipeline-state.js +477 -28
- package/dist/installer/runtime/pipeline-state.js.map +1 -1
- package/docs/agent-runtime-efficiency.md +65 -0
- package/docs/agent-runtime.md +86 -15
- package/integration-contract.json +2 -1
- package/package.json +5 -2
- package/schemas/agent-runtime.schema.json +25 -3
- package/schemas/fixtures/runtime-efficiency-summary.v1.json +790 -0
- package/templates/codex-skills/batch-implement/SKILL.md +33 -58
- package/templates/codex-skills/implement/SKILL.md +21 -124
- package/templates/codex-skills/retry/SKILL.md +8 -34
- package/templates/commands/specrails/batch-implement.md +21 -16
- package/templates/commands/specrails/implement.md +17 -276
- package/templates/commands/specrails/retry.md +6 -34
- package/templates/gemini-commands/batch-implement.toml +34 -28
- package/templates/gemini-commands/implement.toml +34 -55
- package/templates/gemini-commands/retry.toml +10 -16
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { EVALUATION_CORPUS } from './evaluation-corpus.js';
|
|
7
|
+
import { fingerprint } from './durable-store.js';
|
|
8
|
+
import { runCoreWorkflow, coreRuntimeIdentity } from './core-host.js';
|
|
9
|
+
import { ExecutorRegistry } from './executors.js';
|
|
10
|
+
import { OpenSpecTools } from './openspec.js';
|
|
11
|
+
import { validatePipelineContext } from '../installer/runtime/pipeline-state.js';
|
|
12
|
+
import { runtimeEfficiency } from './efficiency.js';
|
|
13
|
+
function oracle(test, file) {
|
|
14
|
+
return spawnSync(process.execPath, ['-e', 'const assert = require("node:assert/strict"); const api = require(process.argv[1]);' + test.oracle, file], { encoding: 'utf8', timeout: 10000 }).status === 0;
|
|
15
|
+
}
|
|
16
|
+
function prepare(test, root) {
|
|
17
|
+
const repositories = test.repositories.map(id => {
|
|
18
|
+
const directory = path.join(root, id);
|
|
19
|
+
mkdirSync(directory);
|
|
20
|
+
writeFileSync(path.join(directory, 'implementation.cjs'), test.source);
|
|
21
|
+
writeFileSync(path.join(directory, 'AGENTS.md'), '# Repository guidance\n' + 'Keep public behavior stable. Follow the existing implementation.cjs convention.\n'.repeat(180));
|
|
22
|
+
execFileSync('git', ['init', '-q', directory]);
|
|
23
|
+
execFileSync('git', ['-C', directory, 'add', '.']);
|
|
24
|
+
execFileSync('git', ['-C', directory, '-c', 'user.name=Evaluation', '-c', 'user.email=evaluation@example.invalid', 'commit', '-qm', 'frozen fixture']);
|
|
25
|
+
return { id, name: id, path: directory, baseSha: execFileSync('git', ['-C', directory, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim() };
|
|
26
|
+
});
|
|
27
|
+
return validatePipelineContext({ schemaVersion: 1, runId: randomUUID(), backlogRoot: root, artifactRoot: repositories[0].path, artifactRepositoryId: repositories[0].id, repositories, ownership: { git: 'host', backlog: 'host', worktrees: 'host' }, specs: [{ id: 1, title: test.id, description: test.description + ' Implement behavior in implementation.cjs in every selected repository.', repositoryIds: test.repositories, acceptanceCriteria: [test.description] }] });
|
|
28
|
+
}
|
|
29
|
+
function fixtureRegistry(test, context) {
|
|
30
|
+
let developers = 0, reviews = 0;
|
|
31
|
+
const sessions = new Map();
|
|
32
|
+
return new ExecutorRegistry().register('fixture', {
|
|
33
|
+
capabilities: () => ({ transport: 'offline-fixture', continuation: 'supported', effortSupport: 'unsupported', supportedEfforts: [], observedModel: false, observedEffort: false }),
|
|
34
|
+
async execute(request) {
|
|
35
|
+
const sessionId = 'fixture-' + request.role;
|
|
36
|
+
if (request.resumeSessionId && !sessions.has(request.resumeSessionId))
|
|
37
|
+
throw new Error('Fixture session was not restored');
|
|
38
|
+
const history = sessions.get(sessionId) ?? [];
|
|
39
|
+
history.push(request.prompt);
|
|
40
|
+
sessions.set(sessionId, history);
|
|
41
|
+
const tools = new OpenSpecTools(request.openspec);
|
|
42
|
+
await tools.execute({ action: 'load_skill' });
|
|
43
|
+
let output;
|
|
44
|
+
if (request.role === 'architect') {
|
|
45
|
+
await tools.execute({ action: 'new' });
|
|
46
|
+
const artifacts = [
|
|
47
|
+
['proposal', 'proposal.md', `## Why\n${test.description}\n## What Changes\n- Implement the requested behavior.\n## Capabilities\n### New Capabilities\n- \`fixture-behavior\`: ${test.description}\n### Modified Capabilities\nNone.\n## Impact\nSelected repositories.\n`],
|
|
48
|
+
['design', 'design.md', `## Context\n${test.description}\n## Decisions\nUse the existing implementation.cjs pattern and keep the public function boundary.`],
|
|
49
|
+
['specs', 'specs/fixture-behavior/spec.md', `## ADDED Requirements\n### Requirement: Requested behavior\nThe implementation SHALL satisfy the frozen behavior.\n#### Scenario: Supported input\n- **WHEN** a supported input is supplied\n- **THEN** the expected value is returned without losing required ordering or boundary behavior\n`],
|
|
50
|
+
['tasks', 'tasks.md', '## 1. Implementation\n- [ ] 1.1 Implement and test the requested behavior\n'],
|
|
51
|
+
];
|
|
52
|
+
for (const [artifact, file, content] of artifacts) {
|
|
53
|
+
await tools.execute({ action: 'instructions', artifact });
|
|
54
|
+
await tools.execute({ action: 'write_artifact', path: file, content });
|
|
55
|
+
}
|
|
56
|
+
output = { confidence: 'high', planningDepth: context.repositories.length > 1 ? 'full' : 'focused', referencePatterns: ['implementation.cjs'], riskFlags: [], verification: [] };
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
await tools.execute({ action: 'instructions', artifact: 'apply' });
|
|
60
|
+
if (request.role === 'developer') {
|
|
61
|
+
developers++;
|
|
62
|
+
for (const repo of context.repositories)
|
|
63
|
+
writeFileSync(path.join(repo.path, 'implementation.cjs'), test.correction && developers === 1 ? test.defects[0] : test.solution);
|
|
64
|
+
writeFileSync(path.join(context.artifactRoot, 'openspec/changes/evaluation-change/tasks.md'), '## 1. Implementation\n- [x] 1.1 Implement and test the requested behavior\n');
|
|
65
|
+
output = { summary: 'Implemented fixture behavior', files: ['implementation.cjs'], tests: [], verification: 'Core executes the frozen checks', incomplete: [] };
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
reviews++;
|
|
69
|
+
const approved = !(test.correction === 'review' && reviews === 1);
|
|
70
|
+
output = { approved, summary: approved ? 'Behavior reviewed' : 'Stable order is missing', issues: approved ? [] : ['Preserve original order'], score: approved ? 95 : 50, aspects: Object.fromEntries(['type_correctness', 'pattern_adherence', 'test_coverage', 'security', 'architectural_alignment'].map(key => [key, approved ? 95 : 50])), acceptance: { criteria: [{ specId: '1', criterionIndex: 0, status: approved ? 'met' : 'blocked', evidence: ['implementation.cjs inspected against frozen behavior'] }], checks: [], findings: [] } };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { text: JSON.stringify(output), structuredOutput: output, sessionId, usage: { inputTokens: Math.ceil(request.prompt.length / 4), outputTokens: 100, costUsd: 0 } };
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
export async function runEvaluation(options) {
|
|
78
|
+
const repetitions = options.repetitions ?? 1;
|
|
79
|
+
if (!Number.isSafeInteger(repetitions) || repetitions < 1 || repetitions > 20)
|
|
80
|
+
throw new Error('Evaluation repetitions must be 1–20');
|
|
81
|
+
if (options.real && (!options.config || !Number.isFinite(options.maxCostUsd) || options.maxCostUsd <= 0 || Object.values(options.config.agents).some(role => !role.model)))
|
|
82
|
+
throw new Error('Real evaluation requires explicit per-role models and a positive aggregate spend limit');
|
|
83
|
+
const identity = coreRuntimeIdentity();
|
|
84
|
+
const observations = [];
|
|
85
|
+
let spend = 0, stopReason = null;
|
|
86
|
+
const root = mkdtempSync(path.join(tmpdir(), 'specrails-evaluation-'));
|
|
87
|
+
try {
|
|
88
|
+
for (let repeat = 0; repeat < repetitions && !stopReason; repeat++)
|
|
89
|
+
for (const test of EVALUATION_CORPUS) {
|
|
90
|
+
// Check the independent oracle itself against defective candidates.
|
|
91
|
+
const probe = path.join(root, 'oracle-probe.cjs');
|
|
92
|
+
writeFileSync(probe, test.solution);
|
|
93
|
+
if (!oracle(test, probe))
|
|
94
|
+
throw new Error('Acceptance oracle rejects reference solution: ' + test.id);
|
|
95
|
+
for (const defect of test.defects) {
|
|
96
|
+
writeFileSync(probe, defect);
|
|
97
|
+
if (oracle(test, probe))
|
|
98
|
+
throw new Error('Acceptance oracle admits defective variant: ' + test.id);
|
|
99
|
+
}
|
|
100
|
+
const order = options.real && Math.random() < 0.5 ? ['optimized', 'full'] : ['full', 'optimized'];
|
|
101
|
+
for (const mode of order) {
|
|
102
|
+
if (stopReason)
|
|
103
|
+
break;
|
|
104
|
+
const workspace = path.join(root, `${test.id}-${repeat}-${mode}`);
|
|
105
|
+
mkdirSync(workspace);
|
|
106
|
+
const context = prepare(test, workspace);
|
|
107
|
+
const base = options.config ?? { schemaVersion: 1, enabled: true, providers: [], agents: { architect: { provider: 'fixture', model: 'fixture' }, developer: { provider: 'fixture', model: 'fixture' }, reviewer: { provider: 'fixture', model: 'fixture' } }, verification: [] };
|
|
108
|
+
const config = { ...structuredClone(base), approvalBeforeArchive: false, efficiency: { schemaVersion: 1, contextMode: mode === 'full' ? 'full' : 'incremental', reviewMode: mode === 'full' ? 'full' : 'incremental', planning: mode === 'full' ? 'full' : 'proportional', verification: { maxConcurrency: 1 } }, limits: { ...base.limits, maxAttempts: 3, timeoutMs: 300000, ...(options.real ? { maxCostUsd: options.maxCostUsd - spend } : {}) }, verification: context.repositories.map(repo => ({ repositoryId: repo.id, command: process.execPath, args: ['-e', 'const assert = require("node:assert/strict"); const api = require("./implementation.cjs");' + (test.verification ?? '')] })) };
|
|
109
|
+
if (options.real && config.limits.maxCostUsd <= 0) {
|
|
110
|
+
stopReason = 'Aggregate spend limit reached';
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
const started = Date.now();
|
|
114
|
+
let state;
|
|
115
|
+
try {
|
|
116
|
+
state = await runCoreWorkflow({ context, change: 'evaluation-change', config, ...(options.real ? {} : { registry: fixtureRegistry(test, context) }) });
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
observations.push({ caseId: test.id, repeat, mode, status: 'blocked', independentAccepted: false, error: error instanceof Error ? error.message : String(error), runtimeIdentity: identity, taskHash: fingerprint(test), oracleHash: fingerprint(test.oracle), repositories: context.repositories.map(({ id, baseSha }) => ({ id, baseSha })), configHash: fingerprint(config) });
|
|
120
|
+
stopReason = 'Runtime preflight failed; experiment stopped without retry';
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
const accepted = context.repositories.every(repo => oracle(test, path.join(repo.path, 'implementation.cjs')));
|
|
124
|
+
const metrics = runtimeEfficiency(state);
|
|
125
|
+
const calls = state.history.flatMap(attempt => attempt.invocations ?? []);
|
|
126
|
+
observations.push({ caseId: test.id, repeat, mode, order, taskHash: fingerprint(test), oracleHash: fingerprint(test.oracle), repositories: context.repositories.map(({ id, baseSha }) => ({ id, baseSha })), configHash: fingerprint(config), runtimeIdentity: identity, cacheState: 'uncontrolled-provider-cache; fresh workspace and role sessions', status: state.status, independentAccepted: accepted && state.status === 'succeeded', activeDurationMs: Date.now() - started, metrics, invocations: calls.map(call => ({ kind: call.kind, promptBytes: call.promptBytes, provider: call.provider, model: call.model, status: call.status })), failures: state.history.filter(attempt => attempt.status !== 'succeeded' || attempt.output?.valid === false || attempt.output?.approved === false).map(attempt => ({ role: attempt.stepId, status: attempt.status, error: attempt.error ?? null })) });
|
|
127
|
+
if (options.real) {
|
|
128
|
+
if (metrics.total.costUsd === null)
|
|
129
|
+
stopReason = 'Billing incomplete; monetary comparison is inconclusive and further spend is stopped';
|
|
130
|
+
else
|
|
131
|
+
spend += metrics.total.costUsd;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (stopReason)
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
const median = (values) => { const sorted = [...values].sort((a, b) => a - b); return sorted.length ? (sorted[Math.floor((sorted.length - 1) / 2)] + sorted[Math.floor(sorted.length / 2)]) / 2 : null; };
|
|
138
|
+
const groups = Object.fromEntries(['full', 'optimized'].map(mode => {
|
|
139
|
+
const rows = observations.filter(row => row.mode === mode);
|
|
140
|
+
const accepted = rows.filter(row => row.independentAccepted).length;
|
|
141
|
+
const costs = rows.map(row => row.metrics?.total.costUsd);
|
|
142
|
+
const knownCost = costs.every(value => typeof value === 'number') ? costs.reduce((sum, value) => sum + value, 0) : null;
|
|
143
|
+
const durations = rows.flatMap(row => typeof row.activeDurationMs === 'number' ? [row.activeDurationMs] : []);
|
|
144
|
+
const average = durations.length ? durations.reduce((sum, value) => sum + value, 0) / durations.length : null;
|
|
145
|
+
return [mode, { samples: rows.length, independentlyAccepted: accepted, costPerAcceptedUsd: options.real && knownCost !== null && accepted ? knownCost / accepted : null, medianActiveDurationMs: median(durations), minActiveDurationMs: durations.length ? Math.min(...durations) : null, maxActiveDurationMs: durations.length ? Math.max(...durations) : null, standardDeviationMs: average === null ? null : Math.sqrt(durations.reduce((sum, value) => sum + (value - average) ** 2, 0) / durations.length) }];
|
|
146
|
+
}));
|
|
147
|
+
const full = groups.full, optimized = groups.optimized;
|
|
148
|
+
const paired = observations.length === EVALUATION_CORPUS.length * repetitions * 2 && full.samples === optimized.samples;
|
|
149
|
+
const noObservedQualityDrop = paired && optimized.independentlyAccepted >= full.independentlyAccepted;
|
|
150
|
+
const promptPairs = observations.filter(row => row.mode === 'full' && row.caseId === 'verification-correction').map(row => {
|
|
151
|
+
const other = observations.find(item => item.mode === 'optimized' && item.caseId === row.caseId && item.repeat === row.repeat);
|
|
152
|
+
const correction = (value) => value?.invocations?.find(call => call.kind === 'correction')?.promptBytes;
|
|
153
|
+
const before = correction(row), after = correction(other);
|
|
154
|
+
return { caseId: row.caseId, repeat: row.repeat, fullBytes: before ?? null, optimizedBytes: after ?? null, reduction: before && after !== undefined ? 1 - after / before : null };
|
|
155
|
+
});
|
|
156
|
+
const noExtraInvocations = paired && observations.filter(row => row.mode === 'full').every(row => {
|
|
157
|
+
const other = observations.find(item => item.mode === 'optimized' && item.caseId === row.caseId && item.repeat === row.repeat);
|
|
158
|
+
return Array.isArray(row.invocations) && Array.isArray(other?.invocations) && other.invocations.length <= row.invocations.length;
|
|
159
|
+
});
|
|
160
|
+
const monetaryConclusion = options.real && paired && full.costPerAcceptedUsd !== null && optimized.costPerAcceptedUsd !== null && full.costPerAcceptedUsd > 0 ? optimized.costPerAcceptedUsd <= full.costPerAcceptedUsd * 0.8 && noObservedQualityDrop ? 'target-met-in-this-sample' : 'target-not-met-in-this-sample' : 'inconclusive';
|
|
161
|
+
const report = { schemaVersion: 1, experiment: Object.values(options.config?.agents ?? {}).some(role => role.escalation) ? 'routing' : 'same-model', noExtraInvocations, mode: options.real ? 'real' : 'offline', runtimeIdentity: identity, corpusHash: fingerprint(EVALUATION_CORPUS), measurement: options.real ? 'reported-provider-usage' : 'synthetic-fixture-usage; no actual AI savings measured', monetaryTarget: 0.2, monetaryConclusion, groups, noObservedQualityDrop, promptPairs, correctionPromptTargetMet: promptPairs.length > 0 && promptPairs.every(pair => pair.reduction !== null && pair.reduction >= 0.4), sampleLimitations: 'Small descriptive paired sample; does not establish a universal quality or savings guarantee.', stopReason, reportedSpendUsd: options.real ? spend : 0, observations };
|
|
162
|
+
mkdirSync(options.output, { recursive: true });
|
|
163
|
+
writeFileSync(path.join(options.output, 'evaluation.json'), JSON.stringify(report, null, 2) + '\n');
|
|
164
|
+
writeFileSync(path.join(options.output, 'evaluation.md'), `# Implementation efficiency evaluation\n\nMode: ${report.mode}. ${report.measurement}.\n\nMonetary target: 20% lower aggregate cost per independently accepted output. Conclusion: ${monetaryConclusion}.\n\n${JSON.stringify(groups, null, 2)}\n\nCorrection prompt comparison: ${JSON.stringify(promptPairs)}\n\n${observations.map(row => `- ${row.caseId}, ${row.mode}: ${row.status}; independent acceptance ${row.independentAccepted}; ${row.activeDurationMs} ms`).join('\n')}\n\n${stopReason ?? (options.real ? report.sampleLimitations : 'No paid benchmark has established monetary savings.')}\n`);
|
|
165
|
+
return report;
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
rmSync(root, { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=evaluation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluation.js","sourceRoot":"","sources":["../../src/agent-runtime/evaluation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { VerificationCommand } from '../installer/runtime/pipeline-state.js';
|
|
2
|
+
import type { CacheTokenUsage } from './efficiency-types.js';
|
|
2
3
|
export type AgentRole = 'architect' | 'developer' | 'reviewer';
|
|
3
4
|
export type CliProvider = 'claude' | 'codex' | 'gemini' | 'kimi';
|
|
4
5
|
export type RuntimeProviderConfig = {
|
|
@@ -15,10 +16,35 @@ export interface RuntimeAgentConfig {
|
|
|
15
16
|
provider: string;
|
|
16
17
|
model?: string;
|
|
17
18
|
maxTurns?: number;
|
|
19
|
+
effort?: string;
|
|
20
|
+
escalation?: {
|
|
21
|
+
model: string;
|
|
22
|
+
effort?: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface EfficiencyPolicy {
|
|
26
|
+
schemaVersion: 1;
|
|
27
|
+
contextMode?: 'full' | 'incremental';
|
|
28
|
+
reviewMode?: 'full' | 'incremental';
|
|
29
|
+
planning?: 'full' | 'proportional';
|
|
30
|
+
acceptDeveloperChecks?: boolean;
|
|
31
|
+
verification?: {
|
|
32
|
+
maxConcurrency?: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface ExecutorCapabilities {
|
|
36
|
+
transport: string;
|
|
37
|
+
continuation: 'supported' | 'unsupported' | 'unknown';
|
|
38
|
+
effortSupport: 'supported' | 'unsupported' | 'unknown';
|
|
39
|
+
supportedEfforts: string[] | null;
|
|
40
|
+
observedModel: boolean;
|
|
41
|
+
observedEffort: boolean;
|
|
18
42
|
}
|
|
19
43
|
export type ReviewAspectName = 'type_correctness' | 'pattern_adherence' | 'test_coverage' | 'security' | 'architectural_alignment';
|
|
20
44
|
export interface RuntimeConfig {
|
|
21
45
|
schemaVersion: 1;
|
|
46
|
+
rolePrompts?: Partial<Record<AgentRole, string>>;
|
|
47
|
+
efficiency?: EfficiencyPolicy;
|
|
22
48
|
enabled: boolean;
|
|
23
49
|
providers: RuntimeProviderConfig[];
|
|
24
50
|
agents: Record<AgentRole, RuntimeAgentConfig>;
|
|
@@ -41,7 +67,7 @@ export interface RuntimeConfig {
|
|
|
41
67
|
};
|
|
42
68
|
}
|
|
43
69
|
/** Null means unavailable, including when a CLI never reports billing. */
|
|
44
|
-
export interface AgentUsage {
|
|
70
|
+
export interface AgentUsage extends CacheTokenUsage {
|
|
45
71
|
inputTokens: number | null;
|
|
46
72
|
outputTokens: number | null;
|
|
47
73
|
costUsd: number | null;
|
|
@@ -52,19 +78,24 @@ export interface AgentEvent {
|
|
|
52
78
|
tool?: string;
|
|
53
79
|
/** Short human-readable tool target, such as a file path or command. Never a complete transcript. */
|
|
54
80
|
detail?: string;
|
|
81
|
+
/** Untruncated tool paths for repository attribution; never file contents. */
|
|
82
|
+
targetPaths?: string[];
|
|
83
|
+
cwd?: string;
|
|
55
84
|
usage?: AgentUsage;
|
|
56
85
|
}
|
|
57
86
|
export interface AgentRequest {
|
|
87
|
+
openspec?: import('./openspec.js').OpenSpecRoleContext;
|
|
58
88
|
role: AgentRole;
|
|
59
89
|
prompt: string;
|
|
60
90
|
cwd: string;
|
|
61
91
|
allowedRoots: string[];
|
|
62
92
|
model?: string;
|
|
93
|
+
effort?: string;
|
|
63
94
|
maxTurns?: number;
|
|
64
95
|
timeoutMs?: number;
|
|
65
96
|
maxTokens?: number;
|
|
66
97
|
maxCostUsd?: number;
|
|
67
|
-
/**
|
|
98
|
+
/** Only send a partial follow-up when capabilities guarantee restored history before inference. */
|
|
68
99
|
resumeSessionId?: string;
|
|
69
100
|
/** JSON Schema for the final structured reply. Executors with native structured output enforce it; the prompt remains authoritative elsewhere. */
|
|
70
101
|
outputSchema?: Record<string, unknown>;
|
|
@@ -79,7 +110,9 @@ export interface AgentResult {
|
|
|
79
110
|
}
|
|
80
111
|
export type AgentLimits = Pick<AgentRequest, 'maxTokens' | 'maxCostUsd'>;
|
|
81
112
|
export interface AgentExecutor {
|
|
113
|
+
capabilities?(model?: string): ExecutorCapabilities | Promise<ExecutorCapabilities>;
|
|
82
114
|
/** Side-effect-free preflight. Custom executors own their limit capabilities. */
|
|
115
|
+
validateOpenSpec?(context: import('./openspec.js').OpenSpecRoleContext): Promise<void>;
|
|
83
116
|
validateLimits?(limits: AgentLimits): void;
|
|
84
117
|
execute(request: AgentRequest): Promise<AgentResult>;
|
|
85
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor-types.js","sourceRoot":"","sources":["../../src/agent-runtime/executor-types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"executor-types.js","sourceRoot":"","sources":["../../src/agent-runtime/executor-types.ts"],"names":[],"mappings":"AA8FA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACC;IAA8B;IAA3E,YAAY,OAAe,EAAkB,IAAY,EAAkB,QAAoB,YAAY,EAAE;QAC3G,KAAK,CAAC,OAAO,CAAC,CAAA;QAD6B,SAAI,GAAJ,IAAI,CAAQ;QAAkB,UAAK,GAAL,KAAK,CAA6B;QAE3G,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AACD,MAAM,UAAU,YAAY,KAAiB,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA,CAAC,CAAC;AAC9G,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,IAAI,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,sBAAsB,EAAE,iBAAiB,CAAC,CAAA;IAC7O,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,0BAA0B,EAAE,eAAe,CAAC,CAAA;IACvQ,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,CAAU,EAAE,CAAC;QAChF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAC1B,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,mBAAmB,CAAC,WAAW,GAAG,EAAE,EAAE,eAAe,CAAC,CAAA;IAChM,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,KAAK,SAAS,IAAI,CAAC,OAAO,OAAO,CAAC,eAAe,KAAK,QAAQ,IAAI,CAAC,qCAAqC,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,4BAA4B,EAAE,iBAAiB,CAAC,CAAA;IAClP,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,uBAAuB,EAAE,iBAAiB,CAAC,CAAA;IAC/N,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAA;AAC1F,CAAC"}
|
|
@@ -10,6 +10,7 @@ export declare class ExecutorRegistry {
|
|
|
10
10
|
register(id: string, executor: AgentExecutor): this;
|
|
11
11
|
get(id: string): AgentExecutor;
|
|
12
12
|
ids(): string[];
|
|
13
|
+
capabilities(id: string, model?: string): Promise<import("./executor-types.js").ExecutorCapabilities>;
|
|
13
14
|
validateLimits(id: string, limits: AgentLimits): void;
|
|
14
15
|
execute(id: string, request: AgentRequest): Promise<AgentResult>;
|
|
15
16
|
}
|
|
@@ -2,6 +2,7 @@ import { AgentExecutionError, validateAgentRequest } from './executor-types.js';
|
|
|
2
2
|
import { validateRuntimeConfig } from './config.js';
|
|
3
3
|
import { CliExecutor } from './cli-executor.js';
|
|
4
4
|
import { OpenAICompatibleExecutor } from './openai-executor.js';
|
|
5
|
+
import { assertEffortSupported, unknownCapabilities } from './capabilities.js';
|
|
5
6
|
export { CliExecutor, buildCliInvocation, parseCliOutput } from './cli-executor.js';
|
|
6
7
|
export { OpenAICompatibleExecutor } from './openai-executor.js';
|
|
7
8
|
export { AgentExecutionError } from './executor-types.js';
|
|
@@ -20,9 +21,12 @@ export class ExecutorRegistry {
|
|
|
20
21
|
return executor;
|
|
21
22
|
}
|
|
22
23
|
ids() { return [...this.executors.keys()]; }
|
|
24
|
+
async capabilities(id, model) { return await this.get(id).capabilities?.(model) ?? unknownCapabilities('registered:' + id); }
|
|
23
25
|
validateLimits(id, limits) { this.get(id).validateLimits?.(limits); }
|
|
24
26
|
async execute(id, request) {
|
|
25
27
|
validateAgentRequest(request);
|
|
28
|
+
if (request.effort !== undefined)
|
|
29
|
+
assertEffortSupported(request, await this.capabilities(id, request.model));
|
|
26
30
|
const result = await this.get(id).execute(request);
|
|
27
31
|
if (!result || typeof result.text !== 'string' || !result.text.trim() || !result.usage || ['inputTokens', 'outputTokens', 'costUsd'].some(key => {
|
|
28
32
|
const value = result.usage[key];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executors.js","sourceRoot":"","sources":["../../src/agent-runtime/executors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAA;AACxE,OAAO,EAAE,wBAAwB,EAAgC,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"executors.js","sourceRoot":"","sources":["../../src/agent-runtime/executors.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,32 +9,18 @@ export declare function parseAgentObject(text: string): Record<string, unknown>;
|
|
|
9
9
|
export declare function child(root: string, relative: string): string;
|
|
10
10
|
export declare function write(file: string, content: string): void;
|
|
11
11
|
export declare function journal(context: PipelineContext): PipelineState;
|
|
12
|
-
export
|
|
13
|
-
proposal: string;
|
|
14
|
-
design: string;
|
|
15
|
-
tasks: string[];
|
|
16
|
-
specs: Array<{
|
|
17
|
-
name: string;
|
|
18
|
-
content: string;
|
|
19
|
-
}>;
|
|
12
|
+
export declare function parseArchitecture(raw: Record<string, unknown>): {
|
|
20
13
|
confidence: DesignConfidence;
|
|
21
14
|
question?: string;
|
|
22
15
|
verification: unknown;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* design resting on one named assumption) with its provenance kept alongside,
|
|
30
|
-
* because Core's design gate admits only high or medium confidence.
|
|
31
|
-
*/
|
|
32
|
-
export declare function writeArchitecture(context: PipelineContext, change: string, architecture: Architecture, options?: {
|
|
16
|
+
planningDepth: 'focused' | 'full';
|
|
17
|
+
planningReason?: string;
|
|
18
|
+
referencePatterns: string[];
|
|
19
|
+
riskFlags: string[];
|
|
20
|
+
};
|
|
21
|
+
export declare function writeDesignConfidence(context: PipelineContext, change: string, architecture: ReturnType<typeof parseArchitecture>, options?: {
|
|
33
22
|
assumed?: boolean;
|
|
34
23
|
}): void;
|
|
35
24
|
/** Commands the architect proposed for repositories the configuration leaves uncovered. */
|
|
36
25
|
export declare function proposedVerification(context: PipelineContext, configured: VerificationCommand[], verification: unknown): VerificationCommand[];
|
|
37
|
-
export declare function
|
|
38
|
-
/** Archive is deterministic and replayable only with explicit write recovery.
|
|
39
|
-
* The reviewed specification documents are complete replacements, not deltas. */
|
|
40
|
-
export declare function archive(context: PipelineContext, change: string): void;
|
|
26
|
+
export declare function archive(context: PipelineContext, change: string, signal?: AbortSignal): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { artifactPath, hash, resolveOpenSpecCli, runOpenSpec } from '../openspec.js';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { checkArchive, pipelineStateDirectory, transitionPipeline, validateVerificationRequest, } from '../../installer/runtime/pipeline-state.js';
|
|
5
6
|
export const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -8,11 +9,6 @@ export function object(value) {
|
|
|
8
9
|
throw new Error('Expected a structured JSON object from agent');
|
|
9
10
|
return value;
|
|
10
11
|
}
|
|
11
|
-
function markdown(value, name) {
|
|
12
|
-
if (typeof value !== 'string' || !value.trim() || value.length > 500_000)
|
|
13
|
-
throw new Error('Invalid agent field: ' + name);
|
|
14
|
-
return value.trim() + '\n';
|
|
15
|
-
}
|
|
16
12
|
/** Accepts the object wherever the model put it: bare, fenced, or surrounded by commentary. */
|
|
17
13
|
export function parseAgentObject(text) {
|
|
18
14
|
if (text.length > 2_000_000)
|
|
@@ -65,56 +61,36 @@ export function write(file, content) {
|
|
|
65
61
|
export function journal(context) {
|
|
66
62
|
return JSON.parse(readFileSync(path.join(pipelineStateDirectory(context), 'state.json'), 'utf8'));
|
|
67
63
|
}
|
|
68
|
-
/** Validates the architect's structured reply; throws `Invalid …` so the role gets one repair turn. */
|
|
69
64
|
export function parseArchitecture(raw) {
|
|
70
|
-
const proposal = markdown(raw.proposal, 'proposal');
|
|
71
|
-
const design = markdown(raw.design, 'design');
|
|
72
65
|
if (!['high', 'medium', 'low'].includes(String(raw.confidence)))
|
|
73
66
|
throw new Error('Invalid design confidence');
|
|
74
|
-
if (
|
|
75
|
-
throw new Error('Architecture requires 1–200 tasks');
|
|
76
|
-
const tasks = raw.tasks.map(task => {
|
|
77
|
-
const title = object(task).title;
|
|
78
|
-
if (typeof title !== 'string' || !title.trim() || /[\r\n]/.test(title) || title.length > 1000)
|
|
79
|
-
throw new Error('Invalid task title');
|
|
80
|
-
return title.trim();
|
|
81
|
-
});
|
|
82
|
-
if (!Array.isArray(raw.specs) || !raw.specs.length || raw.specs.length > 100)
|
|
83
|
-
throw new Error('Architecture requires 1–100 specifications');
|
|
84
|
-
const specs = raw.specs.map((item) => {
|
|
85
|
-
const spec = object(item);
|
|
86
|
-
if (typeof spec.name !== 'string' || !SLUG.test(spec.name) || spec.name.length > 100)
|
|
87
|
-
throw new Error('Invalid specification name');
|
|
88
|
-
return { name: spec.name, content: markdown(spec.content, 'spec.content') };
|
|
89
|
-
});
|
|
90
|
-
if (new Set(specs.map(s => s.name)).size !== specs.length)
|
|
91
|
-
throw new Error('Duplicate specification names');
|
|
92
|
-
if (raw.question !== undefined && raw.question !== null && (typeof raw.question !== 'string' || raw.question.length > 4000))
|
|
67
|
+
if (raw.question !== undefined && (typeof raw.question !== 'string' || raw.question.length > 4000))
|
|
93
68
|
throw new Error('Invalid architect question');
|
|
94
|
-
|
|
95
|
-
|
|
69
|
+
if (raw.planningDepth !== undefined && raw.planningDepth !== 'focused' && raw.planningDepth !== 'full')
|
|
70
|
+
throw new Error('Invalid planning depth');
|
|
71
|
+
if (raw.planningReason !== undefined && (typeof raw.planningReason !== 'string' || !raw.planningReason.trim() || raw.planningReason.length > 2000))
|
|
72
|
+
throw new Error('Invalid planning reason');
|
|
73
|
+
const references = (key) => {
|
|
74
|
+
const value = raw[key];
|
|
75
|
+
if (value === undefined)
|
|
76
|
+
return [];
|
|
77
|
+
if (!Array.isArray(value) || value.length > 20 || value.some(item => typeof item !== 'string' || !item.trim() || item.length > 1000))
|
|
78
|
+
throw new Error('Invalid architect ' + key);
|
|
79
|
+
return value;
|
|
80
|
+
};
|
|
81
|
+
const referencePatterns = references('referencePatterns'), riskFlags = references('riskFlags');
|
|
82
|
+
return { confidence: raw.confidence, ...(typeof raw.question === 'string' && raw.question.trim() ? { question: raw.question.trim() } : {}), verification: raw.verification,
|
|
83
|
+
planningDepth: raw.planningDepth === 'focused' && referencePatterns.length > 0 && riskFlags.length === 0 ? 'focused' : 'full',
|
|
84
|
+
...(typeof raw.planningReason === 'string' ? { planningReason: raw.planningReason } : {}), referencePatterns, riskFlags, };
|
|
96
85
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* because Core's design gate admits only high or medium confidence.
|
|
102
|
-
*/
|
|
103
|
-
export function writeArchitecture(context, change, architecture, options = {}) {
|
|
104
|
-
const tasks = architecture.tasks.map((title, i) => '- [ ] ' + (i + 1) + '. ' + title).join('\n') + '\n';
|
|
105
|
-
const prefix = 'openspec/changes/' + change + '/';
|
|
86
|
+
export function writeDesignConfidence(context, change, architecture, options = {}) {
|
|
87
|
+
// A question may precede change creation. Do not fabricate a change directory in that case.
|
|
88
|
+
if (!existsSync(child(context.artifactRoot, 'openspec/changes/' + change + '/.openspec.yaml')))
|
|
89
|
+
return;
|
|
106
90
|
const confidence = options.assumed && architecture.confidence === 'low'
|
|
107
|
-
? { confidence: 'medium', assumed: true, reportedConfidence: 'low',
|
|
108
|
-
: { confidence: architecture.confidence,
|
|
109
|
-
|
|
110
|
-
['proposal.md', architecture.proposal], ['design.md', architecture.design], ['tasks.md', tasks],
|
|
111
|
-
['design-confidence.json', JSON.stringify(confidence, null, 2) + '\n'],
|
|
112
|
-
...architecture.specs.map(s => ['specs/' + s.name + '/spec.md', s.content]),
|
|
113
|
-
];
|
|
114
|
-
// Validate every destination before the first write.
|
|
115
|
-
const targets = files.map(([name, content]) => [child(context.artifactRoot, prefix + name), content]);
|
|
116
|
-
for (const [target, content] of targets)
|
|
117
|
-
write(target, content);
|
|
91
|
+
? { confidence: 'medium', assumed: true, reportedConfidence: 'low', question: architecture.question }
|
|
92
|
+
: { confidence: architecture.confidence, question: architecture.question };
|
|
93
|
+
write(child(context.artifactRoot, 'openspec/changes/' + change + '/design-confidence.json'), JSON.stringify(confidence, null, 2) + '\n');
|
|
118
94
|
}
|
|
119
95
|
/** Commands the architect proposed for repositories the configuration leaves uncovered. */
|
|
120
96
|
export function proposedVerification(context, configured, verification) {
|
|
@@ -133,35 +109,85 @@ export function proposedVerification(context, configured, verification) {
|
|
|
133
109
|
validateVerificationRequest(context, { kind: 'scoped', commands: proposed });
|
|
134
110
|
return proposed;
|
|
135
111
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
112
|
+
/** Publish an archive prepared by the real CLI. The durable write set makes a
|
|
113
|
+
* crash between main-spec updates recoverable without applying a delta twice. */
|
|
114
|
+
async function archiveWithOpenSpec(context, change, active, signal) {
|
|
115
|
+
const directory = pipelineStateDirectory(context);
|
|
116
|
+
const receipt = path.join(directory, 'openspec-archive.json');
|
|
117
|
+
const files = (root, prefix = '') => {
|
|
118
|
+
if (!existsSync(root))
|
|
119
|
+
return [];
|
|
120
|
+
return readdirSync(root, { withFileTypes: true }).flatMap(entry => {
|
|
121
|
+
const relative = prefix + entry.name;
|
|
122
|
+
if (entry.isSymbolicLink())
|
|
123
|
+
throw new Error('OpenSpec archive refuses symlink trees');
|
|
124
|
+
return entry.isDirectory() ? files(path.join(root, entry.name), relative + '/') : [relative];
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
const activeHash = hash(JSON.stringify(files(active).sort().map(file => [file, readFileSync(artifactPath(active, file), 'utf8')])));
|
|
128
|
+
let plan;
|
|
129
|
+
if (existsSync(receipt))
|
|
130
|
+
plan = JSON.parse(readFileSync(receipt, 'utf8'));
|
|
131
|
+
else {
|
|
132
|
+
const staging = mkdtempSync(path.join(directory, '.openspec-archive-'));
|
|
133
|
+
try {
|
|
134
|
+
const source = artifactPath(context.artifactRoot, 'openspec');
|
|
135
|
+
files(source); // Reject symlinks before copying or invoking the external CLI.
|
|
136
|
+
cpSync(source, path.join(staging, 'openspec'), { recursive: true });
|
|
137
|
+
await runOpenSpec(resolveOpenSpecCli(), staging, ['archive', change, '--yes'], signal);
|
|
138
|
+
const archives = readdirSync(path.join(staging, 'openspec/changes/archive')).filter(name => name.endsWith('-' + change));
|
|
139
|
+
if (archives.length !== 1)
|
|
140
|
+
throw new Error('OpenSpec archive destination is ambiguous');
|
|
141
|
+
const destination = 'openspec/changes/archive/' + archives[0];
|
|
142
|
+
if (existsSync(artifactPath(context.artifactRoot, destination)))
|
|
143
|
+
throw new Error('OpenSpec archive destination already exists');
|
|
144
|
+
const beforeRoot = path.join(source, 'specs'), afterRoot = path.join(staging, 'openspec/specs');
|
|
145
|
+
const names = new Set([...files(beforeRoot), ...files(afterRoot)]);
|
|
146
|
+
const contents = (root, name) => existsSync(path.join(root, name)) ? readFileSync(path.join(root, name), 'utf8') : null;
|
|
147
|
+
plan = { activeHash, destination, writes: [...names].map(name => ({ path: 'openspec/specs/' + name, before: contents(beforeRoot, name), after: contents(afterRoot, name) })).filter(item => item.before !== item.after) };
|
|
148
|
+
write(receipt, JSON.stringify(plan));
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
rmSync(staging, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (plan.activeHash !== activeHash)
|
|
155
|
+
throw new Error('Reviewed artifacts changed since the OpenSpec archive was prepared');
|
|
156
|
+
if (!new RegExp('^openspec/changes/archive/[0-9]{4}-[0-9]{2}-[0-9]{2}-' + change + '$').test(plan.destination))
|
|
157
|
+
throw new Error('Invalid OpenSpec archive receipt');
|
|
158
|
+
const targets = plan.writes.map(item => {
|
|
159
|
+
if (!item.path.startsWith('openspec/specs/'))
|
|
160
|
+
throw new Error('Invalid OpenSpec archive write');
|
|
161
|
+
const target = artifactPath(context.artifactRoot, item.path);
|
|
162
|
+
const current = existsSync(target) ? readFileSync(target, 'utf8') : null;
|
|
163
|
+
if (current !== item.before && current !== item.after)
|
|
164
|
+
throw new Error('Main specification changed during archive; refusing to overwrite: ' + item.path);
|
|
165
|
+
return { ...item, target };
|
|
166
|
+
});
|
|
167
|
+
// The external CLI ran asynchronously; recheck the candidate and artifacts before publishing.
|
|
168
|
+
signal?.throwIfAborted();
|
|
169
|
+
checkArchive(context);
|
|
170
|
+
// Validate every path and preimage before publishing the first output.
|
|
171
|
+
const destination = artifactPath(context.artifactRoot, plan.destination);
|
|
172
|
+
if (existsSync(destination))
|
|
173
|
+
throw new Error('Archive destination already exists');
|
|
174
|
+
for (const item of targets) {
|
|
175
|
+
if (item.after === null)
|
|
176
|
+
rmSync(item.target, { force: true });
|
|
177
|
+
else
|
|
178
|
+
write(item.target, item.after);
|
|
179
|
+
}
|
|
180
|
+
mkdirSync(path.dirname(destination), { recursive: true });
|
|
181
|
+
renameSync(active, destination);
|
|
141
182
|
}
|
|
142
|
-
|
|
143
|
-
* The reviewed specification documents are complete replacements, not deltas. */
|
|
144
|
-
export function archive(context, change) {
|
|
183
|
+
export async function archive(context, change, signal) {
|
|
145
184
|
const state = journal(context);
|
|
146
185
|
if (state.phases.archive.status !== 'done') {
|
|
147
|
-
const active =
|
|
186
|
+
const active = artifactPath(context.artifactRoot, 'openspec/changes/' + change);
|
|
148
187
|
if (existsSync(active)) {
|
|
149
188
|
checkArchive(context);
|
|
150
189
|
transitionPipeline(context, 'archive', 'running');
|
|
151
|
-
|
|
152
|
-
const specs = readdirSync(specsDir, { withFileTypes: true }).filter(item => item.isDirectory());
|
|
153
|
-
const writes = specs.map(spec => {
|
|
154
|
-
if (!SLUG.test(spec.name))
|
|
155
|
-
throw new Error('Invalid archived specification name');
|
|
156
|
-
return [child(context.artifactRoot, 'openspec/specs/' + spec.name + '/spec.md'), readFileSync(child(context.artifactRoot, 'openspec/changes/' + change + '/specs/' + spec.name + '/spec.md'), 'utf8')];
|
|
157
|
-
});
|
|
158
|
-
const destination = child(context.artifactRoot, 'openspec/changes/archive/' + state.createdAt.slice(0, 10) + '-' + change);
|
|
159
|
-
if (existsSync(destination))
|
|
160
|
-
throw new Error('Archive destination already exists');
|
|
161
|
-
for (const [file, content] of writes)
|
|
162
|
-
write(file, content);
|
|
163
|
-
mkdirSync(path.dirname(destination), { recursive: true });
|
|
164
|
-
renameSync(active, destination);
|
|
190
|
+
await archiveWithOpenSpec(context, change, active, signal);
|
|
165
191
|
}
|
|
166
192
|
// If the process died after rename, Core rechecks the saved archive approval.
|
|
167
193
|
transitionPipeline(context, 'archive', 'done');
|