phasegate 0.229.0 → 0.264.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 (154) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/README.ja.md +31 -1
  3. package/README.md +30 -0
  4. package/docs/ADR/031-world-model-ownership-and-corpus-lifecycle.md +155 -0
  5. package/docs/ADR/032-world-node-identity.md +198 -0
  6. package/docs/ADR/033-world-snapshot-canonicalization.md +246 -0
  7. package/docs/ADR/034-world-constraint-semantics.md +218 -0
  8. package/docs/ADR/035-world-adoption-baseline-and-waiver.md +341 -0
  9. package/docs/ADR/036-world-model-and-doc-freshness.md +169 -0
  10. package/docs/ADR/037-world-cli-and-output-contract.md +398 -0
  11. package/docs/contracts/attestation-v2.schema.json +110 -0
  12. package/docs/contracts/requirement-test-matrix.schema.json +15 -0
  13. package/docs/contracts/world-baseline.schema.json +35 -0
  14. package/docs/contracts/world-constraints.schema.json +56 -0
  15. package/docs/contracts/world-debts.schema.json +32 -0
  16. package/docs/contracts/world-obligation-report.schema.json +259 -0
  17. package/docs/contracts/world-waivers.schema.json +32 -0
  18. package/docs/guide/cli-reference.md +45 -0
  19. package/docs/guide/configuration.md +56 -0
  20. package/docs/templates/ci/aidlc-gate.yml +42 -2
  21. package/package.json +1 -1
  22. package/scripts/harness/agent-integration/application/dto/open-world-obligations-context-dto.ts +30 -0
  23. package/scripts/harness/agent-integration/application/ports/world-obligations-query-port.ts +30 -0
  24. package/scripts/harness/agent-integration/application/usecases/get-open-world-obligations-context-usecase.ts +66 -0
  25. package/scripts/harness/agent-integration/infrastructure/adapters/world-model-open-obligations-query-adapter.ts +58 -0
  26. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +37 -1
  27. package/scripts/harness/agent-integration/presentation/world-obligations-session-context.ts +60 -0
  28. package/scripts/harness/attestation/application/dto/attestation-document.ts +17 -4
  29. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +55 -4
  30. package/scripts/harness/attestation/application/ports/sha256-capability.ts +25 -0
  31. package/scripts/harness/attestation/application/ports/world-snapshot-root-provider.ts +10 -0
  32. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +33 -7
  33. package/scripts/harness/attestation/composition-root.ts +19 -4
  34. package/scripts/harness/attestation/domain/entities/attestation-record.ts +37 -2
  35. package/scripts/harness/attestation/index.ts +13 -2
  36. package/scripts/harness/attestation/infrastructure/adapters/node-crypto-content-hasher-adapter.ts +6 -6
  37. package/scripts/harness/attestation/infrastructure/adapters/node-crypto-sha256-capability.ts +19 -0
  38. package/scripts/harness/attestation/presentation/handlers/attest-handler.ts +5 -2
  39. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +35 -8
  40. package/scripts/harness/config-foundation/application/mappers/world-model-config-mapper.ts +15 -0
  41. package/scripts/harness/config-foundation/domain/harness-config.ts +67 -87
  42. package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +77 -88
  43. package/scripts/harness/config-foundation/domain/value-objects/world-config.ts +198 -0
  44. package/scripts/harness/config-foundation/index.ts +14 -15
  45. package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +24 -0
  46. package/scripts/harness/config-foundation/infrastructure/presets/standard.json +24 -0
  47. package/scripts/harness/config-foundation/infrastructure/presets/strict.json +24 -0
  48. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +83 -0
  49. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +83 -0
  50. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +18 -4
  51. package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +4 -1
  52. package/scripts/harness/integrations/pre-commit.ts +169 -27
  53. package/scripts/harness/main.ts +276 -126
  54. package/scripts/harness/nyquist-validation/application/dto/generate-matrix-output.ts +11 -0
  55. package/scripts/harness/nyquist-validation/application/usecases/check-ac-coverage-gate-usecase.ts +6 -0
  56. package/scripts/harness/nyquist-validation/application/usecases/generate-requirement-test-matrix-usecase.ts +8 -4
  57. package/scripts/harness/nyquist-validation/domain/entities/story-mapping.ts +29 -2
  58. package/scripts/harness/nyquist-validation/domain/services/ac-coverage-gate-policy.ts +28 -0
  59. package/scripts/harness/nyquist-validation/infrastructure/adapters/markdown-requirement-source-adapter.ts +71 -4
  60. package/scripts/harness/nyquist-validation/infrastructure/schema/matrix-schema-loader.ts +4 -0
  61. package/scripts/harness/traceability-model/application/dto/changed-design-fragment-dto.ts +31 -0
  62. package/scripts/harness/traceability-model/application/dto/traceability-world-read-dto.ts +67 -0
  63. package/scripts/harness/traceability-model/application/facades/design-change-read-facade.ts +14 -0
  64. package/scripts/harness/traceability-model/application/facades/traceability-world-read-facade.ts +372 -0
  65. package/scripts/harness/traceability-model/application/ports/staged-design-change-source-port.ts +9 -0
  66. package/scripts/harness/traceability-model/application/ports/traceability-world-read-source-port.ts +61 -0
  67. package/scripts/harness/traceability-model/composition-root.ts +26 -5
  68. package/scripts/harness/traceability-model/index.ts +26 -6
  69. package/scripts/harness/traceability-model/infrastructure/adapters/file-system-traceability-world-read-adapter.ts +223 -0
  70. package/scripts/harness/traceability-model/infrastructure/adapters/git-staged-design-change-adapter.ts +155 -0
  71. package/scripts/harness/traceability-model/infrastructure/parsers/story-catalog-parser.ts +86 -8
  72. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +31 -0
  73. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +34 -0
  74. package/scripts/harness/validator-system/composition-root.ts +21 -6
  75. package/scripts/harness/validator-system/domain/ports/world-constraint-admission-policy-port.ts +28 -0
  76. package/scripts/harness/validator-system/domain/ports/world-constraint-rederivation-policy-port.ts +11 -0
  77. package/scripts/harness/validator-system/domain/services/design-change-declaration-policy.ts +75 -0
  78. package/scripts/harness/validator-system/domain/services/world-constraint-admission-service.ts +78 -0
  79. package/scripts/harness/validator-system/domain/services/world-constraint-rederivation-service.ts +76 -0
  80. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +4 -0
  81. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +38 -16
  82. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-admission-adapter.ts +56 -0
  83. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-rederivation-adapter.ts +56 -0
  84. package/scripts/harness/world-model/application/dto/pinned-design-endpoint-dto.ts +19 -0
  85. package/scripts/harness/world-model/application/dto/world-inspection-dto.ts +43 -0
  86. package/scripts/harness/world-model/application/dto/world-obligation-report-dto.ts +54 -0
  87. package/scripts/harness/world-model/application/dto/world-resolved-config-input.ts +127 -0
  88. package/scripts/harness/world-model/application/dto/world-snapshot-root-dto.ts +8 -0
  89. package/scripts/harness/world-model/application/facades/pinned-design-endpoint-facade.ts +63 -0
  90. package/scripts/harness/world-model/application/facades/world-snapshot-root-facade.ts +19 -0
  91. package/scripts/harness/world-model/application/ports/obligation-report-writer-port.ts +7 -0
  92. package/scripts/harness/world-model/application/ports/world-control-declaration-repository-port.ts +57 -0
  93. package/scripts/harness/world-model/application/ports/world-fact-source-port.ts +17 -0
  94. package/scripts/harness/world-model/application/usecases/build-snapshot-use-case.ts +111 -0
  95. package/scripts/harness/world-model/application/usecases/derive-obligations-use-case.ts +155 -0
  96. package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts +146 -0
  97. package/scripts/harness/world-model/application/usecases/inspect-world-use-case.ts +74 -0
  98. package/scripts/harness/world-model/application/usecases/pin-constraint-endpoint-use-case.ts +139 -0
  99. package/scripts/harness/world-model/composition-root.ts +247 -0
  100. package/scripts/harness/world-model/domain/entities/constraint-record.ts +148 -0
  101. package/scripts/harness/world-model/domain/entities/control-declarations.ts +274 -0
  102. package/scripts/harness/world-model/domain/entities/edge.ts +41 -0
  103. package/scripts/harness/world-model/domain/entities/extraction-diagnostic.ts +60 -0
  104. package/scripts/harness/world-model/domain/entities/snapshot.ts +75 -0
  105. package/scripts/harness/world-model/domain/entities/world-node.ts +210 -0
  106. package/scripts/harness/world-model/domain/ports/world-hashing-port.ts +8 -0
  107. package/scripts/harness/world-model/domain/services/canonical-json-serializer.ts +113 -0
  108. package/scripts/harness/world-model/domain/services/constraint-evaluator.ts +486 -0
  109. package/scripts/harness/world-model/domain/services/obligation-derivation-service.ts +199 -0
  110. package/scripts/harness/world-model/domain/services/policy-inputs-digest-deriver.ts +66 -0
  111. package/scripts/harness/world-model/domain/services/snapshot-root-deriver.ts +218 -0
  112. package/scripts/harness/world-model/domain/services/text-content-normalizer.ts +30 -0
  113. package/scripts/harness/world-model/domain/services/violation-fingerprint-deriver.ts +183 -0
  114. package/scripts/harness/world-model/domain/value-objects/artifact-kind.ts +58 -0
  115. package/scripts/harness/world-model/domain/value-objects/change-provenance.ts +137 -0
  116. package/scripts/harness/world-model/domain/value-objects/corpus-role.ts +57 -0
  117. package/scripts/harness/world-model/domain/value-objects/declared-key.ts +35 -0
  118. package/scripts/harness/world-model/domain/value-objects/evaluation-id.ts +41 -0
  119. package/scripts/harness/world-model/domain/value-objects/explicit-constraint-relation.ts +53 -0
  120. package/scripts/harness/world-model/domain/value-objects/explicit-node-alias.ts +30 -0
  121. package/scripts/harness/world-model/domain/value-objects/node-pin.ts +34 -0
  122. package/scripts/harness/world-model/domain/value-objects/path-key.ts +103 -0
  123. package/scripts/harness/world-model/domain/value-objects/sha256-digest.ts +39 -0
  124. package/scripts/harness/world-model/domain/value-objects/violation-fingerprint.ts +36 -0
  125. package/scripts/harness/world-model/domain/value-objects/wcr-rule-id.ts +59 -0
  126. package/scripts/harness/world-model/domain/value-objects/world-node-id.ts +222 -0
  127. package/scripts/harness/world-model/index.ts +33 -0
  128. package/scripts/harness/world-model/infrastructure/adapters/adr-fact-extractor.ts +27 -0
  129. package/scripts/harness/world-model/infrastructure/adapters/assembled-world-fact-source.ts +17 -0
  130. package/scripts/harness/world-model/infrastructure/adapters/attestation-fact-extractor.ts +323 -0
  131. package/scripts/harness/world-model/infrastructure/adapters/attestation-sha256-world-hashing-adapter.ts +15 -0
  132. package/scripts/harness/world-model/infrastructure/adapters/composite-design-fact-source.ts +23 -0
  133. package/scripts/harness/world-model/infrastructure/adapters/design-corpus-fact-extractor.ts +285 -0
  134. package/scripts/harness/world-model/infrastructure/adapters/design-fact-extraction.ts +63 -0
  135. package/scripts/harness/world-model/infrastructure/adapters/file-system-obligation-report-writer-adapter.ts +37 -0
  136. package/scripts/harness/world-model/infrastructure/adapters/file-system-world-control-repository-adapters.ts +332 -0
  137. package/scripts/harness/world-model/infrastructure/adapters/integrity-manifest-fact-extractor.ts +101 -0
  138. package/scripts/harness/world-model/infrastructure/adapters/json-fact-extractor-support.ts +151 -0
  139. package/scripts/harness/world-model/infrastructure/adapters/markdown-design-fact-extractor.ts +493 -0
  140. package/scripts/harness/world-model/infrastructure/adapters/matrix-fact-extractor.ts +283 -0
  141. package/scripts/harness/world-model/infrastructure/adapters/product-fact-extractor.ts +29 -0
  142. package/scripts/harness/world-model/infrastructure/adapters/proposal-fact-extractor.ts +29 -0
  143. package/scripts/harness/world-model/infrastructure/adapters/runtime-fact-extraction.ts +19 -0
  144. package/scripts/harness/world-model/infrastructure/adapters/source-metadata-fact-extractor.ts +22 -0
  145. package/scripts/harness/world-model/infrastructure/adapters/test-reference-source-fact-extractor.ts +22 -0
  146. package/scripts/harness/world-model/infrastructure/adapters/traceability-design-fact-adapter.ts +112 -0
  147. package/scripts/harness/world-model/infrastructure/adapters/traceability-world-read-facade-merger.ts +38 -0
  148. package/scripts/harness/world-model/infrastructure/adapters/type-script-source-fact-extractor.ts +236 -0
  149. package/scripts/harness/world-model/infrastructure/adapters/unit-fact-extractor.ts +71 -0
  150. package/scripts/harness/world-model/infrastructure/adapters/world-control-declaration-mapper.ts +383 -0
  151. package/scripts/harness/world-model/presentation/cli/world-command-support.ts +60 -0
  152. package/scripts/harness/world-model/presentation/cli/world-derive-command-handler.ts +109 -0
  153. package/scripts/harness/world-model/presentation/cli/world-inspect-command-handler.ts +194 -0
  154. package/scripts/harness/world-model/presentation/cli/world-pin-command-handler.ts +100 -0
