devcontour-harness 0.1.0-alpha.12 → 0.1.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -2
- package/docs/architecture.md +6 -0
- package/docs/connectors.md +26 -0
- package/docs/contracts.md +100 -0
- package/docs/quickstart.md +1 -1
- package/docs/release-notes/0.1.0-alpha.13.md +91 -0
- package/docs/release-notes/0.1.0-alpha.14.md +69 -0
- package/docs/release-notes/0.1.0-alpha.15.md +62 -0
- package/docs/release-notes/0.1.0-alpha.16.md +36 -0
- package/docs/turbo-starter.md +2 -1
- package/docs/upgrade.md +15 -14
- package/examples/turbo-starter/setup.mjs +9 -2
- package/lib/connectors/files.js +2 -0
- package/lib/contracts/checks.d.ts +119 -0
- package/lib/contracts/checks.js +64 -1
- package/lib/contracts/execution.d.ts +28 -0
- package/lib/contracts/published.d.ts +28 -0
- package/lib/control/harness.js +2 -1
- package/lib/control/recovery.d.ts +25 -0
- package/lib/engine/runner.js +28 -3
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/integrations/connector/commands.d.ts +11 -4
- package/lib/integrations/connector/commands.js +16 -2
- package/lib/integrations/connector/config.d.ts +5 -0
- package/lib/integrations/connector/config.js +18 -1
- package/lib/integrations/connector/index.d.ts +1 -1
- package/lib/integrations/connector/index.js +1 -1
- package/lib/integrations/connector/snapshot.d.ts +22 -1
- package/lib/integrations/connector/snapshot.js +60 -3
- package/lib/integrations/connector/source.d.ts +4 -0
- package/lib/integrations/connector/source.js +1 -0
- package/lib/verification/acceptance.js +2 -1
- package/lib/verification/checks.d.ts +28 -0
- package/lib/verification/checks.js +270 -22
- package/lib/verification/corpus.d.ts +50 -0
- package/lib/verification/gates.js +14 -4
- package/lib/verification/property.d.ts +9 -0
- package/lib/verification/property.js +11 -2
- package/lib/verification/self-check.js +12 -12
- package/package.json +1 -1
- package/schemas/contract-document.schema.json +161 -0
- package/skills/harness-setup/SKILL.md +191 -69
- package/templates/controller-mode/README.md +5 -0
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
writeFileSync,
|
|
35
35
|
} from 'node:fs';
|
|
36
36
|
import { tmpdir } from 'node:os';
|
|
37
|
-
import { dirname, join, relative, resolve } from 'node:path';
|
|
37
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
38
38
|
import { fileURLToPath } from 'node:url';
|
|
39
39
|
import { parseArgs } from 'node:util';
|
|
40
40
|
|
|
@@ -257,10 +257,17 @@ export function changeFiles(fixture, patch) {
|
|
|
257
257
|
*/
|
|
258
258
|
export function controllerContext(fixture, dir) {
|
|
259
259
|
const target = join(dir, 'controller-mode');
|
|
260
|
+
// Пересборка начинается с пустого каталога: справки удалённых AGENTS.md не
|
|
261
|
+
// остаются, а закоммиченный пакет и .harness/ не попадают в свои же справки.
|
|
262
|
+
rmSync(target, { recursive: true, force: true });
|
|
260
263
|
mkdirSync(join(target, 'references'), { recursive: true });
|
|
261
264
|
const overlay = readFileSync(join(CONTROLLER_TEMPLATE, 'AGENTS.controller.md'));
|
|
262
265
|
writeFileSync(join(target, 'AGENTS.controller.md'), overlay);
|
|
263
|
-
const
|
|
266
|
+
const inside = relative(fixture.repo, resolve(target));
|
|
267
|
+
const exclude = [':(exclude).harness/**'];
|
|
268
|
+
if (inside && !inside.startsWith('..') && !isAbsolute(inside))
|
|
269
|
+
exclude.push(`:(exclude)${inside.split(sep).join('/')}/**`);
|
|
270
|
+
const product = git(fixture.repo, ['ls-files', '--', 'AGENTS.md', '**/AGENTS.md', ...exclude])
|
|
264
271
|
.split('\n')
|
|
265
272
|
.filter(Boolean);
|
|
266
273
|
const references = product.map((path) => {
|
package/lib/connectors/files.js
CHANGED
|
@@ -71,6 +71,8 @@ export class FileContextSource {
|
|
|
71
71
|
const base = realpathSync(this.dir);
|
|
72
72
|
const sources = [...new Set(ids)].map((id) => {
|
|
73
73
|
const root = resolve(base, id);
|
|
74
|
+
if (root.startsWith(base + sep) && existsSync(root) && lstatSync(root).isSymbolicLink())
|
|
75
|
+
throw new HarnessError('INVALID_INPUT', `Контекст не читается через symlink: ${id}`);
|
|
74
76
|
if (!root.startsWith(base + sep) || !existsSync(root) || !lstatSync(root).isDirectory())
|
|
75
77
|
throw new HarnessError('NOT_READY', `Источник контекста ${id} не найден в ${this.dir}`);
|
|
76
78
|
const pin = (path) => {
|
|
@@ -2,6 +2,41 @@ import { z } from 'zod';
|
|
|
2
2
|
export declare const promiseKinds: readonly ["functional", "accuracy", "optimality", "completeness", "performance", "invariant", "other"];
|
|
3
3
|
/** Вычислительные обещания: без независимого способа проверки их не утверждают. */
|
|
4
4
|
export declare const strongPromises: ReadonlySet<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Проверка по закреплённым эталонным наборам (`verification: 'reference'`).
|
|
7
|
+
*
|
|
8
|
+
* Ответы независимого эталона (решатель, полный перебор, таблица DP)
|
|
9
|
+
* получены заранее, сверены с другим способом на семействах входов и
|
|
10
|
+
* закреплены артефактами контракта. Gate сверяет реализацию с наборами и
|
|
11
|
+
* отчитывается тем же отчётом свойств: число проверенных входов по свойству
|
|
12
|
+
* и по семействам. Каждый набор принадлежит одному gate: его минимум и
|
|
13
|
+
* минимумы семейств — доля этого gate в покрытии обязательства. Обобщения за
|
|
14
|
+
* пределы наборов такой способ не доказывает — это называет `gaps`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const referenceSchema: z.ZodObject<{
|
|
17
|
+
oracle: z.ZodObject<{
|
|
18
|
+
description: z.ZodString;
|
|
19
|
+
limits: z.ZodString;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
crossChecks: z.ZodArray<z.ZodObject<{
|
|
22
|
+
family: z.ZodString;
|
|
23
|
+
against: z.ZodString;
|
|
24
|
+
matches: z.ZodNumber;
|
|
25
|
+
total: z.ZodNumber;
|
|
26
|
+
artifacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
|
+
}, z.core.$strict>>;
|
|
28
|
+
sets: z.ZodArray<z.ZodObject<{
|
|
29
|
+
gate: z.ZodString;
|
|
30
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
31
|
+
minCases: z.ZodNumber;
|
|
32
|
+
families: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
33
|
+
id: z.ZodString;
|
|
34
|
+
minCases: z.ZodNumber;
|
|
35
|
+
}, z.core.$strict>>>;
|
|
36
|
+
}, z.core.$strict>>;
|
|
37
|
+
gaps: z.ZodString;
|
|
38
|
+
}, z.core.$strict>;
|
|
39
|
+
export type ReferenceVerification = z.infer<typeof referenceSchema>;
|
|
5
40
|
export declare const obligationSchema: z.ZodObject<{
|
|
6
41
|
property: z.ZodString;
|
|
7
42
|
requirements: z.ZodArray<z.ZodString>;
|
|
@@ -63,6 +98,34 @@ export declare const obligationSchema: z.ZodObject<{
|
|
|
63
98
|
environment: z.ZodString;
|
|
64
99
|
}, z.core.$strict>>;
|
|
65
100
|
}, z.core.$strict>;
|
|
101
|
+
gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
102
|
+
verification: z.ZodOptional<z.ZodEnum<{
|
|
103
|
+
property: "property";
|
|
104
|
+
reference: "reference";
|
|
105
|
+
}>>;
|
|
106
|
+
reference: z.ZodOptional<z.ZodObject<{
|
|
107
|
+
oracle: z.ZodObject<{
|
|
108
|
+
description: z.ZodString;
|
|
109
|
+
limits: z.ZodString;
|
|
110
|
+
}, z.core.$strict>;
|
|
111
|
+
crossChecks: z.ZodArray<z.ZodObject<{
|
|
112
|
+
family: z.ZodString;
|
|
113
|
+
against: z.ZodString;
|
|
114
|
+
matches: z.ZodNumber;
|
|
115
|
+
total: z.ZodNumber;
|
|
116
|
+
artifacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
117
|
+
}, z.core.$strict>>;
|
|
118
|
+
sets: z.ZodArray<z.ZodObject<{
|
|
119
|
+
gate: z.ZodString;
|
|
120
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
121
|
+
minCases: z.ZodNumber;
|
|
122
|
+
families: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
123
|
+
id: z.ZodString;
|
|
124
|
+
minCases: z.ZodNumber;
|
|
125
|
+
}, z.core.$strict>>>;
|
|
126
|
+
}, z.core.$strict>>;
|
|
127
|
+
gaps: z.ZodString;
|
|
128
|
+
}, z.core.$strict>>;
|
|
66
129
|
hard: z.ZodDefault<z.ZodBoolean>;
|
|
67
130
|
feasibility: z.ZodOptional<z.ZodObject<{
|
|
68
131
|
spike: z.ZodString;
|
|
@@ -134,6 +197,34 @@ export declare const checkDeclarationSchema: z.ZodObject<{
|
|
|
134
197
|
environment: z.ZodString;
|
|
135
198
|
}, z.core.$strict>>;
|
|
136
199
|
}, z.core.$strict>;
|
|
200
|
+
gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
201
|
+
verification: z.ZodOptional<z.ZodEnum<{
|
|
202
|
+
property: "property";
|
|
203
|
+
reference: "reference";
|
|
204
|
+
}>>;
|
|
205
|
+
reference: z.ZodOptional<z.ZodObject<{
|
|
206
|
+
oracle: z.ZodObject<{
|
|
207
|
+
description: z.ZodString;
|
|
208
|
+
limits: z.ZodString;
|
|
209
|
+
}, z.core.$strict>;
|
|
210
|
+
crossChecks: z.ZodArray<z.ZodObject<{
|
|
211
|
+
family: z.ZodString;
|
|
212
|
+
against: z.ZodString;
|
|
213
|
+
matches: z.ZodNumber;
|
|
214
|
+
total: z.ZodNumber;
|
|
215
|
+
artifacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
216
|
+
}, z.core.$strict>>;
|
|
217
|
+
sets: z.ZodArray<z.ZodObject<{
|
|
218
|
+
gate: z.ZodString;
|
|
219
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
220
|
+
minCases: z.ZodNumber;
|
|
221
|
+
families: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
222
|
+
id: z.ZodString;
|
|
223
|
+
minCases: z.ZodNumber;
|
|
224
|
+
}, z.core.$strict>>>;
|
|
225
|
+
}, z.core.$strict>>;
|
|
226
|
+
gaps: z.ZodString;
|
|
227
|
+
}, z.core.$strict>>;
|
|
137
228
|
hard: z.ZodDefault<z.ZodBoolean>;
|
|
138
229
|
feasibility: z.ZodOptional<z.ZodObject<{
|
|
139
230
|
spike: z.ZodString;
|
|
@@ -233,6 +324,34 @@ export declare const contractDocumentSchema: z.ZodObject<{
|
|
|
233
324
|
environment: z.ZodString;
|
|
234
325
|
}, z.core.$strict>>;
|
|
235
326
|
}, z.core.$strict>;
|
|
327
|
+
gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
328
|
+
verification: z.ZodOptional<z.ZodEnum<{
|
|
329
|
+
property: "property";
|
|
330
|
+
reference: "reference";
|
|
331
|
+
}>>;
|
|
332
|
+
reference: z.ZodOptional<z.ZodObject<{
|
|
333
|
+
oracle: z.ZodObject<{
|
|
334
|
+
description: z.ZodString;
|
|
335
|
+
limits: z.ZodString;
|
|
336
|
+
}, z.core.$strict>;
|
|
337
|
+
crossChecks: z.ZodArray<z.ZodObject<{
|
|
338
|
+
family: z.ZodString;
|
|
339
|
+
against: z.ZodString;
|
|
340
|
+
matches: z.ZodNumber;
|
|
341
|
+
total: z.ZodNumber;
|
|
342
|
+
artifacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
343
|
+
}, z.core.$strict>>;
|
|
344
|
+
sets: z.ZodArray<z.ZodObject<{
|
|
345
|
+
gate: z.ZodString;
|
|
346
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
347
|
+
minCases: z.ZodNumber;
|
|
348
|
+
families: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
349
|
+
id: z.ZodString;
|
|
350
|
+
minCases: z.ZodNumber;
|
|
351
|
+
}, z.core.$strict>>>;
|
|
352
|
+
}, z.core.$strict>>;
|
|
353
|
+
gaps: z.ZodString;
|
|
354
|
+
}, z.core.$strict>>;
|
|
236
355
|
hard: z.ZodDefault<z.ZodBoolean>;
|
|
237
356
|
feasibility: z.ZodOptional<z.ZodObject<{
|
|
238
357
|
spike: z.ZodString;
|
package/lib/contracts/checks.js
CHANGED
|
@@ -31,6 +31,57 @@ export const strongPromises = new Set([
|
|
|
31
31
|
'completeness',
|
|
32
32
|
'performance',
|
|
33
33
|
]);
|
|
34
|
+
/**
|
|
35
|
+
* Проверка по закреплённым эталонным наборам (`verification: 'reference'`).
|
|
36
|
+
*
|
|
37
|
+
* Ответы независимого эталона (решатель, полный перебор, таблица DP)
|
|
38
|
+
* получены заранее, сверены с другим способом на семействах входов и
|
|
39
|
+
* закреплены артефактами контракта. Gate сверяет реализацию с наборами и
|
|
40
|
+
* отчитывается тем же отчётом свойств: число проверенных входов по свойству
|
|
41
|
+
* и по семействам. Каждый набор принадлежит одному gate: его минимум и
|
|
42
|
+
* минимумы семейств — доля этого gate в покрытии обязательства. Обобщения за
|
|
43
|
+
* пределы наборов такой способ не доказывает — это называет `gaps`.
|
|
44
|
+
*/
|
|
45
|
+
export const referenceSchema = z.strictObject({
|
|
46
|
+
/** Эталон и его пределы: версия, предел времени, что исключено из наборов. */
|
|
47
|
+
oracle: z.strictObject({ description: text(), limits: text() }),
|
|
48
|
+
/** Сверка эталона с другим способом: семейство, способ, совпало из всего. */
|
|
49
|
+
crossChecks: z
|
|
50
|
+
.array(z.strictObject({
|
|
51
|
+
family: text(200),
|
|
52
|
+
against: text(500),
|
|
53
|
+
matches: z.number().int().min(0).max(100_000_000),
|
|
54
|
+
total: z.number().int().min(1).max(100_000_000),
|
|
55
|
+
/**
|
|
56
|
+
* Закреплённые файлы, воспроизводящие сверку (скрипт, журнал) — среди
|
|
57
|
+
* артефактов контракта. Без них числа — утверждение автора.
|
|
58
|
+
*/
|
|
59
|
+
artifacts: z.array(relativePathSchema).min(1).max(10).optional(),
|
|
60
|
+
}))
|
|
61
|
+
.min(1)
|
|
62
|
+
.max(10),
|
|
63
|
+
sets: z
|
|
64
|
+
.array(z.strictObject({
|
|
65
|
+
/** Gate, который сверяет реализацию с этим набором. */
|
|
66
|
+
gate: idSchema,
|
|
67
|
+
/** Файлы набора — среди артефактов контракта. */
|
|
68
|
+
artifacts: z.array(relativePathSchema).min(1).max(10),
|
|
69
|
+
/** Сколько входов набора gate обязан проверить за прогон. */
|
|
70
|
+
minCases: z.number().int().min(1).max(1_000_000),
|
|
71
|
+
/** Доля семейств покрытия обязательства в этом наборе. */
|
|
72
|
+
families: z
|
|
73
|
+
.array(z.strictObject({
|
|
74
|
+
id: familyIdSchema,
|
|
75
|
+
minCases: z.number().int().min(1).max(1_000_000),
|
|
76
|
+
}))
|
|
77
|
+
.max(20)
|
|
78
|
+
.default([]),
|
|
79
|
+
}))
|
|
80
|
+
.min(1)
|
|
81
|
+
.max(20),
|
|
82
|
+
/** Чего наборы не доказывают: обобщение за пределы входов, классы вне наборов. */
|
|
83
|
+
gaps: text(),
|
|
84
|
+
});
|
|
34
85
|
export const obligationSchema = z.strictObject({
|
|
35
86
|
property: propertyIdSchema,
|
|
36
87
|
/** Существующие требования или критерии приёмки, которые обязательство уточняет. */
|
|
@@ -79,6 +130,15 @@ export const obligationSchema = z.strictObject({
|
|
|
79
130
|
oracle: text(),
|
|
80
131
|
benchmark: z.strictObject({ method: text(), environment: text() }).optional(),
|
|
81
132
|
}),
|
|
133
|
+
/**
|
|
134
|
+
* Gates, исполняющие обязательство; без поля — gate договора. У
|
|
135
|
+
* генеративного обязательства каждый из них проверяет полное покрытие, у
|
|
136
|
+
* эталонного — свои наборы.
|
|
137
|
+
*/
|
|
138
|
+
gates: z.array(idSchema).min(1).max(10).optional(),
|
|
139
|
+
/** Способ исполнения: `property` (без поля) — генератор; `reference` — эталонные наборы. */
|
|
140
|
+
verification: z.enum(['property', 'reference']).optional(),
|
|
141
|
+
reference: referenceSchema.optional(),
|
|
82
142
|
/** Тяжёлый точный алгоритм или жёсткое время: до реализации нужен ограниченный эксперимент. */
|
|
83
143
|
hard: z.boolean().default(false),
|
|
84
144
|
feasibility: z
|
|
@@ -90,7 +150,10 @@ export const obligationSchema = z.strictObject({
|
|
|
90
150
|
});
|
|
91
151
|
export const checkDeclarationSchema = z.strictObject({
|
|
92
152
|
version: z.literal(1),
|
|
93
|
-
/**
|
|
153
|
+
/**
|
|
154
|
+
* Gate проверки по умолчанию: исполняет обязательства без своих `gates`
|
|
155
|
+
* (генеративный, с закреплённым checker).
|
|
156
|
+
*/
|
|
94
157
|
gate: idSchema,
|
|
95
158
|
obligations: z.array(obligationSchema).min(1).max(30),
|
|
96
159
|
/**
|
|
@@ -2289,6 +2289,34 @@ export declare const localSchemas: {
|
|
|
2289
2289
|
environment: z.ZodString;
|
|
2290
2290
|
}, z.core.$strict>>;
|
|
2291
2291
|
}, z.core.$strict>;
|
|
2292
|
+
gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2293
|
+
verification: z.ZodOptional<z.ZodEnum<{
|
|
2294
|
+
property: "property";
|
|
2295
|
+
reference: "reference";
|
|
2296
|
+
}>>;
|
|
2297
|
+
reference: z.ZodOptional<z.ZodObject<{
|
|
2298
|
+
oracle: z.ZodObject<{
|
|
2299
|
+
description: z.ZodString;
|
|
2300
|
+
limits: z.ZodString;
|
|
2301
|
+
}, z.core.$strict>;
|
|
2302
|
+
crossChecks: z.ZodArray<z.ZodObject<{
|
|
2303
|
+
family: z.ZodString;
|
|
2304
|
+
against: z.ZodString;
|
|
2305
|
+
matches: z.ZodNumber;
|
|
2306
|
+
total: z.ZodNumber;
|
|
2307
|
+
artifacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2308
|
+
}, z.core.$strict>>;
|
|
2309
|
+
sets: z.ZodArray<z.ZodObject<{
|
|
2310
|
+
gate: z.ZodString;
|
|
2311
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
2312
|
+
minCases: z.ZodNumber;
|
|
2313
|
+
families: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
2314
|
+
id: z.ZodString;
|
|
2315
|
+
minCases: z.ZodNumber;
|
|
2316
|
+
}, z.core.$strict>>>;
|
|
2317
|
+
}, z.core.$strict>>;
|
|
2318
|
+
gaps: z.ZodString;
|
|
2319
|
+
}, z.core.$strict>>;
|
|
2292
2320
|
hard: z.ZodDefault<z.ZodBoolean>;
|
|
2293
2321
|
feasibility: z.ZodOptional<z.ZodObject<{
|
|
2294
2322
|
spike: z.ZodString;
|
|
@@ -760,6 +760,34 @@ export declare const publishedSchemas: {
|
|
|
760
760
|
environment: z.ZodString;
|
|
761
761
|
}, z.core.$strict>>;
|
|
762
762
|
}, z.core.$strict>;
|
|
763
|
+
gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
764
|
+
verification: z.ZodOptional<z.ZodEnum<{
|
|
765
|
+
property: "property";
|
|
766
|
+
reference: "reference";
|
|
767
|
+
}>>;
|
|
768
|
+
reference: z.ZodOptional<z.ZodObject<{
|
|
769
|
+
oracle: z.ZodObject<{
|
|
770
|
+
description: z.ZodString;
|
|
771
|
+
limits: z.ZodString;
|
|
772
|
+
}, z.core.$strict>;
|
|
773
|
+
crossChecks: z.ZodArray<z.ZodObject<{
|
|
774
|
+
family: z.ZodString;
|
|
775
|
+
against: z.ZodString;
|
|
776
|
+
matches: z.ZodNumber;
|
|
777
|
+
total: z.ZodNumber;
|
|
778
|
+
artifacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
779
|
+
}, z.core.$strict>>;
|
|
780
|
+
sets: z.ZodArray<z.ZodObject<{
|
|
781
|
+
gate: z.ZodString;
|
|
782
|
+
artifacts: z.ZodArray<z.ZodString>;
|
|
783
|
+
minCases: z.ZodNumber;
|
|
784
|
+
families: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
785
|
+
id: z.ZodString;
|
|
786
|
+
minCases: z.ZodNumber;
|
|
787
|
+
}, z.core.$strict>>>;
|
|
788
|
+
}, z.core.$strict>>;
|
|
789
|
+
gaps: z.ZodString;
|
|
790
|
+
}, z.core.$strict>>;
|
|
763
791
|
hard: z.ZodDefault<z.ZodBoolean>;
|
|
764
792
|
feasibility: z.ZodOptional<z.ZodObject<{
|
|
765
793
|
spike: z.ZodString;
|
package/lib/control/harness.js
CHANGED
|
@@ -18,7 +18,7 @@ import { policySourceChanged } from "./provenance.js";
|
|
|
18
18
|
import { JointRunner } from "../engine/joint-runner.js";
|
|
19
19
|
import { git } from "../environment/process.js";
|
|
20
20
|
import { analyzeRecovery, applyCases } from "./recovery.js";
|
|
21
|
-
import { declarationProblems } from "../verification/checks.js";
|
|
21
|
+
import { declarationProblems, referenceNote } from "../verification/checks.js";
|
|
22
22
|
import { spikeOf, spikesOf } from "../verification/spike.js";
|
|
23
23
|
import { staleArtifacts } from "../verification/confirm.js";
|
|
24
24
|
const stateFile = (dir) => join(dir, 'state.sqlite');
|
|
@@ -193,6 +193,7 @@ export class Harness {
|
|
|
193
193
|
: 'You independently review a contract document. Check that every obligation is feasible and measurable. Do not edit files.',
|
|
194
194
|
JSON.stringify(input.kind === 'plan' ? input.graph : input.contract, null, 2),
|
|
195
195
|
input.kind === 'contract' ? this.spikeNote(input.contract) : '',
|
|
196
|
+
input.kind === 'contract' ? referenceNote(input.contract.checks) : '',
|
|
196
197
|
],
|
|
197
198
|
...(context ? { context } : {}),
|
|
198
199
|
...(input.kind === 'contract' ? { contract: input.contract } : {}),
|
|
@@ -139,6 +139,31 @@ export declare function recoveryBrief(view: ExecutionView, work: WorkState, poli
|
|
|
139
139
|
};
|
|
140
140
|
hard: boolean;
|
|
141
141
|
explanation?: string | undefined;
|
|
142
|
+
gates?: string[] | undefined;
|
|
143
|
+
verification?: "property" | "reference" | undefined;
|
|
144
|
+
reference?: {
|
|
145
|
+
oracle: {
|
|
146
|
+
description: string;
|
|
147
|
+
limits: string;
|
|
148
|
+
};
|
|
149
|
+
crossChecks: {
|
|
150
|
+
family: string;
|
|
151
|
+
against: string;
|
|
152
|
+
matches: number;
|
|
153
|
+
total: number;
|
|
154
|
+
artifacts?: string[] | undefined;
|
|
155
|
+
}[];
|
|
156
|
+
sets: {
|
|
157
|
+
gate: string;
|
|
158
|
+
artifacts: string[];
|
|
159
|
+
minCases: number;
|
|
160
|
+
families: {
|
|
161
|
+
id: string;
|
|
162
|
+
minCases: number;
|
|
163
|
+
}[];
|
|
164
|
+
}[];
|
|
165
|
+
gaps: string;
|
|
166
|
+
} | undefined;
|
|
142
167
|
feasibility?: {
|
|
143
168
|
spike: string;
|
|
144
169
|
digest: string;
|
package/lib/engine/runner.js
CHANGED
|
@@ -19,11 +19,11 @@ import { REQUIREMENT_GATE, assertRequirements } from "../verification/requiremen
|
|
|
19
19
|
import { BlockedError, classifyFailure } from "./classify.js";
|
|
20
20
|
import { TaskFailure } from "./failure.js";
|
|
21
21
|
import { prompt } from "./prompt.js";
|
|
22
|
-
import { contractDocuments, orderCheckManifest } from "../verification/checks.js";
|
|
22
|
+
import { contractDocuments, orderCheckManifest, referenceCodeNote, referenceSetFiles, setFileReference, } from "../verification/checks.js";
|
|
23
23
|
import { CorpusStore, localRoots } from "../verification/corpus.js";
|
|
24
24
|
import { submitCase } from "../verification/confirm.js";
|
|
25
25
|
import { runSpike } from "../verification/spike.js";
|
|
26
|
-
import { propertySeed } from "../verification/property.js";
|
|
26
|
+
import { expectsReference, propertySeed } from "../verification/property.js";
|
|
27
27
|
import { selfCheckNote, writeSelfCheck } from "../verification/self-check.js";
|
|
28
28
|
import { measuredInvoke } from "./measured.js";
|
|
29
29
|
import { continuableDraft, implementationBasis } from "./reuse.js";
|
|
@@ -491,6 +491,24 @@ export class Runner {
|
|
|
491
491
|
const forbidden = changed.filter((p) => within(p, protectedList));
|
|
492
492
|
if (forbidden.length)
|
|
493
493
|
throw new TaskFailure('scope-violation', `Изменены защищённые файлы: ${forbidden.join(', ')}`);
|
|
494
|
+
// Ответы эталонных наборов лежат в рабочем дереве: изменённый код,
|
|
495
|
+
// который называет файл набора строковым литералом, читает эталон, а не
|
|
496
|
+
// решает. Тесты исполнителя (не артефакты контракта) проверяются так же:
|
|
497
|
+
// через них ответы тоже можно подать реализации.
|
|
498
|
+
const sets = this.engine.read((v) => referenceSetFiles(contractDocuments(work.order, v), repo.id));
|
|
499
|
+
if (sets.length) {
|
|
500
|
+
const named = [];
|
|
501
|
+
for (const path of changed) {
|
|
502
|
+
const text = await git(cwd, 'show', `${range ? range[1] : ''}:${path}`).catch(() => '');
|
|
503
|
+
if (text.length > 4 * 1024 * 1024)
|
|
504
|
+
continue;
|
|
505
|
+
const hit = setFileReference(path, text, sets);
|
|
506
|
+
if (hit)
|
|
507
|
+
named.push(`${path} → ${hit}`);
|
|
508
|
+
}
|
|
509
|
+
if (named.length)
|
|
510
|
+
throw new TaskFailure('scope-violation', `Реализация ссылается на файлы эталонных наборов: ${named.join(', ')}`);
|
|
511
|
+
}
|
|
494
512
|
if (!changed.length)
|
|
495
513
|
throw new TaskFailure('empty-result', 'Изменений нет: поручение отчиталось о выполнении пустым коммитом');
|
|
496
514
|
const role = roleBinding(this.policy, work.order.role, work.order.repositoryId)?.writePaths;
|
|
@@ -990,6 +1008,10 @@ export class Runner {
|
|
|
990
1008
|
await this.timed(attempt, stage, () => this.runOneGate(attempt, cwd, sha, phase, gate, artifacts, signal, execution, consumer));
|
|
991
1009
|
}
|
|
992
1010
|
async runOneGate(attempt, cwd, sha, phase, gate, artifacts, signal, execution, consumer) {
|
|
1011
|
+
// Генеративный gate — всегда; gate эталонных наборов — когда манифест
|
|
1012
|
+
// поручения ждёт от него отчёта (без договора — прежний уровень).
|
|
1013
|
+
const run = this.propertyRun(attempt, gate, consumer);
|
|
1014
|
+
const property = gate.property || expectsReference(run.expected) ? run : undefined;
|
|
993
1015
|
await runGate({
|
|
994
1016
|
policy: this.policy,
|
|
995
1017
|
record: (e) => this.engine.evidence(attempt.id, attempt.token, e),
|
|
@@ -1004,7 +1026,7 @@ export class Runner {
|
|
|
1004
1026
|
execution,
|
|
1005
1027
|
boundary: this.boundary(consumer?.dependencies ?? attempt.dependencies),
|
|
1006
1028
|
...(consumer ? { consumer: consumer.repositoryId } : {}),
|
|
1007
|
-
...(
|
|
1029
|
+
...(property ? { property } : {}),
|
|
1008
1030
|
});
|
|
1009
1031
|
}
|
|
1010
1032
|
/**
|
|
@@ -1602,6 +1624,9 @@ export class Runner {
|
|
|
1602
1624
|
}))));
|
|
1603
1625
|
// Согласованные контракты поручения — их текстом, по закреплённому digest.
|
|
1604
1626
|
const contracts = contractDocuments(v.work(attempt.workId).order, v);
|
|
1627
|
+
const sets = referenceCodeNote(referenceSetFiles(contracts), review);
|
|
1628
|
+
if (sets)
|
|
1629
|
+
out.push(sets);
|
|
1605
1630
|
if (contracts.length)
|
|
1606
1631
|
out.push('Approved contracts: ' +
|
|
1607
1632
|
JSON.stringify(contracts.map((c) => ({ id: c.id, title: c.title, content: c.content }))));
|
package/lib/index.d.ts
CHANGED
|
@@ -31,8 +31,8 @@ export { callDurationMs, callRecords, usageTotals } from './engine/usage.ts';
|
|
|
31
31
|
* ожидаемая проверка поручения, сверка отчёта свойств, закреплённые версии
|
|
32
32
|
* корпуса, подтверждение примера эталоном и переносимая форма корпуса.
|
|
33
33
|
*/
|
|
34
|
-
export { contractDocuments, declarationProblems, corpusBindingHolds, effectiveChecks, orderCheckManifest, orderPropertyProblems, suiteBasis, } from './verification/checks.ts';
|
|
35
|
-
export { corpusWitness, manifestDigest, propertyCoverage, propertyFailure, propertySeed, readPropertyReport, type CheckManifest, } from './verification/property.ts';
|
|
34
|
+
export { contractDocuments, declarationProblems, corpusBindingHolds, effectiveChecks, isReference, obligationGates, orderCheckManifest, orderPropertyProblems, referenceCodeNote, referenceNote, referenceSetFiles, setFileReference, suiteBasis, } from './verification/checks.ts';
|
|
35
|
+
export { corpusWitness, expectsReference, manifestDigest, propertyCoverage, propertyFailure, propertySeed, readPropertyReport, type CheckManifest, } from './verification/property.ts';
|
|
36
36
|
export { CorpusStore, canonicalJson, caseId, corpusFile, parseCanonical, versionOf, portableSuite, scrubLocalPaths, scrubPortable, verifySuite, type PortableSuite, } from './verification/corpus.ts';
|
|
37
37
|
export { caseHooks, caseInputSchema, staleArtifacts, submitCase, type CaseRequest, type ConfirmContext, } from './verification/confirm.ts';
|
|
38
38
|
export { assertResult, pinnedCorpus, propertyProofProblem, resultProblems, type ReceiverContext, type ReceiverPolicy, } from './verification/acceptance.ts';
|
package/lib/index.js
CHANGED
|
@@ -31,8 +31,8 @@ export { callDurationMs, callRecords, usageTotals } from "./engine/usage.js";
|
|
|
31
31
|
* ожидаемая проверка поручения, сверка отчёта свойств, закреплённые версии
|
|
32
32
|
* корпуса, подтверждение примера эталоном и переносимая форма корпуса.
|
|
33
33
|
*/
|
|
34
|
-
export { contractDocuments, declarationProblems, corpusBindingHolds, effectiveChecks, orderCheckManifest, orderPropertyProblems, suiteBasis, } from "./verification/checks.js";
|
|
35
|
-
export { corpusWitness, manifestDigest, propertyCoverage, propertyFailure, propertySeed, readPropertyReport, } from "./verification/property.js";
|
|
34
|
+
export { contractDocuments, declarationProblems, corpusBindingHolds, effectiveChecks, isReference, obligationGates, orderCheckManifest, orderPropertyProblems, referenceCodeNote, referenceNote, referenceSetFiles, setFileReference, suiteBasis, } from "./verification/checks.js";
|
|
35
|
+
export { corpusWitness, expectsReference, manifestDigest, propertyCoverage, propertyFailure, propertySeed, readPropertyReport, } from "./verification/property.js";
|
|
36
36
|
export { CorpusStore, canonicalJson, caseId, corpusFile, parseCanonical, versionOf, portableSuite, scrubLocalPaths, scrubPortable, verifySuite, } from "./verification/corpus.js";
|
|
37
37
|
export { caseHooks, caseInputSchema, staleArtifacts, submitCase, } from "./verification/confirm.js";
|
|
38
38
|
export { assertResult, pinnedCorpus, propertyProofProblem, resultProblems, } from "./verification/acceptance.js";
|
|
@@ -63,6 +63,7 @@ export declare class Connector {
|
|
|
63
63
|
requiredDocuments: string[];
|
|
64
64
|
repositoryId?: string | undefined;
|
|
65
65
|
gates?: string[] | undefined;
|
|
66
|
+
context?: string[] | undefined;
|
|
66
67
|
}[];
|
|
67
68
|
completion: {
|
|
68
69
|
mode: "complete" | "review-only";
|
|
@@ -85,6 +86,7 @@ export declare class Connector {
|
|
|
85
86
|
client: string;
|
|
86
87
|
};
|
|
87
88
|
stateDir?: string | undefined;
|
|
89
|
+
contextDir?: string | undefined;
|
|
88
90
|
} | {
|
|
89
91
|
provider: "relay-v2-cli";
|
|
90
92
|
relay: Record<string, unknown> & {
|
|
@@ -115,6 +117,7 @@ export declare class Connector {
|
|
|
115
117
|
requiredDocuments: string[];
|
|
116
118
|
repositoryId?: string | undefined;
|
|
117
119
|
gates?: string[] | undefined;
|
|
120
|
+
context?: string[] | undefined;
|
|
118
121
|
}[];
|
|
119
122
|
completion: {
|
|
120
123
|
mode: "complete" | "review-only";
|
|
@@ -137,7 +140,10 @@ export declare class Connector {
|
|
|
137
140
|
client: string;
|
|
138
141
|
};
|
|
139
142
|
stateDir?: string | undefined;
|
|
143
|
+
contextDir?: string | undefined;
|
|
140
144
|
};
|
|
145
|
+
/** Каталог пакетов `tasks[].context`: `contextDir` конфигурации, иначе `work/context` рядом с ней. */
|
|
146
|
+
get contextDir(): string;
|
|
141
147
|
/** Состояние коннектора (создаётся при первой записи). */
|
|
142
148
|
get store(): ConnectorStore;
|
|
143
149
|
/** Только чтение: без создания файла; нет состояния — `undefined`. */
|
|
@@ -186,10 +192,6 @@ export declare class Connector {
|
|
|
186
192
|
* semantic digest: повтор неизменной постановки ничего не меняет.
|
|
187
193
|
*/
|
|
188
194
|
snapshot(outDir?: string): Promise<{
|
|
189
|
-
connector: string;
|
|
190
|
-
graphId: string;
|
|
191
|
-
graphDigest: string;
|
|
192
|
-
contextDigest: string;
|
|
193
195
|
revision: string;
|
|
194
196
|
created: boolean;
|
|
195
197
|
eligible: boolean;
|
|
@@ -209,6 +211,11 @@ export declare class Connector {
|
|
|
209
211
|
manifest: string;
|
|
210
212
|
};
|
|
211
213
|
snapshot: import("./snapshot.ts").BuiltSnapshot;
|
|
214
|
+
localContext?: import("./snapshot.ts").LocalContextManifest[] | undefined;
|
|
215
|
+
connector: string;
|
|
216
|
+
graphId: string;
|
|
217
|
+
graphDigest: string;
|
|
218
|
+
contextDigest: string;
|
|
212
219
|
}>;
|
|
213
220
|
/**
|
|
214
221
|
* Перед запуском — свежее чтение: semantic digest обязан совпасть с
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import { HarnessError, } from "../../contracts/index.js";
|
|
3
|
+
import { API_VERSION, HarnessError, } from "../../contracts/index.js";
|
|
4
4
|
import { redactor } from "../../environment/redaction.js";
|
|
5
5
|
import { connectorIdOf, loadConfig, orderIdOf, resolveFrom, storeFileOf, } from "./config.js";
|
|
6
6
|
import { RelaySink } from "./delivery.js";
|
|
7
7
|
import { HistoryRecorder } from "./history.js";
|
|
8
8
|
import { createTransportFor } from "./registry.js";
|
|
9
|
-
import { assertEligible, checkIdentity, matchCriterion, readStable, semanticDigest, } from "./snapshot.js";
|
|
9
|
+
import { assertEligible, checkIdentity, contextLimitIssues, localContext, matchCriterion, readStable, semanticDigest, } from "./snapshot.js";
|
|
10
10
|
import { RelayWorkSource } from "./source.js";
|
|
11
11
|
import { redactingTransport, settingsSecrets } from "./redact.js";
|
|
12
12
|
import { ConnectorStore } from "./store.js";
|
|
@@ -46,6 +46,10 @@ export class Connector {
|
|
|
46
46
|
get config() {
|
|
47
47
|
return this.loaded.config;
|
|
48
48
|
}
|
|
49
|
+
/** Каталог пакетов `tasks[].context`: `contextDir` конфигурации, иначе `work/context` рядом с ней. */
|
|
50
|
+
get contextDir() {
|
|
51
|
+
return resolveFrom(this.loaded.baseDir, this.config.contextDir ?? 'work/context');
|
|
52
|
+
}
|
|
49
53
|
/** Состояние коннектора (создаётся при первой записи). */
|
|
50
54
|
get store() {
|
|
51
55
|
this.storeInstance ??= new ConnectorStore(this.storeFile, { provider: this.config.provider, projectId: this.config.projectId }, this.options.onStoreWrite ? { onWrite: this.options.onStoreWrite } : {});
|
|
@@ -64,6 +68,7 @@ export class Connector {
|
|
|
64
68
|
config: this.config,
|
|
65
69
|
transport: this.transport,
|
|
66
70
|
store: this.store,
|
|
71
|
+
contextDir: this.contextDir,
|
|
67
72
|
});
|
|
68
73
|
}
|
|
69
74
|
get history() {
|
|
@@ -114,6 +119,14 @@ export class Connector {
|
|
|
114
119
|
return this.checkReport(undefined, false, [message], []);
|
|
115
120
|
}
|
|
116
121
|
const mapping = [];
|
|
122
|
+
try {
|
|
123
|
+
const local = await localContext(config, this.contextDir);
|
|
124
|
+
if (local.length)
|
|
125
|
+
problems.push(...contextLimitIssues({ apiVersion: API_VERSION, sources: local, missing: [] }));
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
problems.push(this.redact(error instanceof Error ? error.message : String(error)));
|
|
129
|
+
}
|
|
117
130
|
try {
|
|
118
131
|
const { reads } = await readStable(this.transport, config.tasks.map((t) => t.ref), config.limits.rereads);
|
|
119
132
|
reads.forEach((read, i) => {
|
|
@@ -202,6 +215,7 @@ export class Connector {
|
|
|
202
215
|
graphId: snapshot.graph.id,
|
|
203
216
|
graphDigest: snapshot.manifest.graphDigest,
|
|
204
217
|
contextDigest: snapshot.manifest.contextDigest,
|
|
218
|
+
...(snapshot.manifest.localContext ? { localContext: snapshot.manifest.localContext } : {}),
|
|
205
219
|
revision: snapshot.revision,
|
|
206
220
|
created,
|
|
207
221
|
eligible: snapshot.problems.length === 0,
|
|
@@ -40,6 +40,7 @@ export declare const taskMappingSchema: z.ZodObject<{
|
|
|
40
40
|
}>>;
|
|
41
41
|
}, z.core.$strict>>>;
|
|
42
42
|
requiredDocuments: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
43
|
+
context: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
44
|
}, z.core.$strict>;
|
|
44
45
|
export type TaskMapping = z.infer<typeof taskMappingSchema>;
|
|
45
46
|
export declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -51,6 +52,7 @@ export declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
51
52
|
projectId: z.ZodString;
|
|
52
53
|
actor: z.ZodString;
|
|
53
54
|
stateDir: z.ZodOptional<z.ZodString>;
|
|
55
|
+
contextDir: z.ZodOptional<z.ZodString>;
|
|
54
56
|
repository: z.ZodObject<{
|
|
55
57
|
id: z.ZodString;
|
|
56
58
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -72,6 +74,7 @@ export declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
72
74
|
}>>;
|
|
73
75
|
}, z.core.$strict>>>;
|
|
74
76
|
requiredDocuments: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
77
|
+
context: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
75
78
|
}, z.core.$strict>>;
|
|
76
79
|
completion: z.ZodDefault<z.ZodObject<{
|
|
77
80
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
@@ -122,6 +125,7 @@ export declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
122
125
|
projectId: z.ZodString;
|
|
123
126
|
actor: z.ZodString;
|
|
124
127
|
stateDir: z.ZodOptional<z.ZodString>;
|
|
128
|
+
contextDir: z.ZodOptional<z.ZodString>;
|
|
125
129
|
repository: z.ZodObject<{
|
|
126
130
|
id: z.ZodString;
|
|
127
131
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -143,6 +147,7 @@ export declare const connectorConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
143
147
|
}>>;
|
|
144
148
|
}, z.core.$strict>>>;
|
|
145
149
|
requiredDocuments: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
150
|
+
context: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
146
151
|
}, z.core.$strict>>;
|
|
147
152
|
completion: z.ZodDefault<z.ZodObject<{
|
|
148
153
|
mode: z.ZodDefault<z.ZodEnum<{
|