phasegate 0.254.0 → 0.283.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.ja.md +6 -3
  3. package/README.md +6 -3
  4. package/docs/contracts/attestation-v2.schema.json +110 -0
  5. package/docs/guide/cli-reference.md +12 -1
  6. package/docs/guide/configuration.md +56 -0
  7. package/docs/guide/installation.md +1 -1
  8. package/docs/templates/ci/aidlc-gate.yml +60 -2
  9. package/package.json +2 -2
  10. package/scripts/harness/agent-integration/application/dto/open-world-obligations-context-dto.ts +30 -0
  11. package/scripts/harness/agent-integration/application/ports/world-obligations-query-port.ts +30 -0
  12. package/scripts/harness/agent-integration/application/usecases/get-open-world-obligations-context-usecase.ts +66 -0
  13. package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +30 -27
  14. package/scripts/harness/agent-integration/infrastructure/adapters/world-model-open-obligations-query-adapter.ts +58 -0
  15. package/scripts/harness/agent-integration/presentation/post-tool-use-hook.ts +29 -16
  16. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +37 -1
  17. package/scripts/harness/agent-integration/presentation/stop-hook.ts +35 -26
  18. package/scripts/harness/agent-integration/presentation/world-obligations-session-context.ts +60 -0
  19. package/scripts/harness/attestation/application/dto/attestation-document.ts +17 -4
  20. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +55 -4
  21. package/scripts/harness/attestation/application/ports/world-snapshot-root-provider.ts +10 -0
  22. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +33 -7
  23. package/scripts/harness/attestation/composition-root.ts +5 -0
  24. package/scripts/harness/attestation/domain/entities/attestation-record.ts +37 -2
  25. package/scripts/harness/attestation/index.ts +7 -1
  26. package/scripts/harness/attestation/presentation/handlers/attest-handler.ts +5 -2
  27. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +40 -9
  28. package/scripts/harness/config-foundation/application/mappers/world-model-config-mapper.ts +15 -0
  29. package/scripts/harness/config-foundation/domain/harness-config.ts +70 -87
  30. package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +80 -88
  31. package/scripts/harness/config-foundation/domain/value-objects/project-config.ts +34 -18
  32. package/scripts/harness/config-foundation/domain/value-objects/world-config.ts +198 -0
  33. package/scripts/harness/config-foundation/index.ts +14 -15
  34. package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +24 -0
  35. package/scripts/harness/config-foundation/infrastructure/presets/standard.json +24 -0
  36. package/scripts/harness/config-foundation/infrastructure/presets/strict.json +24 -0
  37. package/scripts/harness/config-foundation/infrastructure/repositories/file-system-config-repository.ts +27 -18
  38. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +84 -8
  39. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +84 -8
  40. package/scripts/harness/harness-api/domain/ports/config-query-port.ts +11 -1
  41. package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +125 -86
  42. package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +31 -21
  43. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +18 -4
  44. package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +23 -8
  45. package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +4 -4
  46. package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +79 -34
  47. package/scripts/harness/installation/application/usecases/run-install.ts +199 -51
  48. package/scripts/harness/installation/application/usecases/run-reconcile.ts +277 -69
  49. package/scripts/harness/installation/domain/deployment-manifest.ts +43 -0
  50. package/scripts/harness/integrations/pre-commit.ts +169 -27
  51. package/scripts/harness/main.ts +200 -136
  52. package/scripts/harness/traceability-model/application/dto/changed-design-fragment-dto.ts +31 -0
  53. package/scripts/harness/traceability-model/application/facades/design-change-read-facade.ts +14 -0
  54. package/scripts/harness/traceability-model/application/ports/staged-design-change-source-port.ts +9 -0
  55. package/scripts/harness/traceability-model/composition-root.ts +5 -0
  56. package/scripts/harness/traceability-model/index.ts +9 -0
  57. package/scripts/harness/traceability-model/infrastructure/adapters/git-staged-design-change-adapter.ts +155 -0
  58. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +31 -0
  59. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +57 -6
  60. package/scripts/harness/validator-system/composition-root.ts +25 -7
  61. package/scripts/harness/validator-system/domain/ports/ac-coverage-policy-port.ts +10 -1
  62. package/scripts/harness/validator-system/domain/ports/world-constraint-admission-policy-port.ts +28 -0
  63. package/scripts/harness/validator-system/domain/ports/world-constraint-rederivation-policy-port.ts +11 -0
  64. package/scripts/harness/validator-system/domain/services/design-change-declaration-policy.ts +75 -0
  65. package/scripts/harness/validator-system/domain/services/world-constraint-admission-service.ts +78 -0
  66. package/scripts/harness/validator-system/domain/services/world-constraint-rederivation-service.ts +76 -0
  67. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +4 -0
  68. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +126 -18
  69. package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-coverage-policy-adapter.ts +49 -20
  70. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-admission-adapter.ts +56 -0
  71. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-rederivation-adapter.ts +56 -0
  72. package/scripts/harness/world-model/application/dto/pinned-design-endpoint-dto.ts +19 -0
  73. package/scripts/harness/world-model/application/dto/world-resolved-config-input.ts +92 -27
  74. package/scripts/harness/world-model/application/dto/world-snapshot-root-dto.ts +8 -0
  75. package/scripts/harness/world-model/application/facades/pinned-design-endpoint-facade.ts +63 -0
  76. package/scripts/harness/world-model/application/facades/world-snapshot-root-facade.ts +19 -0
  77. package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts +3 -1
  78. package/scripts/harness/world-model/composition-root.ts +62 -33
  79. package/scripts/harness/world-model/index.ts +11 -0
  80. package/scripts/harness/world-model/infrastructure/adapters/attestation-fact-extractor.ts +48 -13
  81. package/scripts/harness/world-model/infrastructure/adapters/file-system-world-control-repository-adapters.ts +6 -4
  82. package/scripts/harness/world-model/presentation/cli/world-derive-command-handler.ts +6 -2