@@ -4,6 +4,7 @@
4
4
  * @work-item-id WI-141
5
5
  * @work-item-id WI-109
6
6
  * @work-item-id WI-189
7
+ * @work-item-id WI-305
7
8
  *
8
9
  * Pre-commit CLI entry.
9
10
  * Runs L2 validators against staged TypeScript files AND design-document
@@ -18,14 +19,16 @@
18
19
 
19
20
  import { execFileSync, execSync } from "node:child_process";
20
21
  import { access, readFile } from "node:fs/promises";
21
- import { createConfigFoundationModule } from "../config-foundation/composition-root.js";
22
22
  import { toValidatorSystemConfig } from "../config-foundation/application/mappers/validator-system-config-mapper.js";
23
+ import { createConfigFoundationModule } from "../config-foundation/composition-root.js";
24
+ import { toWorldModelConfig, type WorldConfigDocument } from "../config-foundation/index.js";
23
25
  import { createTraceabilityModelModule } from "../traceability-model/composition-root.js";
24
26
  import type { ValidateMetadataCommandOutput } from "../traceability-model/presentation/cli/validate-metadata-command-handler.js";
25
27
  import type { AggregatedValidationReport } from "../validator-system/application/dto/aggregated-validation-report.js";
26
28
  import type { ValidationResultContract } from "../validator-system/application/dto/validation-result-contract.js";
