devcontour-harness 0.1.0-alpha.13 → 0.1.0-alpha.18

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 (73) hide show
  1. package/README.md +48 -2
  2. package/docs/architecture.md +77 -3
  3. package/docs/contracts.md +100 -0
  4. package/docs/quickstart.md +1 -1
  5. package/docs/release-notes/0.1.0-alpha.14.md +69 -0
  6. package/docs/release-notes/0.1.0-alpha.15.md +62 -0
  7. package/docs/release-notes/0.1.0-alpha.16.md +36 -0
  8. package/docs/release-notes/0.1.0-alpha.17.md +86 -0
  9. package/docs/release-notes/0.1.0-alpha.18.md +48 -0
  10. package/docs/review-probes.md +9 -3
  11. package/docs/review-protocol.md +9 -0
  12. package/docs/upgrade.md +16 -14
  13. package/lib/agents/cli-adapter.js +14 -10
  14. package/lib/agents/review-probe.d.ts +9 -0
  15. package/lib/agents/review-probe.js +23 -1
  16. package/lib/agents/schemas.d.ts +206 -0
  17. package/lib/agents/schemas.js +37 -1
  18. package/lib/agents/usage.d.ts +22 -0
  19. package/lib/agents/usage.js +112 -6
  20. package/lib/contracts/checks.d.ts +119 -0
  21. package/lib/contracts/checks.js +64 -1
  22. package/lib/contracts/cli.d.ts +30 -0
  23. package/lib/contracts/diagnosis.d.ts +3 -1
  24. package/lib/contracts/diagnosis.js +2 -0
  25. package/lib/contracts/events.d.ts +12 -0
  26. package/lib/contracts/execution.d.ts +109 -5
  27. package/lib/contracts/execution.js +58 -0
  28. package/lib/contracts/failure.d.ts +17 -3
  29. package/lib/contracts/failure.js +4 -2
  30. package/lib/contracts/published.d.ts +67 -1
  31. package/lib/contracts/repair-policy.d.ts +7 -0
  32. package/lib/contracts/repair-policy.js +17 -0
  33. package/lib/contracts/review.d.ts +12 -0
  34. package/lib/contracts/review.js +19 -0
  35. package/lib/control/harness.d.ts +2 -2
  36. package/lib/control/harness.js +2 -1
  37. package/lib/control/recovery.d.ts +26 -1
  38. package/lib/engine/engine.d.ts +41 -14
  39. package/lib/engine/engine.js +92 -8
  40. package/lib/engine/failure.d.ts +1 -1
  41. package/lib/engine/failure.js +1 -1
  42. package/lib/engine/property-recovery.d.ts +1 -0
  43. package/lib/engine/property-recovery.js +4 -1
  44. package/lib/engine/repair.d.ts +26 -0
  45. package/lib/engine/repair.js +83 -1
  46. package/lib/engine/runner.d.ts +7 -0
  47. package/lib/engine/runner.js +207 -34
  48. package/lib/explain/state.js +4 -2
  49. package/lib/index.d.ts +6 -4
  50. package/lib/index.js +6 -4
  51. package/lib/projections/stop.d.ts +1 -1
  52. package/lib/projections/stop.js +16 -0
  53. package/lib/storage/document.d.ts +48 -8
  54. package/lib/verification/acceptance.js +2 -1
  55. package/lib/verification/already-satisfied.d.ts +44 -0
  56. package/lib/verification/already-satisfied.js +49 -0
  57. package/lib/verification/checks.d.ts +28 -0
  58. package/lib/verification/checks.js +270 -22
  59. package/lib/verification/corpus.d.ts +50 -0
  60. package/lib/verification/gate-scope.d.ts +36 -0
  61. package/lib/verification/gate-scope.js +97 -0
  62. package/lib/verification/gates-at.d.ts +67 -0
  63. package/lib/verification/gates-at.js +173 -0
  64. package/lib/verification/gates.d.ts +5 -0
  65. package/lib/verification/gates.js +20 -5
  66. package/lib/verification/property.d.ts +9 -0
  67. package/lib/verification/property.js +11 -2
  68. package/lib/verification/self-check.js +12 -12
  69. package/package.json +1 -1
  70. package/schemas/contract-document.schema.json +161 -0
  71. package/schemas/execution-document.schema.json +134 -0
  72. package/schemas/run-event.schema.json +4 -0
  73. package/schemas/stop-diagnosis.schema.json +2 -0