@@ -0,0 +1,31 @@
1
+ // @unit traceability-model
2
+ // @layer application
3
+ // @work-item-id WI-305
4
+
5
+ export type DesignFragmentCorpusRole = "product" | "inception";
6
+ export type DesignFragmentChangeKind = "added" | "modified" | "deleted";
7
+
8
+ export interface ChangedDesignFragmentDto {
9
+ readonly corpusRole: DesignFragmentCorpusRole;
10
+ readonly declaredKey: string;
11
+ readonly path: string;
12
+ readonly changeKind: DesignFragmentChangeKind;
13
+ readonly workItemIds: readonly string[];
14
+ readonly reflectionTargets: readonly string[];
15
+ }
16
+
17
+ export interface DesignChangeReadDiagnosticDto {
18
+ readonly code: string;
19
+ readonly path: string | null;
20
+ }
21
+
22
+ export type DesignChangeReadResultDto =
23
+ | {
24
+ readonly state: "available";
25
+ readonly fragments: readonly ChangedDesignFragmentDto[];
26
+ readonly diagnostics: readonly DesignChangeReadDiagnosticDto[];
27
+ }
28
+ | {
29
+ readonly state: "unavailable";
30
+ readonly diagnostics: readonly DesignChangeReadDiagnosticDto[];
31
+ };
@@ -0,0 +1,14 @@
1
+ // @unit traceability-model
2
+ // @layer application
3
+ // @work-item-id WI-305
4
+
5
+ import type { DesignChangeReadResultDto } from "../dto/changed-design-fragment-dto.js";
6
+ import type { StagedDesignChangeSourcePort } from "../ports/staged-design-change-source-port.js";
7
+
8
+ export class DesignChangeReadFacade {
9
+ constructor(private readonly source: StagedDesignChangeSourcePort) {}
10
+
11
+ observe(stagedFiles: readonly string[]): Promise<DesignChangeReadResultDto> {
12
+ return this.source.observe(stagedFiles);
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ // @unit traceability-model
2
+ // @layer application
3
+ // @work-item-id WI-305
4
+
5
+ import type { DesignChangeReadResultDto } from "../dto/changed-design-fragment-dto.js";
6
+
7
+ export interface StagedDesignChangeSourcePort {
8
+ observe(stagedFiles: readonly string[]): Promise<DesignChangeReadResultDto>;
9
+ }
@@ -2,11 +2,13 @@
2
2
  * @layer application
3
3
  * @unit traceability-model
4
4
  * @work-item-id WI-093
5
+ * @work-item-id WI-305
5
6
  *
6
7
  * traceability-model ユニットの Composition Root。
7
8
  * 全コンポーネントを生成・配線し、外部に公開するハンドラー群を返す。
8
9
  */
9
10
 
11
+ import { DesignChangeReadFacade } from "./application/facades/design-change-read-facade.js";
10
12
  import { TraceabilityWorldReadFacade } from "./application/facades/traceability-world-read-facade.js";
11
13
  import { ApplyWorkItemMigrationUseCase } from "./application/usecases/apply-work-item-migration-usecase.js";
12
14
  import { ApplyWorkItemStatusUseCase } from "./application/usecases/apply-work-item-status-usecase.js";
@@ -21,6 +23,7 @@ import { TraceabilityChainBuilder } from "./domain/services/traceability-chain-b
21
23
  import { WorkItemStatusDerivationService } from "./domain/services/work-item-status-derivation-service.js";
22
24
  import { ProjectRelativePath } from "./domain/value-objects/project-relative-path.js";
23
25
  import { FileSystemTraceabilityWorldReadAdapter } from "./infrastructure/adapters/file-system-traceability-world-read-adapter.js";
26
+ import { GitStagedDesignChangeAdapter } from "./infrastructure/adapters/git-staged-design-change-adapter.js";
24
27
  import { FileSystemInceptionPlanGateway } from "./infrastructure/gateways/file-system-inception-plan-gateway.js";
25
28
  import { FileSystemMetadataReader } from "./infrastructure/gateways/file-system-metadata-reader.js";
26
29
  import { FileSystemWorkItemIdentityGateway } from "./infrastructure/gateways/file-system-work-item-identity-gateway.js";
@@ -101,6 +104,7 @@ export function createTraceabilityModelModule(rootDir: string, options: Traceabi
101
104
  const worldReadFacade = new TraceabilityWorldReadFacade({
102
105
  sourcePort: worldReadSource,
103
106
  });
107
+ const designChangeReadFacade = new DesignChangeReadFacade(new GitStagedDesignChangeAdapter(rootDir));
104
108
 
105
109
  // Usecases
106
110
  const validateImplementationMetadataUseCase = new ValidateImplementationMetadataUseCase({
@@ -157,5 +161,6 @@ export function createTraceabilityModelModule(rootDir: string, options: Traceabi
157
161
  traceabilityChainBuilder,
158
162
  storyIdAliasResolver,
159
163
  worldReadFacade,
164
+ designChangeReadFacade,
160
165
  } as const;
161
166
  }
@@ -1,10 +1,18 @@
1
1
  /**
2
2
  * @layer public-api
3
3
  * @unit traceability-model
4
+ * @work-item-id WI-305
4
5
  *
5
6
  * traceability-model ユニットのバレルエクスポート。
6
7
  */
7
8
 
9
+ export type {
10
+ ChangedDesignFragmentDto,
11
+ DesignChangeReadDiagnosticDto,
12
+ DesignChangeReadResultDto,
13
+ DesignFragmentChangeKind,
14
+ DesignFragmentCorpusRole,
15
+ } from "./application/dto/changed-design-fragment-dto.js";
8
16
  export type {
9
17
  TraceabilityAcceptanceCriterionDto,
10
18
  TraceabilityReadDiagnosticDto,
@@ -16,6 +24,7 @@ export type {
16
24
  TraceabilityWorldReadDto,
17
25
  } from "./application/dto/traceability-world-read-dto.js";
18
26
  export { TRACEABILITY_WORLD_READ_SCHEMA_VERSION } from "./application/dto/traceability-world-read-dto.js";
27
+ export { DesignChangeReadFacade } from "./application/facades/design-change-read-facade.js";
19
28
  // Application — public World read facade and plain DTO contract
20
29
  export { TraceabilityWorldReadFacade } from "./application/facades/traceability-world-read-facade.js";
21
30
  // Composition Root
@@ -0,0 +1,155 @@
1
+ // @unit traceability-model
2
+ // @layer infrastructure
3
+ // @work-item-id WI-305
4
+
5
+ import { execFileSync } from "node:child_process";
6
+ import type {
7
+ ChangedDesignFragmentDto,
8
+ DesignChangeReadDiagnosticDto,
9
+ DesignChangeReadResultDto,
10
+ DesignFragmentCorpusRole,
11
+ } from "../../application/dto/changed-design-fragment-dto.js";
12
+ import type { StagedDesignChangeSourcePort } from "../../application/ports/staged-design-change-source-port.js";
13
+
14
+ const FRAGMENT = /^\s*<!--\s*@world-fragment-id\s+(\S+)\s*-->\s*$/;
15
+ const WORK_ITEM = /^\s*<!--\s*@work-item-id\s+(.+?)\s*-->\s*$/;
16
+ const REFLECTS = /^\s*<!--\s*@world-reflects\s+(\S+)\s*-->\s*$/;
17
+ const HEADING = /^#{1,6}\s+/;
18
+ const FENCE = /^\s*(`{3,}|~{3,})/;
19
+ const compare = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
20
+
21
+ interface ParsedFragment {
22
+ readonly declaredKey: string;
23
+ readonly content: string;
24
+ readonly workItemIds: readonly string[];
25
+ readonly reflectionTargets: readonly string[];
26
+ }
27
+
28
+ interface ParsedFragmentSet {
29
+ readonly fragments: readonly ParsedFragment[];
30
+ readonly validPrelude: boolean;
31
+ }
32
+
33
+ const corpusRoleOf = (filePath: string): DesignFragmentCorpusRole | null => {
34
+ if (filePath.startsWith("docs/product/")) return "product";
35
+ if (filePath.startsWith("docs/inception/")) return "inception";
36
+ return null;
37
+ };
38
+
39
+ const uniqueSorted = (values: readonly string[]): readonly string[] =>
40
+ Object.freeze([...new Set(values)].sort(compare));
41
+
42
+ const parseFragments = (text: string): ParsedFragmentSet => {
43
+ const lines = text.replace(/\r\n?/g, "\n").split("\n");
44
+ const markers: { readonly index: number; readonly key: string }[] = [];
45
+ let activeFence: string | null = null;
46
+ for (let index = 0; index < lines.length; index += 1) {
47
+ const fence = FENCE.exec(lines[index]);
48
+ if (fence) {
49
+ const character = fence[1][0];
50
+ activeFence = activeFence === null ? character : activeFence === character ? null : activeFence;
51
+ continue;
52
+ }
53
+ if (activeFence !== null) continue;
54
+ const marker = FRAGMENT.exec(lines[index]);
55
+ if (marker) markers.push({ index, key: marker[1] });
56
+ }
57
+
58
+ let validPrelude = true;
59
+ const fragments = markers.flatMap((marker, markerIndex): readonly ParsedFragment[] => {
60
+ const end = markers[markerIndex + 1]?.index ?? lines.length;
61
+ let headingIndex = marker.index + 1;
62
+ while (headingIndex < end && (WORK_ITEM.test(lines[headingIndex]) || REFLECTS.test(lines[headingIndex]))) {
63
+ headingIndex += 1;
64
+ }
65
+ if (headingIndex >= end || !HEADING.test(lines[headingIndex])) {
66
+ validPrelude = false;
67
+ return [];
68
+ }
69
+ const metadata = lines.slice(marker.index, headingIndex);
70
+ return [
71
+ Object.freeze({
72
+ declaredKey: marker.key,
73
+ content: lines.slice(marker.index, end).join("\n"),
74
+ workItemIds: uniqueSorted(metadata.flatMap((line) => WORK_ITEM.exec(line)?.[1].match(/\bWI-\d+\b/g) ?? [])),
75
+ reflectionTargets: uniqueSorted(metadata.flatMap((line) => REFLECTS.exec(line)?.[1] ?? [])),
76
+ }),
77
+ ];
78
+ });
79
+ return Object.freeze({ fragments: Object.freeze(fragments), validPrelude });
80
+ };
81
+
82
+ const indexByKey = (fragments: readonly ParsedFragment[]): Map<string, ParsedFragment> | null => {
83
+ const result = new Map<string, ParsedFragment>();
84
+ for (const fragment of fragments) {
85
+ if (result.has(fragment.declaredKey)) return null;
86
+ result.set(fragment.declaredKey, fragment);
87
+ }
88
+ return result;
89
+ };
90
+
91
+ export class GitStagedDesignChangeAdapter implements StagedDesignChangeSourcePort {
92
+ constructor(private readonly rootDir: string) {}
93
+
94
+ async observe(stagedFiles: readonly string[]): Promise<DesignChangeReadResultDto> {
95
+ const fragments: ChangedDesignFragmentDto[] = [];
96
+ const diagnostics: DesignChangeReadDiagnosticDto[] = [];
97
+ for (const filePath of [...new Set(stagedFiles)].sort(compare)) {
98
+ const corpusRole = corpusRoleOf(filePath);
99
+ if (corpusRole === null || !filePath.endsWith(".md")) continue;
100
+ const beforeText = this.readBlob(`HEAD:${filePath}`);
101
+ const afterText = this.readBlob(`:${filePath}`);
102
+ if (beforeText === null && afterText === null) {
103
+ diagnostics.push(Object.freeze({ code: "staged-design-content-unavailable", path: filePath }));
104
+ continue;
105
+ }
106
+ const beforeParsed = parseFragments(beforeText ?? "");
107
+ const afterParsed = parseFragments(afterText ?? "");
108
+ if (!beforeParsed.validPrelude || !afterParsed.validPrelude) {
109
+ diagnostics.push(Object.freeze({ code: "invalid-staged-fragment-prelude", path: filePath }));
110
+ continue;
111
+ }
112
+ const before = indexByKey(beforeParsed.fragments);
113
+ const after = indexByKey(afterParsed.fragments);
114
+ if (before === null || after === null) {
115
+ diagnostics.push(Object.freeze({ code: "duplicate-staged-fragment-key", path: filePath }));
116
+ continue;
117
+ }
118
+ const keys = uniqueSorted([...before.keys(), ...after.keys()]);
119
+ for (const key of keys) {
120
+ const oldFragment = before.get(key);
121
+ const newFragment = after.get(key);
122
+ if (oldFragment?.content === newFragment?.content) continue;
123
+ const observed = newFragment ?? oldFragment;
124
+ if (!observed) continue;
125
+ fragments.push(
126
+ Object.freeze({
127
+ corpusRole,
128
+ declaredKey: key,
129
+ path: filePath,
130
+ changeKind: oldFragment === undefined ? "added" : newFragment === undefined ? "deleted" : "modified",
131
+ workItemIds: observed.workItemIds,
132
+ reflectionTargets: observed.reflectionTargets,
133
+ }),
134
+ );
135
+ }
136
+ }
137
+ if (diagnostics.length > 0) {
138
+ return Object.freeze({ state: "unavailable", diagnostics: Object.freeze(diagnostics) });
139
+ }
140
+ return Object.freeze({ state: "available", fragments: Object.freeze(fragments), diagnostics: Object.freeze([]) });
141
+ }
142
+
143
+ private readBlob(spec: string): string | null {
144
+ try {
145
+ return execFileSync("git", ["show", spec], {
146
+ cwd: this.rootDir,
147
+ encoding: "utf8",
148
+ stdio: ["ignore", "pipe", "ignore"],
149
+ maxBuffer: 8 * 1024 * 1024,
150
+ });
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+ }
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * RunL2ValidatorsUseCase — H08-01: L2バリデータ実行
6
6
  * @work-item-id WI-110 / WI-111 / WI-140 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138
7
+ * @work-item-id WI-301
7
8
  */
8
9
  import { readFile } from "node:fs/promises";
9
10
  import type { CliCommandRegistryPort } from "../../domain/ports/cli-command-registry-port.js";
@@ -15,6 +16,7 @@ import type { PhaseGatePolicyPort } from "../../domain/ports/phase-gate-policy-p
15
16
  import type { TestQualityAnalyzerPort } from "../../domain/ports/test-quality-analyzer-port.js";
16
17
  import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
17
18
  import type { WorkItemStatusPolicyPort } from "../../domain/ports/work-item-status-policy-port.js";
19
+ import type { WorldConstraintAdmissionPolicyPort } from "../../domain/ports/world-constraint-admission-policy-port.js";
18
20
  import { CliE2eTestExistenceService } from "../../domain/services/cli-e2e-test-existence-service.js";
19
21
  import { ContractTraceabilityCoverageService } from "../../domain/services/contract-traceability-coverage-service.js";
20
22
  import { CoverageAttestationGatingService } from "../../domain/services/coverage-attestation-gating-service.js";
@@ -23,6 +25,7 @@ import {
23
25
  type ValidatorExecutionService,
24
26
  } from "../../domain/services/validator-execution-service.js";
25
27
  import type { ValidatorRegistry } from "../../domain/services/validator-registry.js";
28
+ import { WorldConstraintAdmissionService } from "../../domain/services/world-constraint-admission-service.js";
26
29
  import { type HarnessErrorLike, ValidationResult } from "../../domain/value-objects/validation-result.js";
27
30
  import { ValidatorId } from "../../domain/value-objects/validator-id.js";
28
31
  import type { RunL2ValidatorsInput } from "../dto/run-l2-validators-input.js";
@@ -42,6 +45,7 @@ export interface RunL2ValidatorsUseCaseDeps {
42
45
  workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
43
46
  contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
44
47
  coverageAttestationGatingPolicyPort?: CoverageAttestationGatingPolicyPort;
48
+ worldConstraintAdmissionPolicyPort?: WorldConstraintAdmissionPolicyPort;
45
49
  }
46
50
 
47
51
  export class RunL2ValidatorsUseCase {
@@ -57,9 +61,11 @@ export class RunL2ValidatorsUseCase {
57
61
  private readonly workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
58
62
  private readonly contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
59
63
  private readonly coverageAttestationGatingPolicyPort?: CoverageAttestationGatingPolicyPort;
64
+ private readonly worldConstraintAdmissionPolicyPort?: WorldConstraintAdmissionPolicyPort;
60
65
  private readonly cliE2eTestExistenceService = new CliE2eTestExistenceService();
61
66
  private readonly contractTraceabilityCoverageService = new ContractTraceabilityCoverageService();
62
67
  private readonly coverageAttestationGatingService = new CoverageAttestationGatingService();
68
+ private readonly worldConstraintAdmissionService = new WorldConstraintAdmissionService();
63
69
 
64
70
  constructor(deps: RunL2ValidatorsUseCaseDeps) {
65
71
  this.registry = deps.validatorRegistry;
@@ -74,6 +80,7 @@ export class RunL2ValidatorsUseCase {
74
80
  this.workItemStatusPolicyPort = deps.workItemStatusPolicyPort;
75
81
  this.contractTraceabilityPolicyPort = deps.contractTraceabilityPolicyPort;
76
82
  this.coverageAttestationGatingPolicyPort = deps.coverageAttestationGatingPolicyPort;
83
+ this.worldConstraintAdmissionPolicyPort = deps.worldConstraintAdmissionPolicyPort;
77
84
  }
78
85
 
79
86
  async execute(input: RunL2ValidatorsInput): Promise<readonly ValidationResultContract[]> {
@@ -241,6 +248,30 @@ export class RunL2ValidatorsUseCase {
241
248
  }
242
249
  }
243
250
 
251
+ if (this.worldConstraintAdmissionPolicyPort) {
252
+ const l2017Result = overrideMap.get("L2-017");
253
+ if (l2017Result && !l2017Result.skipped) {
254
+ const observation = await this.worldConstraintAdmissionPolicyPort.collect();
255
+ const findings = this.worldConstraintAdmissionService.evaluate(observation);
256
+ if (findings.length > 0) {
257
+ const errors: HarnessErrorLike[] = findings.map((finding) => ({
258
+ code: { value: "L2-017", toString: () => "L2-017" },
259
+ severity: { value: finding.severity, toString: () => finding.severity },
260
+ message: finding.message,
261
+ suggestion: finding.suggestion,
262
+ ruleId: finding.ruleId,
263
+ violationFingerprint: finding.violationFingerprint,
264
+ constraintId: finding.constraintId,
265
+ classification: finding.classification,
266
+ sourcePath: finding.sourcePath,
267
+ }));
268
+ overrideMap.set("L2-017", ValidationResult.fail(ValidatorId.create("L2-017"), errors, 0));
269
+ } else {
270
+ overrideMap.set("L2-017", ValidationResult.pass(ValidatorId.create("L2-017"), 0));
271
+ }
272
+ }
273
+ }
274
+
244
275
  const finalResults = definitions.map(
245
276
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
246
277
  );
@@ -2,6 +2,9 @@
2
2
  * @layer application
3
3
  * @unit validator-system
4
4
  * @work-item-id WI-212
5
+ * @work-item-id WI-302
6
+ * @work-item-id WI-317
7
+ * @work-item-id WI-324
5
8
  *
6
9
  * RunL3ValidatorsUseCase — H08-02: L3バリデータ実行
7
10
  */
@@ -13,6 +16,7 @@ import type { InjectionScanPolicyPort } from "../../domain/ports/injection-scan-
13
16
  import type { PerformanceScannerPort } from "../../domain/ports/performance-scanner-port.js";
14
17
  import type { SecurityPatternScannerPort } from "../../domain/ports/security-pattern-scanner-port.js";
15
18
  import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
19
+ import type { WorldConstraintRederivationPolicyPort } from "../../domain/ports/world-constraint-rederivation-policy-port.js";
16
20
  import { CoverageAttestationVerificationService } from "../../domain/services/coverage-attestation-verification-service.js";
17
21
  import { InjectionPatternScanService } from "../../domain/services/injection-pattern-scan-service.js";
18
22
  import {
@@ -21,6 +25,7 @@ import {
21
25
  } from "../../domain/services/validator-execution-service.js";
22
26
  import { ValidatorLanguageCapabilityService } from "../../domain/services/validator-language-capability-service.js";
23
27
  import type { ValidatorRegistry } from "../../domain/services/validator-registry.js";
28
+ import { WorldConstraintRederivationService } from "../../domain/services/world-constraint-rederivation-service.js";
24
29
  import type { HarnessErrorLike } from "../../domain/value-objects/validation-result.js";
25
30
  import { ValidationResult } from "../../domain/value-objects/validation-result.js";
26
31
  import { ValidatorId } from "../../domain/value-objects/validator-id.js";
@@ -54,6 +59,8 @@ export interface RunL3ValidatorsUseCaseDeps {
54
59
  injectionScanPolicyPort?: InjectionScanPolicyPort;
55
60
  /** WI-268 / ADR-030 §Decision.1・§Decision.3.② 第2段: L3-007 (coverage-attestation-verification, fail-closed) 用ポート。 */
56
61
  coverageAttestationVerificationPolicyPort?: CoverageAttestationVerificationPolicyPort;
62
+ /** WI-302: L3-008 authoritative World clean-corpus re-derivation port. */
63
+ worldConstraintRederivationPolicyPort?: WorldConstraintRederivationPolicyPort;
57
64
  /** L3-005 のスコープ対象 story-id(config layers.L3.acBoundStories 由来。既定 [])。 */
58
65
  acBoundStories?: readonly string[];
59
66
  }
@@ -70,10 +77,12 @@ export class RunL3ValidatorsUseCase {
70
77
  private readonly performanceScannerPort?: PerformanceScannerPort;
71
78
  private readonly injectionScanPolicyPort?: InjectionScanPolicyPort;
72
79
  private readonly coverageAttestationVerificationPolicyPort?: CoverageAttestationVerificationPolicyPort;
80
+ private readonly worldConstraintRederivationPolicyPort?: WorldConstraintRederivationPolicyPort;
73
81
  private readonly acBoundStories: readonly string[];
74
82
  private readonly languageCapabilityService = new ValidatorLanguageCapabilityService();
75
83
  private readonly injectionScanService = new InjectionPatternScanService();
76
84
  private readonly coverageAttestationVerificationService = new CoverageAttestationVerificationService();
85
+ private readonly worldConstraintRederivationService = new WorldConstraintRederivationService();
77
86
 
78
87
  constructor(deps: RunL3ValidatorsUseCaseDeps) {
79
88
  this.registry = deps.validatorRegistry;
@@ -87,6 +96,7 @@ export class RunL3ValidatorsUseCase {
87
96
  this.performanceScannerPort = deps.performanceScannerPort;
88
97
  this.injectionScanPolicyPort = deps.injectionScanPolicyPort;
89
98
  this.coverageAttestationVerificationPolicyPort = deps.coverageAttestationVerificationPolicyPort;
99
+ this.worldConstraintRederivationPolicyPort = deps.worldConstraintRederivationPolicyPort;
90
100
  this.acBoundStories = deps.acBoundStories ?? [];
91
101
  }
92
102
 
@@ -125,8 +135,10 @@ export class RunL3ValidatorsUseCase {
125
135
  );
126
136
 
127
137
  // L3-003: カバレッジ判定(カバレッジゲートはオプトイン)
128
- // - coverageThreshold 未設定 → SKIP(透過的に判定をスキップ。getCoverage() は呼ばない)
129
- // - coverageThreshold 設定あり → getCoverage() try/catch で包み FAIL-CLOSED で判定する
138
+ // - coverageThreshold 未設定 or 0 → SKIP(透過的に判定をスキップ。getCoverage() は呼ばない)
139
+ // 0 は正規の opt-out(ドメイン VO L3Config.hasCoverageGate() の「threshold > 0 でのみ有効」と整合。
140
+ // minimal preset の coverageThreshold: 0 もこの opt-out 意図。WI-317 / github#37)
141
+ // - coverageThreshold > 0 → getCoverage() を try/catch で包み FAIL-CLOSED で判定する
130
142
  // - 閾値未満 → FAIL / 閾値以上 → PASS
131
143
  // - レポート不在などで getCoverage() が失敗 → FAIL(合格扱いにしない)
132
144
  // このブロックは例外を送出せず、L3-003 の per-validator 結果のみを差し替える。
@@ -137,12 +149,12 @@ export class RunL3ValidatorsUseCase {
137
149
  const l3003Id = ValidatorId.create("L3-003");
138
150
  const threshold = layerConfig.getThreshold("coverageThreshold");
139
151
 
140
- if (threshold === null) {
152
+ if (threshold === null || threshold === 0) {
141
153
  overrideMap.set(
142
154
  "L3-003",
143
155
  ValidationResult.skipWithReason(
144
156
  l3003Id,
145
- "coverageThreshold が未設定のためカバレッジ判定をスキップ(カバレッジゲートはオプトイン)",
157
+ "coverageThreshold が未設定/0 のためカバレッジ判定をスキップ(カバレッジゲートはオプトイン。0 で opt-out)",
146
158
  ),
147
159
  );
148
160
  } else {
@@ -178,7 +190,8 @@ export class RunL3ValidatorsUseCase {
178
190
  code: "L3-003",
179
191
  severity: "error",
180
192
  message: `coverageThreshold=${threshold}% が設定されていますがカバレッジレポートが見つかりません(テストをカバレッジ付きで実行してください)`,
181
- suggestion: "vitest --coverage 等でカバレッジレポートを生成してから再実行してください",
193
+ suggestion:
194
+ '次のいずれかで解消してください: (a) テストをカバレッジ付きで実行してレポートを生成する(例: vitest --coverage)、(b) カバレッジゲートを opt-out するなら config の layers.L3.coverageThreshold を 0 に設定する、(c) 非 JS/TS プロジェクトなら project.languages を宣言する(例: ["python"]。L3-003 自体が unsupported-language SKIP になる)',
182
195
  },
183
196
  ],
184
197
  0,
@@ -216,7 +229,19 @@ export class RunL3ValidatorsUseCase {
216
229
  const policyResult = await this.acCoveragePolicyPort.checkCoverage({
217
230
  matrixFilePath: input.requirementMatrixPath,
218
231
  });
219
- if (!policyResult.passed) {
232
+ // WI-324: フレッシュプロジェクト(story 未作成・matrix 未生成)は policy adapter が
233
+ // skipped=true を返すので、L3-003 の opt-out と同じ表現で skipWithReason に変換する。
234
+ // story が存在するのに matrix が不在の場合は従来どおり fail-closed(下の分岐)。
235
+ if (policyResult.skipped) {
236
+ overrideMap.set(
237
+ "L3-004",
238
+ ValidationResult.skipWithReason(
239
+ ValidatorId.create("L3-004"),
240
+ policyResult.skipReason ??
241
+ "story 未作成のため L3-004 をスキップ(story 作成後に requirement-test-matrix を生成すると有効化されます)",
242
+ ),
243
+ );
244
+ } else if (!policyResult.passed) {
220
245
  overrideMap.set("L3-004", ValidationResult.fail(ValidatorId.create("L3-004"), [...policyResult.errors], 0));
221
246
  }
222
247
  }
@@ -305,6 +330,32 @@ export class RunL3ValidatorsUseCase {
305
330
  }
306
331
  }
307
332
 
333
+ // WI-302 / ADR-030 trust root: L3-008 independently re-derives World obligations from the
334
+ // current corpus and versioned control declarations. The adapter has no persisted-report input.
335
+ if (this.worldConstraintRederivationPolicyPort) {
336
+ const l3008Result = overrideMap.get("L3-008");
337
+ if (l3008Result && !l3008Result.skipped) {
338
+ const observation = await this.worldConstraintRederivationPolicyPort.collect();
339
+ const findings = this.worldConstraintRederivationService.evaluate(observation);
340
+ if (findings.length > 0) {
341
+ const errors: HarnessErrorLike[] = findings.map((finding) => ({
342
+ code: { value: "L3-008", toString: () => "L3-008" },
343
+ severity: { value: finding.severity, toString: () => finding.severity },
344
+ message: finding.message,
345
+ suggestion: finding.suggestion,
346
+ sourcePath: finding.sourcePath,
347
+ ruleId: finding.ruleId,
348
+ violationFingerprint: finding.violationFingerprint,
349
+ constraintId: finding.constraintId,
350
+ classification: finding.classification,
351
+ }));
352
+ overrideMap.set("L3-008", ValidationResult.fail(ValidatorId.create("L3-008"), errors, 0));
353
+ } else {
354
+ overrideMap.set("L3-008", ValidationResult.pass(ValidatorId.create("L3-008"), 0));
355
+ }
356
+ }
357
+ }
358
+
308
359
  const finalResults = definitions.map(
309
360
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
310
361
  );
@@ -5,6 +5,10 @@
5
5
  * DI 組み立て — validator-system の全依存関係を構築する
6
6
  * @work-item-id WI-110 / WI-111 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138 / WI-156
7
7
  * @work-item-id WI-217
8
+ * @work-item-id WI-301
9
+ * @work-item-id WI-302
10
+ * @work-item-id WI-305
11
+ * @work-item-id WI-322
8
12
  */
9
13
 
10
14
  import { join } from "node:path";
@@ -17,6 +21,7 @@ import { RunL2ValidatorsUseCase } from "./application/use-cases/run-l2-validator
17
21
  import { RunL3ValidatorsUseCase } from "./application/use-cases/run-l3-validators-usecase.js";
18
22
  import { RunL4ValidatorsUseCase } from "./application/use-cases/run-l4-validators-usecase.js";
19
23
  import { RunQuickModeUseCase } from "./application/use-cases/run-quick-mode-usecase.js";
24
+ import { DesignChangeDeclarationPolicy } from "./domain/services/design-change-declaration-policy.js";
20
25
  import {
21
26
  ArchitectureSemanticAnalysisService,
22
27
  type ArchitectureSemanticPolicy,
@@ -55,6 +60,8 @@ import { PhaseDependencyPhaseGatePolicyAdapter } from "./infrastructure/adapters
55
60
  import { SourceFileTextScannerAdapter } from "./infrastructure/adapters/source-file-text-scanner-adapter.js";
56
61
  import { TraceabilityMetadataPolicyAdapter } from "./infrastructure/adapters/traceability-metadata-policy-adapter.js";
57
62
  import { TraceabilityWorkItemStatusPolicyAdapter } from "./infrastructure/adapters/traceability-work-item-status-policy-adapter.js";
63
+ import { WorldModelConstraintAdmissionAdapter } from "./infrastructure/adapters/world-model-constraint-admission-adapter.js";
64
+ import { WorldModelConstraintRederivationAdapter } from "./infrastructure/adapters/world-model-constraint-rederivation-adapter.js";
58
65
  import { ReportValidationResultsHandler } from "./presentation/handlers/report-validation-results-handler.js";
59
66
  import { RunQuickModeHandler } from "./presentation/handlers/run-quick-mode-handler.js";
60
67
  import { RunValidatorsHandler } from "./presentation/handlers/run-validators-handler.js";
@@ -67,7 +74,9 @@ const DEFAULT_CONFIG = {
67
74
  L3: {
68
75
  enabled: true,
69
76
  validators: ["L3-001", "L3-002", "L3-003", "L3-004", "L3-006", "L3-007"],
70
- coverageThreshold: 90,
77
+ // WI-322 (github#37 残課題): カバレッジゲートはオプトイン(WI-317)。config なし環境の
78
+ // fallback で 90% を強制しない。0 = 正規の opt-out(L3-003 は透過 SKIP になる)。
79
+ coverageThreshold: 0,
71
80
  bundleSizeLimit: 512000,
72
81
  requirementMatrixPath: ".harness/requirement-test-matrix.json",
73
82
  },
@@ -99,12 +108,6 @@ const DEFAULT_CONFIG = {
99
108
 
100
109
  /** バリデータ定義カタログ */
101
110
  export function buildDefaultRegistry(): ValidatorRegistry {
102
- const defaultRule = ValidationRule.create({
103
- ruleName: "default-rule",
104
- errorTemplate: { code: "L2-001", severity: "error", messageTemplate: "{{message}}" },
105
- fixExample: null,
106
- });
107
-
108
111
  const createDef = (
109
112
  id: string,
110
113
  layer: "L2" | "L3" | "L4",
@@ -134,6 +137,7 @@ export function buildDefaultRegistry(): ValidatorRegistry {
134
137
  createDef("L2-015", "L2", "always", "ContractTraceabilityPolicyPort"),
135
138
  // WI-258 / ADR-030 §Decision.3.②: L2-016 (coverage-attestation-gating, fail-closed, default-ON)。
136
139
  createDef("L2-016", "L2", "always", "CoverageAttestationGatingPolicyPort"),
140
+ createDef("L2-017", "L2", "always", "WorldConstraintAdmissionPolicyPort"),
137
141
  createDef("L3-001", "L3", "always"),
138
142
  createDef("L3-002", "L3", "strictOnly"),
139
143
  createDef("L3-003", "L3", "always"),
@@ -146,6 +150,7 @@ export function buildDefaultRegistry(): ValidatorRegistry {
146
150
  // WI-268 / ADR-030 §Decision.1・§Decision.3.② 第2段: L3-007 (coverage-attestation-verification)
147
151
  // は fail-closed / default-ON。L2-016 の authoritative 相棒。
148
152
  createDef("L3-007", "L3", "always", "CoverageAttestationVerificationPolicyPort"),
153
+ createDef("L3-008", "L3", "always", "WorldConstraintRederivationPolicyPort"),
149
154
  createDef("L4-001", "L4", "always"),
150
155
  createDef("L4-002", "L4", "always"),
151
156
  createDef("L4-003", "L4", "strictOnly"),
@@ -170,6 +175,7 @@ export interface ValidatorSystemModule {
170
175
  aggregateValidationResultsUseCase: AggregateValidationResultsUseCase;
171
176
  runFullValidationUseCase: RunFullValidationUseCase;
172
177
  driftDetectionService: DriftDetectionService;
178
+ designChangeDeclarationPolicy: DesignChangeDeclarationPolicy;
173
179
  handlers: {
174
180
  runValidators: RunValidatorsHandler;
175
181
  runQuickMode: RunQuickModeHandler;
@@ -184,6 +190,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
184
190
  const defaultFailOnWarning = configData.validate?.failOnWarning ?? false;
185
191
  const registry = buildDefaultRegistry();
186
192
  const executionService = new ValidatorExecutionService({ configPort });
193
+ const designChangeDeclarationPolicy = new DesignChangeDeclarationPolicy();
187
194
  const contractMapper = new ValidationResultContractMapper();
188
195
  const phaseGatePolicyPort = new PhaseDependencyPhaseGatePolicyAdapter();
189
196
  const metadataPolicyPort = new TraceabilityMetadataPolicyAdapter();
@@ -201,6 +208,14 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
201
208
  // WI-258 / ADR-030 §Decision.3.②: L2-016 (coverage-attestation-gating) 用アダプタ。
202
209
  // docs/product/construction/*​/coverage_report.md を cwd 起点で走査する(targetPaths 非依存)。
203
210
  const coverageAttestationGatingPolicyPort = new FileSystemCoverageAttestationGatingAdapter(process.cwd());
211
+ const worldConstraintAdmissionPolicyPort = new WorldModelConstraintAdmissionAdapter({
212
+ rootDir: process.cwd(),
213
+ resolvedConfig: (configData as { world?: import("../world-model/index.js").WorldResolvedConfigInput }).world,
214
+ });
215
+ const worldConstraintRederivationPolicyPort = new WorldModelConstraintRederivationAdapter({
216
+ rootDir: process.cwd(),
217
+ resolvedConfig: (configData as { world?: import("../world-model/index.js").WorldResolvedConfigInput }).world,
218
+ });
204
219
  // WI-259 / ADR-030 §Decision.3.④: L3-006 (injection-scan, advisory) 用アダプタ。
205
220
  // 指示搭載ファイル群(skills/**​/SKILL.md / CLAUDE.md / AGENTS.md / agent-context / .claude/settings.json)を
206
221
  // cwd 起点で走査する(targetPaths 非依存)。
@@ -249,6 +264,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
249
264
  workItemStatusPolicyPort,
250
265
  contractTraceabilityPolicyPort,
251
266
  coverageAttestationGatingPolicyPort,
267
+ worldConstraintAdmissionPolicyPort,
252
268
  });
253
269
 
254
270
  // WI-227 / H16-03: L3-005 のスコープ対象 story-id を config から取得(既定 [])。
@@ -267,6 +283,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
267
283
  performanceScannerPort,
268
284
  injectionScanPolicyPort,
269
285
  coverageAttestationVerificationPolicyPort,
286
+ worldConstraintRederivationPolicyPort,
270
287
  acBoundStories,
271
288
  });
272
289
 
@@ -367,6 +384,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
367
384
  aggregateValidationResultsUseCase,
368
385
  runFullValidationUseCase,
369
386
  driftDetectionService,
387
+ designChangeDeclarationPolicy,
370
388
  handlers,
371
389
  };
372
390
  }
@@ -4,11 +4,20 @@
4
4
  *
5
5
  * AcCoveragePolicyPort — nyquist-validation AcCoverageGatePolicy(L3-004)
6
6
  */
7
- import type { HarnessErrorLike } from '../value-objects/validation-result.js';
7
+ import type { HarnessErrorLike } from "../value-objects/validation-result.js";
8
8
 
9
9
  export interface AcCoveragePolicyPort {
10
10
  checkCoverage(context: { matrixFilePath?: string }): Promise<{
11
11
  passed: boolean;
12
12
  errors: readonly HarnessErrorLike[];
13
+ /**
14
+ * WI-324: フレッシュプロジェクト(story 未作成・matrix 未生成)では L3-004 を
15
+ * fail-closed ではなく SKIP として扱う。true の場合、上位(RunL3ValidatorsUseCase)は
16
+ * skipReason 付きの skipWithReason 結果へ変換する。省略時(既存実装・既存モック)は
17
+ * 従来どおり passed/errors のみで判定される(後方互換 optional)。
18
+ */
19
+ skipped?: boolean;
20
+ /** skipped=true のときの人間可読なスキップ理由。 */
21
+ skipReason?: string;
13
22
  }>;
14
23
  }