27
29
  import { createValidatorSystemModule } from "../validator-system/composition-root.js";
28
30
  import { HumanValidationResultFormatter } from "../validator-system/presentation/formatters/human-validation-result-formatter.js";
31
+ import { createWorldModelModule } from "../world-model/index.js";
29
32
 
30
33
  const GREEN = "\x1b[32m";
31
34
  const RED = "\x1b[31m";
@@ -81,6 +84,17 @@ async function loadPreCommitImplementationExtensions(): Promise<readonly string[
81
84
  }
82
85
  }
83
86
 
87
+ async function loadWorldConfig(): Promise<WorldConfigDocument | undefined> {
88
+ const configMod = createConfigFoundationModule();
89
+ try {
90
+ const resolvedConfig = await configMod.usecases.loadResolvedConfigUseCase.execute();
91
+ return toWorldModelConfig(resolvedConfig.config);
92
+ } catch (err) {
93
+ if (isConfigNotFoundError(err)) return undefined;
94
+ throw err;
95
+ }
96
+ }
97
+
84
98
  function isTestFile(path: string): boolean {
85
99
  return TEST_FILE_SUFFIXES.some((suffix) => path.endsWith(suffix));
86
100
  }
@@ -184,6 +198,29 @@ interface ValidateMetadataHandlerLike {
184
198
  export interface PreCommitDeps {
185
199
  readonly runL2ValidatorsUseCase: RunL2UseCaseLike;
186
200
  readonly validateMetadataCommandHandler: ValidateMetadataHandlerLike;
201
+ readonly validateDesignChangeDeclaration?: (
202
+ input: DesignChangeDeclarationCheckInput,
203
+ ) => Promise<DesignChangeDeclarationCheckResult>;
204
+ }
205
+
206
+ export interface DesignChangeDeclarationCheckInput {
207
+ readonly stagedFiles: readonly string[];
208
+ readonly commitMessage: string;
209
+ }
210
+
211
+ export interface DesignChangeDeclarationCheckFinding {
212
+ readonly code: string;
213
+ readonly path: string;
214
+ readonly declaredKey: string;
215
+ readonly expectedWorkItemIds: readonly string[];
216
+ readonly constraintIds: readonly string[];
217
+ }
218
+
219
+ export interface DesignChangeDeclarationCheckResult {
220
+ readonly status: "skipped" | "passed" | "warning" | "failed";
221
+ readonly checkedFragmentCount: number;
222
+ readonly findings: readonly DesignChangeDeclarationCheckFinding[];
223
+ readonly warningCodes: readonly string[];
187
224
  }
188
225
 
189
226
  export interface PreCommitResult {
@@ -202,6 +239,8 @@ export interface PreCommitOptions {
202
239
  readonly implementationExtensions?: readonly string[];
203
240
  readonly allowConditionalBypass?: boolean;
204
241
  readonly evidenceRoot?: string;
242
+ /** Files observed by the commit-msg design declaration path, including deletions. */
243
+ readonly designChangeFiles?: readonly string[];
205
244
  }
206
245
 
207
246
  export interface BypassBlockerClass {
@@ -239,6 +278,21 @@ function getStagedFiles(): string[] {
239
278
  }
240
279
  }
241
280
 
281
+ function getStagedDesignChangeFiles(): string[] {
282
+ try {
283
+ const output = execFileSync("git", ["diff", "--cached", "--name-only", "--diff-filter=ACDMR"], {
284
+ encoding: "utf-8",
285
+ stdio: ["ignore", "pipe", "ignore"],
286
+ });
287
+ return output
288
+ .split("\n")
289
+ .map((filePath) => filePath.trim())
290
+ .filter((filePath) => filePath.length > 0);
291
+ } catch {
292
+ return [];
293
+ }
294
+ }
295
+
242
296
  function buildReport(results: readonly ValidationResultContract[]): AggregatedValidationReport {
243
297
  const passed = results.filter((r) => r.passed && !r.skipped).length;
244
298
  const failed = results.filter((r) => !r.passed && !r.skipped).length;
@@ -276,6 +330,56 @@ function hasWorkItemTrailer(commitMessage: string): boolean {
276
330
  return WORK_ITEM_TRAILER_PATTERN.test(commitMessage);
277
331
  }
278
332
 
333
+ function getWorkItemTrailerIds(commitMessage: string): readonly string[] {
334
+ return Object.freeze(
335
+ [...new Set([...commitMessage.matchAll(/^Work-Item:\s*(WI-\d+)\s*$/gm)].map((match) => match[1]))].sort(),
336
+ );
337
+ }
338
+
339
+ function createDesignChangeDeclarationValidator(
340
+ worldConfig: WorldConfigDocument | undefined,
341
+ traceabilityMod: ReturnType<typeof createTraceabilityModelModule>,
342
+ policy: ReturnType<typeof createValidatorSystemModule>["designChangeDeclarationPolicy"],
343
+ ): NonNullable<PreCommitDeps["validateDesignChangeDeclaration"]> {
344
+ return async ({ stagedFiles, commitMessage }) => {
345
+ if (worldConfig?.enabled !== true) {
346
+ return { status: "skipped", checkedFragmentCount: 0, findings: [], warningCodes: [] };
347
+ }
348
+ const changed = await traceabilityMod.designChangeReadFacade.observe(stagedFiles);
349
+ if (changed.state === "unavailable") {
350
+ return {
351
+ status: "warning",
352
+ checkedFragmentCount: 0,
353
+ findings: [],
354
+ warningCodes: changed.diagnostics.map((item) => item.code).sort(),
355
+ };
356
+ }
357
+ const pinned = await createWorldModelModule({
358
+ rootDir: process.cwd(),
359
+ resolvedConfig: worldConfig,
360
+ }).pinnedDesignEndpointFacade.read();
361
+ if (pinned.state === "unavailable") {
362
+ return {
363
+ status: "warning",
364
+ checkedFragmentCount: 0,
365
+ findings: [],
366
+ warningCodes: pinned.diagnosticCodes.map((code) => `constraint-${code}`).sort(),
367
+ };
368
+ }
369
+ const evaluation = policy.evaluate({
370
+ changedFragments: changed.fragments,
371
+ pinnedEndpoints: pinned.endpoints,
372
+ trailerWorkItemIds: getWorkItemTrailerIds(commitMessage),
373
+ });
374
+ return {
375
+ status: evaluation.findings.length > 0 ? "failed" : "passed",
376
+ checkedFragmentCount: evaluation.checkedFragmentCount,
377
+ findings: evaluation.findings,
378
+ warningCodes: [],
379
+ };
380
+ };
381
+ }
382
+
279
383
  function extractTrailer(commitMessage: string, trailerName: string): string | undefined {
280
384
  const pattern = new RegExp(`^${trailerName}:\\s*(.+?)\\s*$`, "m");
281
385
  return pattern.exec(commitMessage)?.[1]?.trim();
@@ -366,10 +470,9 @@ function hasNonBypassableBlocker(blockers: readonly BypassBlockerClass[]): boole
366
470
  }
367
471
 
368
472
  function normalizeImplementationExtensions(extensions: readonly string[] | undefined): readonly string[] {
369
- const rawExtensions = extensions === undefined || extensions.length === 0
370
- ? DEFAULT_IMPLEMENTATION_EXTENSIONS
371
- : extensions;
372
- return rawExtensions.map((extension) => extension.startsWith(".") ? extension : `.${extension}`);
473
+ const rawExtensions =
474
+ extensions === undefined || extensions.length === 0 ? DEFAULT_IMPLEMENTATION_EXTENSIONS : extensions;
475
+ return rawExtensions.map((extension) => (extension.startsWith(".") ? extension : `.${extension}`));
373
476
  }
374
477
 
375
478
  function hasAnyExtension(filePath: string, extensions: readonly string[]): boolean {
@@ -430,10 +533,12 @@ export async function runPreCommit(
430
533
  }
431
534
 
432
535
  const merged = mergePerUnitResults(runs);
433
- blockerClasses.push(...merged.flatMap((result) => {
434
- const blocker = classifyValidatorFailure(result);
435
- return blocker === undefined ? [] : [blocker];
436
- }));
536
+ blockerClasses.push(
537
+ ...merged.flatMap((result) => {
538
+ const blocker = classifyValidatorFailure(result);
539
+ return blocker === undefined ? [] : [blocker];
540
+ }),
541
+ );
437
542
  const report = buildReport(merged);
438
543
  sections.push("");
439
544
  sections.push(`${BOLD}== 実装ファイル (${implementationFiles.length} file(s)) ==${RESET}`);
@@ -469,6 +574,42 @@ export async function runPreCommit(
469
574
  }
470
575
  }
471
576
 
577
+ if (options.commitMessage !== undefined && deps.validateDesignChangeDeclaration !== undefined) {
578
+ const designResult = await deps.validateDesignChangeDeclaration({
579
+ stagedFiles: options.designChangeFiles ?? stagedFiles,
580
+ commitMessage: options.commitMessage,
581
+ });
582
+ if (designResult.status !== "skipped") {
583
+ sections.push("");
584
+ sections.push(`${BOLD}== Design change declaration ==${RESET}`);
585
+ if (designResult.status === "failed") {
586
+ for (const finding of designResult.findings) {
587
+ sections.push(
588
+ `${RED}FAIL${RESET} ${finding.code}: ${finding.path}#${finding.declaredKey} ` +
589
+ `(expected ${finding.expectedWorkItemIds.join(", ") || "an explicit Work Item"}; ` +
590
+ `constraints ${finding.constraintIds.join(", ")})`,
591
+ );
592
+ }
593
+ sections.push(`${DIM}Local commit-msg is forgeable; L3-008 remains authoritative.${RESET}`);
594
+ blockerClasses.push({
595
+ code: "design-change-declaration",
596
+ label: "design change declaration",
597
+ bypassable: false,
598
+ });
599
+ exitCode = maxExitCode(exitCode, 1);
600
+ } else if (designResult.status === "warning") {
601
+ sections.push(
602
+ `${DIM}WARN ${designResult.warningCodes.join(", ")}; local fast-path continues fail-open. ` +
603
+ `L3-008 remains authoritative.${RESET}`,
604
+ );
605
+ } else {
606
+ sections.push(
607
+ `${GREEN}PASS${RESET} ${designResult.checkedFragmentCount} pinned fragment(s) have matching Work-Item declarations.`,
608
+ );
609
+ }
610
+ }
611
+ }
612
+
472
613
  if (options.commitMessage !== undefined) {
473
614
  const bypassValidation = await validateBypassTrailers(options.commitMessage, options.evidenceRoot);
474
615
  if (bypassValidation.hasAnyBypassTrailer) {
@@ -512,7 +653,10 @@ function getChangedFilesInRange(baseRef: string, headRef: string): string[] {
512
653
  encoding: "utf-8",
513
654
  stdio: ["ignore", "pipe", "ignore"],
514
655
  });
515
- return output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
656
+ return output
657
+ .split("\n")
658
+ .map((line) => line.trim())
659
+ .filter((line) => line.length > 0);
516
660
  } catch {
517
661
  return [];
518
662
  }
@@ -524,7 +668,10 @@ function getCommitMessagesInRange(baseRef: string, headRef: string): string[] {
524
668
  encoding: "utf-8",
525
669
  stdio: ["ignore", "pipe", "ignore"],
526
670
  });
527
- return output.split("\x1e").map((message) => message.trim()).filter((message) => message.length > 0);
671
+ return output
672
+ .split("\x1e")
673
+ .map((message) => message.trim())
674
+ .filter((message) => message.length > 0);
528
675
  } catch {
529
676
  return [];
530
677
  }
@@ -539,10 +686,7 @@ function toBypassAuditStdout(stdout: string, changedFiles: readonly string[]): s
539
686
  return stdout.replace("No staged files to check. Skipping.", "No changed files in range to check. Skipping.");
540
687
  }
