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.
Files changed (44) hide show
  1. package/README.md +45 -2
  2. package/docs/architecture.md +6 -0
  3. package/docs/connectors.md +26 -0
  4. package/docs/contracts.md +100 -0
  5. package/docs/quickstart.md +1 -1
  6. package/docs/release-notes/0.1.0-alpha.13.md +91 -0
  7. package/docs/release-notes/0.1.0-alpha.14.md +69 -0
  8. package/docs/release-notes/0.1.0-alpha.15.md +62 -0
  9. package/docs/release-notes/0.1.0-alpha.16.md +36 -0
  10. package/docs/turbo-starter.md +2 -1
  11. package/docs/upgrade.md +15 -14
  12. package/examples/turbo-starter/setup.mjs +9 -2
  13. package/lib/connectors/files.js +2 -0
  14. package/lib/contracts/checks.d.ts +119 -0
  15. package/lib/contracts/checks.js +64 -1
  16. package/lib/contracts/execution.d.ts +28 -0
  17. package/lib/contracts/published.d.ts +28 -0
  18. package/lib/control/harness.js +2 -1
  19. package/lib/control/recovery.d.ts +25 -0
  20. package/lib/engine/runner.js +28 -3
  21. package/lib/index.d.ts +2 -2
  22. package/lib/index.js +2 -2
  23. package/lib/integrations/connector/commands.d.ts +11 -4
  24. package/lib/integrations/connector/commands.js +16 -2
  25. package/lib/integrations/connector/config.d.ts +5 -0
  26. package/lib/integrations/connector/config.js +18 -1
  27. package/lib/integrations/connector/index.d.ts +1 -1
  28. package/lib/integrations/connector/index.js +1 -1
  29. package/lib/integrations/connector/snapshot.d.ts +22 -1
  30. package/lib/integrations/connector/snapshot.js +60 -3
  31. package/lib/integrations/connector/source.d.ts +4 -0
  32. package/lib/integrations/connector/source.js +1 -0
  33. package/lib/verification/acceptance.js +2 -1
  34. package/lib/verification/checks.d.ts +28 -0
  35. package/lib/verification/checks.js +270 -22
  36. package/lib/verification/corpus.d.ts +50 -0
  37. package/lib/verification/gates.js +14 -4
  38. package/lib/verification/property.d.ts +9 -0
  39. package/lib/verification/property.js +11 -2
  40. package/lib/verification/self-check.js +12 -12
  41. package/package.json +1 -1
  42. package/schemas/contract-document.schema.json +161 -0
  43. package/skills/harness-setup/SKILL.md +191 -69
  44. 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 product = git(fixture.repo, ['ls-files', '--', 'AGENTS.md', '**/AGENTS.md'])
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) => {
@@ -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;
@@ -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
- /** Gate, который исполняет проверку (генеративный, с закреплённым checker). */
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;
@@ -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;
@@ -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
- ...(gate.property ? { property: this.propertyRun(attempt, gate, consumer) } : {}),
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<{