@@ -211,7 +211,7 @@ export declare class DocumentView {
211
211
  activeRunId?: string | undefined;
212
212
  resultSha?: string | undefined;
213
213
  failure?: string | undefined;
214
- failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "scope-violation" | "review-protocol" | "merge" | "budget" | undefined;
214
+ failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "unfinished" | "scope-violation" | "review-protocol" | "merge" | "budget" | "needs-lead" | undefined;
215
215
  failureFingerprint?: string | undefined;
216
216
  approvedAt?: string | undefined;
217
217
  approvedDigest?: string | undefined;
@@ -311,6 +311,7 @@ export declare class DocumentView {
311
311
  retryRun?: string | undefined;
312
312
  reason?: string | undefined;
313
313
  }[] | undefined;
314
+ neutralFailures?: number | undefined;
314
315
  } | undefined;
315
316
  works(graphId?: string): {
316
317
  id: string;
@@ -358,7 +359,7 @@ export declare class DocumentView {
358
359
  activeRunId?: string | undefined;
359
360
  resultSha?: string | undefined;
360
361
  failure?: string | undefined;
361
- failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "scope-violation" | "review-protocol" | "merge" | "budget" | undefined;
362
+ failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "unfinished" | "scope-violation" | "review-protocol" | "merge" | "budget" | "needs-lead" | undefined;
362
363
  failureFingerprint?: string | undefined;
363
364
  approvedAt?: string | undefined;
364
365
  approvedDigest?: string | undefined;
@@ -458,6 +459,7 @@ export declare class DocumentView {
458
459
  retryRun?: string | undefined;
459
460
  reason?: string | undefined;
460
461
  }[] | undefined;
462
+ neutralFailures?: number | undefined;
461
463
  }[];
462
464
  attempt(id: string): {
463
465
  id: string;
@@ -519,7 +521,7 @@ export declare class DocumentView {
519
521
  excluded?: {
520
522
  runId: string;
521
523
  status: string | null;
522
- reason: "not-started" | "legacy-unconfirmed" | "foreign-scope" | "unknown-status" | "unfinished" | "abandoned";
524
+ reason: "unfinished" | "not-started" | "legacy-unconfirmed" | "foreign-scope" | "unknown-status" | "abandoned";
523
525
  }[] | undefined;
524
526
  excludedTotal?: number | undefined;
525
527
  invalid?: number | undefined;
@@ -613,6 +615,19 @@ export declare class DocumentView {
613
615
  coverage?: string[] | undefined;
614
616
  } | undefined;
615
617
  checks?: string | undefined;
618
+ alreadySatisfied?: {
619
+ gates: string[];
620
+ evidence: string;
621
+ basis?: {
622
+ kind: "contract";
623
+ gates: string[];
624
+ } | {
625
+ kind: "dependency";
626
+ workId: string;
627
+ sha: string;
628
+ paths: string[];
629
+ } | undefined;
630
+ } | undefined;
616
631
  }[];
617
632
  model?: string | undefined;
618
633
  reviewerModel?: string | undefined;
@@ -625,7 +640,7 @@ export declare class DocumentView {
625
640
  integrationSha?: string | undefined;
626
641
  error?: string | undefined;
627
642
  blocked?: boolean | undefined;
628
- failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "scope-violation" | "review-protocol" | "merge" | "budget" | undefined;
643
+ failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "unfinished" | "scope-violation" | "review-protocol" | "merge" | "budget" | "needs-lead" | undefined;
629
644
  failureFingerprint?: string | undefined;
630
645
  requiredGates?: string[] | undefined;
631
646
  consumers?: {
@@ -691,6 +706,12 @@ export declare class DocumentView {
691
706
  }[] | undefined;
692
707
  properties?: string[] | undefined;
693
708
  unboundProperties?: string[] | undefined;
709
+ interrupted?: "shutdown" | undefined;
710
+ inputClasses?: string[] | undefined;
711
+ classified?: {
712
+ property: string;
713
+ inputClass: string;
714
+ }[] | undefined;
694
715
  episodeBasis?: string | undefined;
695
716
  implementationBasis?: string | undefined;
696
717
  reusedFrom?: {
@@ -709,7 +730,7 @@ export declare class DocumentView {
709
730
  kind: "candidate" | "unfinished";
710
731
  applied: boolean;
711
732
  files: string[];
712
- reason?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "scope-violation" | "review-protocol" | "merge" | "budget" | undefined;
733
+ reason?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "unfinished" | "scope-violation" | "review-protocol" | "merge" | "budget" | "needs-lead" | undefined;
713
734
  respecified?: boolean | undefined;
714
735
  } | undefined;
715
736
  callsRecorded?: true | undefined;
@@ -810,7 +831,7 @@ export declare class DocumentView {
810
831
  excluded?: {
811
832
  runId: string;
812
833
  status: string | null;
813
- reason: "not-started" | "legacy-unconfirmed" | "foreign-scope" | "unknown-status" | "unfinished" | "abandoned";
834
+ reason: "unfinished" | "not-started" | "legacy-unconfirmed" | "foreign-scope" | "unknown-status" | "abandoned";
814
835
  }[] | undefined;
815
836
  excludedTotal?: number | undefined;
816
837
  invalid?: number | undefined;
@@ -904,6 +925,19 @@ export declare class DocumentView {
904
925
  coverage?: string[] | undefined;
905
926
  } | undefined;
906
927
  checks?: string | undefined;
928
+ alreadySatisfied?: {
929
+ gates: string[];
930
+ evidence: string;
931
+ basis?: {
932
+ kind: "contract";
933
+ gates: string[];
934
+ } | {
935
+ kind: "dependency";
936
+ workId: string;
937
+ sha: string;
938
+ paths: string[];
939
+ } | undefined;
940
+ } | undefined;
907
941
  }[];
908
942
  model?: string | undefined;
909
943
  reviewerModel?: string | undefined;
@@ -916,7 +950,7 @@ export declare class DocumentView {
916
950
  integrationSha?: string | undefined;
917
951
  error?: string | undefined;
918
952
  blocked?: boolean | undefined;
919
- failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "scope-violation" | "review-protocol" | "merge" | "budget" | undefined;
953
+ failureKind?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "unfinished" | "scope-violation" | "review-protocol" | "merge" | "budget" | "needs-lead" | undefined;
920
954
  failureFingerprint?: string | undefined;
921
955
  requiredGates?: string[] | undefined;
922
956
  consumers?: {
@@ -982,6 +1016,12 @@ export declare class DocumentView {
982
1016
  }[] | undefined;
983
1017
  properties?: string[] | undefined;
984
1018
  unboundProperties?: string[] | undefined;
1019
+ interrupted?: "shutdown" | undefined;
1020
+ inputClasses?: string[] | undefined;
1021
+ classified?: {
1022
+ property: string;
1023
+ inputClass: string;
1024
+ }[] | undefined;
985
1025
  episodeBasis?: string | undefined;
986
1026
  implementationBasis?: string | undefined;
987
1027
  reusedFrom?: {
@@ -1000,7 +1040,7 @@ export declare class DocumentView {
1000
1040
  kind: "candidate" | "unfinished";
1001
1041
  applied: boolean;
1002
1042
  files: string[];
1003
- reason?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "scope-violation" | "review-protocol" | "merge" | "budget" | undefined;
1043
+ reason?: "gate" | "unknown" | "review" | "environment" | "cancelled" | "timeout" | "provider-auth" | "stale-base" | "empty-result" | "unfinished" | "scope-violation" | "review-protocol" | "merge" | "budget" | "needs-lead" | undefined;
1004
1044
  respecified?: boolean | undefined;
1005
1045
  } | undefined;
1006
1046
  callsRecorded?: true | undefined;
@@ -20,7 +20,8 @@ export function propertyGates(order, contracts) {
20
20
  const found = obligationOf(contracts, id);
21
21
  if (!found)
22
22
  return { problem: `свойство ${id} не объявлено в договорах поручения` };
23
- byGate.set(found.gate, [...(byGate.get(found.gate) ?? []), id]);
23
+ for (const gate of found.gates)
24
+ byGate.set(gate, [...(byGate.get(gate) ?? []), id]);
24
25
  }
25
26
  return { byGate };
26
27
  }
@@ -0,0 +1,44 @@
1
+ import type { AlreadySatisfied, ContractDocument, EvidenceRecord, GatePolicy } from '../contracts/index.ts';
2
+ /** Gates договоров проверки поручения. */
3
+ export declare const contractCheckGates: (contracts: ContractDocument[]) => string[];
4
+ /**
5
+ * Механическое основание считать работу поручения уже сделанной без изменений.
6
+ * `contract` — договор проверки поручения: его gates в области задачи, и
7
+ * reviewer на них сослался. `dependency` — зависимость поручения своим
8
+ * кандидатом (`sha`) изменила файлы его области записи (`paths`): работу
9
+ * сделала она. Записывает controller, а не reviewer.
10
+ */
11
+ export type AlreadySatisfiedBasis = NonNullable<NonNullable<EvidenceRecord['alreadySatisfied']>['basis']>;
12
+ declare const within: (path: string, prefixes: string[]) => boolean;
13
+ export interface AlreadySatisfiedInput {
14
+ /** Gates области задачи (исполненные попыткой). */
15
+ scope: Pick<GatePolicy, 'id' | 'kind'>[];
16
+ /** Gates договоров проверки поручения (все, а не только в области). */
17
+ contractGates: string[];
18
+ writeScope: string[];
19
+ dependsOn: string[];
20
+ claim: AlreadySatisfied | undefined;
21
+ /** Основание по зависимости, найденное controller (или записанное им). */
22
+ dependency?: Extract<AlreadySatisfiedBasis, {
23
+ kind: 'dependency';
24
+ }>;
25
+ }
26
+ export type AlreadySatisfiedVerdict = {
27
+ ok: true;
28
+ basis: AlreadySatisfiedBasis;
29
+ } | {
30
+ ok: false;
31
+ kind: 'empty-result' | 'needs-lead';
32
+ problem: string;
33
+ };
34
+ /**
35
+ * Правило «приёмка уже выполнена» — одно для ревью попытки и для
36
+ * `engine.finish`. Подтверждение reviewer необходимо, но не достаточно:
37
+ * зелёный общий gate на базе не доказывает, что новая работа есть (её тестов
38
+ * ещё нет). Достаточно одного из механических оснований: договор проверки
39
+ * поручения, все gates которого в области задачи и названы reviewer, или
40
+ * зависимость, изменившая область записи поручения. Без основания — решение
41
+ * ведущего (`needs-lead`), а не `done`.
42
+ */
43
+ export declare function alreadySatisfiedVerdict(input: AlreadySatisfiedInput): AlreadySatisfiedVerdict;
44
+ export { within as withinWriteScope };
@@ -0,0 +1,49 @@
1
+ import { obligationGates } from "./checks.js";
2
+ /** Gates договоров проверки поручения. */
3
+ export const contractCheckGates = (contracts) => contracts.flatMap((c) => c.checks ? c.checks.obligations.flatMap((o) => obligationGates(c.checks, o)) : []);
4
+ const within = (path, prefixes) => prefixes.some((p) => p === '.' || path === p || path.startsWith(p.endsWith('/') ? p : p + '/'));
5
+ /**
6
+ * Правило «приёмка уже выполнена» — одно для ревью попытки и для
7
+ * `engine.finish`. Подтверждение reviewer необходимо, но не достаточно:
8
+ * зелёный общий gate на базе не доказывает, что новая работа есть (её тестов
9
+ * ещё нет). Достаточно одного из механических оснований: договор проверки
10
+ * поручения, все gates которого в области задачи и названы reviewer, или
11
+ * зависимость, изменившая область записи поручения. Без основания — решение
12
+ * ведущего (`needs-lead`), а не `done`.
13
+ */
14
+ export function alreadySatisfiedVerdict(input) {
15
+ const empty = (problem) => ({
16
+ ok: false,
17
+ kind: 'empty-result',
18
+ problem,
19
+ });
20
+ const lead = (problem) => ({ ok: false, kind: 'needs-lead', problem });
21
+ const { claim } = input;
22
+ if (!claim)
23
+ return empty('ответ не содержит alreadySatisfied');
24
+ if (claim.evidence.replace(/\s+/g, '').length < 20)
25
+ return empty('основание alreadySatisfied.evidence короче 20 знаков');
26
+ const scope = new Set(input.scope.map((g) => g.id));
27
+ const outside = claim.gates.filter((g) => !scope.has(g));
28
+ if (outside.length)
29
+ return empty('названы gates вне области задачи: ' + outside.join(', '));
30
+ if (!claim.gates.some((id) => input.scope.some((g) => g.id === id && g.kind === 'test')))
31
+ return empty('не назван ни один test gate');
32
+ const contract = [...new Set(input.contractGates)];
33
+ const unscoped = contract.filter((g) => !scope.has(g));
34
+ if (unscoped.length)
35
+ return lead('проверки договора поручения вне области задачи: ' + unscoped.join(', '));
36
+ const missing = contract.filter((g) => !claim.gates.includes(g));
37
+ if (missing.length)
38
+ return empty('не названы проверки договора поручения: ' + missing.join(', '));
39
+ if (contract.length)
40
+ return { ok: true, basis: { kind: 'contract', gates: contract } };
41
+ const d = input.dependency;
42
+ if (d &&
43
+ input.dependsOn.includes(d.workId) &&
44
+ d.paths.length > 0 &&
45
+ d.paths.every((p) => within(p, input.writeScope)))
46
+ return { ok: true, basis: d };
47
+ return lead('нет механического основания: у поручения нет договора проверки, и ни одна его зависимость не меняла его область записи');
48
+ }
49
+ export { within as withinWriteScope };
@@ -8,9 +8,30 @@ import type { CheckManifest } from './property.ts';
8
8
  export declare function contractDocuments(order: Pick<WorkOrder, 'contracts'>, view: ExecutionView): ContractDocument[];
9
9
  type Declared = {
10
10
  contract: ContractDocument;
11
+ /** Первый gate обязательства (у обязательства без `gates` — gate договора). */
11
12
  gate: string;
13
+ gates: string[];
12
14
  obligation: Obligation;
13
15
  };
16
+ /** Gates обязательства: свои `gates` или gate договора. */
17
+ export declare function obligationGates(declaration: Pick<CheckDeclaration, 'gate'>, o: Obligation): string[];
18
+ /** Обязательство проверяется закреплёнными эталонными наборами. */
19
+ export declare const isReference: (o: Obligation) => boolean;
20
+ /**
21
+ * Файлы эталонных наборов договоров поручения (в репозитории `repositoryId`).
22
+ * Ответы наборов лежат в рабочем дереве: реализация, которая читает их или
23
+ * повторяет их ответы, прошла бы каждый набор, ничего не решив.
24
+ */
25
+ export declare function referenceSetFiles(contracts: ContractDocument[], repositoryId?: string): string[];
26
+ /**
27
+ * Какой файл набора называет изменённый файл: имя файла набора (и, значит,
28
+ * его путь) целым токеном внутри строкового литерала. Комментарии и файлы без кода не
29
+ * считаются — упоминание набора в них ничего не читает. Имя, собранное во
30
+ * время исполнения, так не видно: это остаётся ревью кода.
31
+ */
32
+ export declare function setFileReference(path: string, text: string, sets: string[]): string | undefined;
33
+ /** Подсказка writer или reviewer кода о файлах эталонных наборов; пусто — их нет. */
34
+ export declare function referenceCodeNote(files: string[], review: boolean): string;
14
35
  /** Основание случаев корпуса: закреплённые файлы, gate и смысл обязательств. */
15
36
  export declare function suiteBasis(contract: ContractDocument): string;
16
37
  /**
@@ -38,6 +59,13 @@ export declare function declarationProblems(declaration: CheckDeclaration, conte
38
59
  digest: string;
39
60
  } | undefined;
40
61
  }): string[];
62
+ /**
63
+ * Подсказка reviewer контракта об эталонных обязательствах: что наборы
64
+ * доказывают, чего — нет (`gaps`), чем сверен эталон. Пустая строка — таких
65
+ * обязательств нет. Reviewer видит пределы способа рядом с самим договором,
66
+ * а не выводит их из длинного описания.
67
+ */
68
+ export declare function referenceNote(declaration: CheckDeclaration | undefined): string;
41
69
  /**
42
70
  * Свойства, объявленные поручением, существуют в его договорах и исполняются
43
71
  * его генеративными gates. Поручение без `properties` — прежний уровень.