541
688
 
542
- export async function runBypassAudit(
543
- deps: PreCommitDeps,
544
- options: BypassAuditOptions = {},
545
- ): Promise<PreCommitResult> {
689
+ export async function runBypassAudit(deps: PreCommitDeps, options: BypassAuditOptions = {}): Promise<PreCommitResult> {
546
690
  const baseRef = options.baseRef ?? "origin/main";
547
691
  const headRef = options.headRef ?? "HEAD";
548
692
  const changedFiles = options.changedFiles ?? getChangedFilesInRange(baseRef, headRef);
@@ -580,10 +724,7 @@ export async function runPreCommitCli(): Promise<void> {
580
724
  try {
581
725
  const stagedFiles = getStagedFiles();
582
726
  const validatorMod = createValidatorSystemModule(await loadValidatorSystemConfig());
583
- const traceabilityMod = createTraceabilityModelModule(
584
- process.cwd(),
585
- await loadTraceabilityModelOptions(),
586
- );
727
+ const traceabilityMod = createTraceabilityModelModule(process.cwd(), await loadTraceabilityModelOptions());
587
728
 
588
729
  const result = await runPreCommit(
589
730
  stagedFiles,
@@ -616,19 +757,23 @@ export async function runCommitMsgCli(commitMessagePath: string | undefined): Pr
616
757
  const stagedFiles = getStagedFiles();
617
758
  const commitMessage = await readFile(commitMessagePath, "utf-8");
618
759
  const validatorMod = createValidatorSystemModule(await loadValidatorSystemConfig());
619
- const traceabilityMod = createTraceabilityModelModule(
620
- process.cwd(),
621
- await loadTraceabilityModelOptions(),
622
- );
760
+ const traceabilityMod = createTraceabilityModelModule(process.cwd(), await loadTraceabilityModelOptions());
761
+ const worldConfig = await loadWorldConfig();
623
762
 
624
763
  const result = await runPreCommit(
625
764
  stagedFiles,
626
765
  {
627
766
  runL2ValidatorsUseCase: validatorMod.runL2ValidatorsUseCase,
628
767
  validateMetadataCommandHandler: traceabilityMod.validateMetadataCommandHandler,
768
+ validateDesignChangeDeclaration: createDesignChangeDeclarationValidator(
769
+ worldConfig,
770
+ traceabilityMod,
771
+ validatorMod.designChangeDeclarationPolicy,
772
+ ),
629
773
  },
630
774
  {
631
775
  commitMessage,
776
+ designChangeFiles: getStagedDesignChangeFiles(),
632
777
  implementationExtensions: await loadPreCommitImplementationExtensions(),
633
778
  evidenceRoot: process.cwd(),
634
779
  },
@@ -652,10 +797,7 @@ function parseCliFlag(args: readonly string[], flag: string): string | undefined
652
797
  export async function runBypassAuditCli(args: readonly string[] = []): Promise<void> {
653
798
  try {
654
799
  const validatorMod = createValidatorSystemModule(await loadValidatorSystemConfig());
655
- const traceabilityMod = createTraceabilityModelModule(
656
- process.cwd(),
657
- await loadTraceabilityModelOptions(),
658
- );
800
+ const traceabilityMod = createTraceabilityModelModule(process.cwd(), await loadTraceabilityModelOptions());
659
801
  const result = await runBypassAudit(
660
802
  {
661
803
  runL2ValidatorsUseCase: validatorMod.runL2ValidatorsUseCase,