phasegate 0.132.0 → 0.134.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 (27) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.ja.md +1 -4
  3. package/README.md +1 -5
  4. package/docs/ADR/ADR-018-drift-detect-pointers.md +47 -0
  5. package/docs/guide/configuration.md +0 -1
  6. package/docs/guide/layer-model.md +28 -7
  7. package/package.json +1 -1
  8. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +0 -16
  9. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +0 -16
  10. package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +14 -1
  11. package/scripts/harness/main.ts +3 -3
  12. package/scripts/harness/validator-system/composition-root.ts +1 -14
  13. package/scripts/harness/validator-system/domain/ports/design-document-port.ts +3 -0
  14. package/scripts/harness/validator-system/domain/ports/source-code-analyzer-port.ts +1 -0
  15. package/scripts/harness/validator-system/domain/ports/validator-config-port.ts +1 -1
  16. package/scripts/harness/validator-system/domain/services/l4/drift-detection-service.ts +44 -2
  17. package/scripts/harness/validator-system/domain/services/validator-execution-service.ts +2 -2
  18. package/scripts/harness/validator-system/domain/services/validator-registry.ts +2 -2
  19. package/scripts/harness/validator-system/domain/value-objects/layer-config.ts +2 -2
  20. package/scripts/harness/validator-system/domain/value-objects/validator-definition.ts +5 -6
  21. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +0 -2
  22. package/scripts/harness/validator-system/infrastructure/adapters/biome-ast-source-code-analyzer-adapter.ts +11 -0
  23. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +1 -3
  24. package/scripts/harness/validator-system/infrastructure/adapters/markdown-design-document-adapter.ts +49 -5
  25. package/scripts/harness/validator-system/presentation/handlers/run-validators-handler.ts +6 -35
  26. package/scripts/harness/validator-system/application/dto/run-l0-validators-input.ts +0 -9
  27. package/scripts/harness/validator-system/application/use-cases/run-l0-validators-usecase.ts +0 -60
package/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.134.0] - 2026-05-08
11
+
12
+ ### Changed
13
+
14
+ - **WI-034: L0 legacy validator (`L0-001` / `L0-002`) の撤去** — `validator-system` から FUSE 時代の未実装 validator 定義と `RunL0ValidatorsUseCase` を削除し、config schema / `phasegate.config.json` から `layers.L0` を撤去。`list-errors --layer L0` は空のままになり、`validate --layer L0` は runtime L0 が agent-integration hooks と Husky hooks で提供される旨の案内を表示して exit 0 で終了する。
15
+ - **Migration guide**: CI で `phasegate validate --layer L0` を実行していた場合、そのジョブは削除してください。L0 相当の防御は `.claude/settings.json` / `.codex/hooks.json` の runtime hook と `.husky/pre-commit` / `.husky/commit-msg` で有効化し、CI 側は `phasegate validate --layer L2` / `--layer L3` / `--layer L4` または `--layer all` に移行します。
16
+
17
+ ## [0.133.0] - 2026-05-08
18
+
19
+ ### Added
20
+
21
+ - **WI-095 — L4-001 drift-detect の design pointers 対応** — 設計見出し直下の `<!-- pointers: ... -->` / `<pointers>` block で実装ファイルpathを明示できるようにし、設計名とcode export名が異なる移行中ケースのfalse positiveを抑制。ADR-018でsyntaxとORセマンティクスを定義。
22
+ - **post-publish dogfood**: `/private/tmp/phasegate-dogfood-0.133.0` で `npx phasegate@0.133.0 phasegate:detect-drift --json` を実行。設計見出し `UserProfile` に `<!-- pointers: scripts/harness/sample-unit/domain/user-profile.ts -->` を付け、コード側は `export class UserProfileV2 {}` とした状態で `drifts: [] / totalCount: 0` を確認。
23
+
24
+ ### Fixed
25
+
26
+ - **WI-096 — `phasegate:status` が `layers.L?.enabled` user override を表示に反映しない問題を修正** — status表示用のenabledLayersをpreset値と明示overrideから合成し、`strict + L4.enabled:false` / `minimal + L4.enabled:true` の双方に対応。
27
+ - **post-publish dogfood**: `npx phasegate@0.133.0 phasegate:status --json` で `project.preset: "minimal" + layers.L4.enabled: true` の L4 `enabled: true`、および `project.preset: "strict" + layers.L4.enabled: false` の L4 `enabled: false` を確認。
28
+
10
29
  ## [0.132.0] - 2026-05-08
11
30
 
12
31
  ### Fixed
package/README.ja.md CHANGED
@@ -161,8 +161,6 @@ npx phasegate update-skills # スキルを最新版に再デプロイ
161
161
 
162
162
  エラーは `HarnessError` 形式(理由 / ADR 参照 / 修正例)で返されるため、AI agent が自己修正できます。
163
163
 
164
- > `--layer L0` の `L0-001` / `L0-002` は legacy validator で `enabled: false`。実体の L0 は agent-integration の hook と Husky です。
165
-
166
164
  詳細: [5-Layer Defense Model](docs/guide/layer-model.md)
167
165
 
168
166
  ---
@@ -238,7 +236,7 @@ L2-STORY-REFLECTION バリデータがこのアノテーションを検出し、
238
236
  "project": { "name": "my-project", "preset": "standard" },
239
237
  "architecture": { "preset": "clean" },
240
238
  "layers": {
241
- "L0": { "enabled": false }, "L1": { "enabled": true },
239
+ "L1": { "enabled": true },
242
240
  "L2": { "enabled": true }, "L3": { "enabled": true },
243
241
  "L4": { "enabled": false }
244
242
  },
@@ -456,7 +454,6 @@ reports/
456
454
  | **[WI-031](docs/inception/_cross/WI-031/description.md)** | CI template の二系統統一 + `phasegate init --with-ci` |
457
455
  | **[WI-032](docs/inception/_cross/WI-032/description.md)** | AGENTS.md / CLAUDE.md auto-refresh パイプライン |
458
456
  | **[WI-033](docs/inception/_cross/WI-033/description.md)** | `doc-freshness` / `pointer-validation` を L4 validator に昇格 |
459
- | **[WI-034](docs/inception/_cross/WI-034/description.md)** | L0 legacy validator (`L0-001` / `L0-002`) の撤去 |
460
457
 
461
458
  L3 Nyquist Validation の `requirement-test-matrix.json` 自動生成はまだ未自動化です。現時点では手動セットアップで利用できます。
462
459
 
package/README.md CHANGED
@@ -157,8 +157,6 @@ npx phasegate update-skills
157
157
  | L3 | CI/CD pipeline | Security, performance, coverage (90%/95%), requirements traceability |
158
158
  | L4 | Scheduled (weekly). Currently `layers.L4.enabled: false` by default — opt-in per project | Design-code drift, cross-document consistency, dead code, doc freshness, pointer validation |
159
159
 
160
- > The `L0-001` / `L0-002` validators that appear in `list-errors --layer L0` output are legacy definitions from an earlier design and are disabled by default (`layers.L0.enabled: false`). The runtime L0 enforcement happens via the agent-integration hook scripts and Husky git hooks listed above, not via those validators.
161
-
162
160
  ---
163
161
 
164
162
  ## 28 Skills
@@ -339,7 +337,6 @@ For selection guidance and config examples see [Preset Selection Guide](docs/gui
339
337
  {
340
338
  "project": { "name": "my-project", "preset": "standard" },
341
339
  "layers": {
342
- "L0": { "enabled": false },
343
340
  "L1": { "enabled": true },
344
341
  "L2": { "enabled": true },
345
342
  "L3": { "enabled": true },
@@ -468,7 +465,7 @@ npx phasegate <command> [options]
468
465
  |---|---|
469
466
  | `init --name <name>` | Initialize project, deploy skills, generate config |
470
467
  | `lint` | Run L1 Biome AST checks |
471
- | `validate --layer <L1-L4\|all>` | Run validators for specified layer |
468
+ | `validate --layer <L2-L4\|all>` | Run validators for specified layer (`--layer L0` prints runtime hook guidance) |
472
469
  | `ci-check` | Full CI check (L2-L4) |
473
470
  | `update-skills` | Update skills to latest version |
474
471
  | `phasegate:status` | Display overall harness health summary |
@@ -518,7 +515,6 @@ The main path is ready for project use, but a few documented behaviors still req
518
515
  | **[WI-031](docs/inception/_cross/WI-031/description.md)** | CI template unification + `phasegate init --with-ci` | Today the bundled YAML and the `ci:generate-template` output diverge (different cron schedule, only the bundled one creates GitHub Issues). `phasegate init` does not deploy the workflow, so L4 only runs after the user manually copies the file. WI-031 unifies the two paths and adds an opt-in deploy flag. |
519
516
  | **[WI-032](docs/inception/_cross/WI-032/description.md)** | AGENTS.md / CLAUDE.md auto-refresh pipeline | `ci:migrate-agents-md` exists as a one-shot CLI but there is no scheduled job, and CLAUDE.md is fully hand-maintained. WI-032 adds an `auto-refresh-agent-context` workflow plus a template-driven CLAUDE.md regenerator that preserves user-owned sections. |
520
517
  | **[WI-033](docs/inception/_cross/WI-033/description.md)** | Promote `doc-freshness` / `pointer-validation` to L4 validators | Both capabilities exist as `p2:check-freshness` / `p2:validate-pointers` CLI commands but are not registered as L4 validators, so `validate --layer L4` skips them. WI-033 plumbs them through `validator-system` so they run via the standard L4 path and presets. |
521
- | **[WI-034](docs/inception/_cross/WI-034/description.md)** | Retire legacy L0 validators (`L0-001` / `L0-002`) | The `fuse-hook-config` / `fuse-mount-status` validator IDs are leftovers from an earlier FUSE-based design. They are disabled by default and have no actual implementation behind them. WI-034 removes them and lets the agent-integration runtime hooks be the sole L0 surface. |
522
518
 
523
519
  `requirement-test-matrix.json` auto-generation for L3 Nyquist Validation is not automated yet; see the L3 guide for the current manual setup.
524
520
 
@@ -0,0 +1,47 @@
1
+ # ADR-018: drift-detect の design pointers 仕様
2
+
3
+ ## Status
4
+
5
+ Accepted — 2026-05-08
6
+
7
+ ## Context
8
+
9
+ L4-001 `drift-detect` は設計文書の `##` / `###` 見出しから抽出した要素名と、TypeScript source の export 名を双方向に比較する。WI-091 で括弧 qualifier の normalize は導入済みだが、次のようなケースでは依然として false positive が起きる。
10
+
11
+ - 設計名と実装 export 名が移行期間中だけ異なる
12
+ - 設計文書では業務概念名、コードではより具体的な型名を使う
13
+ - 同名・類似名の要素が複数 unit に存在し、ファイル path で対応を示したい
14
+ - 実装ファイル名は安定しているが export 名を変更している
15
+
16
+ GitHub Issue #4 の follow-up として WI-095 を起票した。
17
+
18
+ ## Decision
19
+
20
+ 設計見出し単位で、対応する実装ファイル path を明示できる `pointers` を導入する。初期実装では以下の2形式を正式に受け付ける。
21
+
22
+ ```markdown
23
+ ## UserProfile
24
+ <!-- pointers: scripts/harness/user/domain/user-profile.ts -->
25
+ ```
26
+
27
+ ```markdown
28
+ ## UserProfile
29
+ <pointers>
30
+ - scripts/harness/user/domain/user-profile.ts
31
+ - scripts/harness/user/domain/user-profile-types.ts
32
+ </pointers>
33
+ ```
34
+
35
+ セマンティクスは以下とする。
36
+
37
+ - pointers がない要素は従来通り、設計要素名と code export 名の完全一致で判定する
38
+ - pointers がある設計要素は、いずれか1つの pointer が code export の定義ファイル path と一致すれば、設計→コード drift とみなさない
39
+ - pointer で対応付けられた code export は、名前が設計要素名と異なっていても code→design drift とみなさない
40
+ - 複数 pointer は OR として扱う
41
+ - path 比較は POSIX separator に正規化し、絶対 path の末尾が pointer と一致する場合も一致とする
42
+
43
+ ## Consequences
44
+
45
+ `DesignDocumentPort` は後方互換のため既存 `getElements()` を維持し、optional な `getElementPointers()` を追加する。`SourceCodeAnalyzerPort` も optional な `getElementFilePathMap()` を追加する。両 optional port が未実装の場合、`DriftDetectionService` は従来挙動のまま動作する。
46
+
47
+ YAML fenced metadata は今回採用しない。Markdown本文の構造と混ざりやすく、既存のコードブロック抽出や設計例との衝突リスクが高いため、必要になった時点で別ADRで拡張する。
@@ -15,7 +15,6 @@ This file is the **Single Source of Truth** for all quality configuration in a P
15
15
  "preset": "standard" // "minimal" | "standard" | "strict"
16
16
  },
17
17
  "layers": {
18
- "L0": { "enabled": false },
19
18
  "L1": { "enabled": true },
20
19
  "L2": { "enabled": true },
21
20
  "L3": { "enabled": true },
@@ -52,13 +52,13 @@ Deployed by `phasegate init --with-husky` into `.husky/`.
52
52
  | **.husky/pre-commit** | `npx phasegate pre-commit` | Runs L2 validators (phase-gate / metadata / story-reflection / test-quality) on staged files. Fails the commit on violation. |
53
53
  | **.husky/commit-msg** | `npx phasegate commit-msg $1` | Enforces the `Work-Item: WI-XXX` trailer when WI directories or their contents are staged. Ensures every commit is traceable to a work item. |
54
54
 
55
- ### About the `validate --layer L0` CLI
55
+ ### About `validate --layer L0`
56
56
 
57
57
  ```bash
58
58
  npx phasegate validate --layer L0
59
59
  ```
60
60
 
61
- `list-errors --layer L0` surfaces `L0-001 fuse-hook-config` / `L0-002 fuse-mount-status` these are **legacy validator definitions** from an earlier design and are disabled by default (`layers.L0.enabled: false`). The **runtime L0 enforcement happens via the agent-runtime hooks and Husky git hooks above**, not via those validators.
61
+ `validate --layer L0` is kept as a compatibility alias that prints guidance and exits successfully. Runtime L0 enforcement happens via the agent-runtime hooks and Husky git hooks above. `list-errors --layer L0` returns no validator definitions.
62
62
 
63
63
  ---
64
64
 
@@ -150,6 +150,27 @@ L4 validators are designed to run on a weekly schedule and detect slow-moving dr
150
150
 
151
151
  > **Note (2026-04-25)**: `doc-freshness` and `pointer-validation` capabilities exist as standalone CLI commands `p2:check-freshness` and `p2:validate-pointers` (implemented in the `phase2-extensions` unit), **not as L4 validators yet**. Promoting them to L4-004 / L4-005 is tracked as **WI-033**. Until that lands, run them explicitly via `npx phasegate p2:check-freshness` / `p2:validate-pointers`.
152
152
 
153
+ ### Drift-detect design pointers
154
+
155
+ L4-001 normally matches design headings to code exports by name. When a heading intentionally maps to a differently named implementation file, add a pointer directly under the heading:
156
+
157
+ ```markdown
158
+ ## UserProfile
159
+ <!-- pointers: scripts/harness/user/domain/user-profile.ts -->
160
+ ```
161
+
162
+ For multiple files:
163
+
164
+ ```markdown
165
+ ## UserProfile
166
+ <pointers>
167
+ - scripts/harness/user/domain/user-profile.ts
168
+ - scripts/harness/user/domain/user-profile-types.ts
169
+ </pointers>
170
+ ```
171
+
172
+ Pointers are optional and backward compatible. If any listed path matches the file defining a code export, drift-detect treats that design heading and export as corresponding even when their names differ.
173
+
153
174
  **Command:**
154
175
 
155
176
  ```bash
@@ -206,14 +227,14 @@ History: prior to v0.131.0, the aggregator's `failOnWarning` flag was effectivel
206
227
 
207
228
  Presets control which layers are active. Choose based on project maturity and team discipline.
208
229
 
209
- | Preset | L0 | L1 | L2 | L3 | L4 |
230
+ | Preset | L0 hooks | L1 | L2 | L3 | L4 |
210
231
  |------------|-----|-----|-----|-----|-----|
211
- | `minimal` | off | on | on | off | off |
212
- | `standard` | off | on | on | on | off |
213
- | `strict` | on | on | on | on | on |
232
+ | `minimal` | installed separately | on | on | off | off |
233
+ | `standard` | installed separately | on | on | on | off |
234
+ | `strict` | installed separately | on | on | on | on |
214
235
 
215
236
  - **minimal** — Suitable for early prototyping. Editor-time rules and pre-commit checks only.
216
237
  - **standard** — Recommended for active development. Adds CI/CD validators for security, performance, coverage, and traceability.
217
- - **strict** — Full defense. Enables the Hooks Engine (L0) and scheduled drift detection (L4). Use for production-critical codebases.
238
+ - **strict** — Full defense. Enables scheduled drift detection (L4). Runtime L0 hooks are configured through the agent and Husky hook files, not through validator presets.
218
239
 
219
240
  Presets are configured in `phasegate.config.json`, the single source of truth for all quality settings.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.132.0",
3
+ "version": "0.134.0",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "MIT",
@@ -68,22 +68,6 @@
68
68
  "type": "object",
69
69
  "additionalProperties": false,
70
70
  "properties": {
71
- "L0": {
72
- "type": "object",
73
- "additionalProperties": false,
74
- "properties": {
75
- "enabled": {
76
- "type": "boolean"
77
- },
78
- "validators": {
79
- "type": "array",
80
- "items": {
81
- "type": "string"
82
- },
83
- "uniqueItems": true
84
- }
85
- }
86
- },
87
71
  "L1": {
88
72
  "type": "object",
89
73
  "additionalProperties": false,
@@ -68,22 +68,6 @@
68
68
  "type": "object",
69
69
  "additionalProperties": false,
70
70
  "properties": {
71
- "L0": {
72
- "type": "object",
73
- "additionalProperties": false,
74
- "properties": {
75
- "enabled": {
76
- "type": "boolean"
77
- },
78
- "validators": {
79
- "type": "array",
80
- "items": {
81
- "type": "string"
82
- },
83
- "uniqueItems": true
84
- }
85
- }
86
- },
87
71
  "L1": {
88
72
  "type": "object",
89
73
  "additionalProperties": false,
@@ -1,4 +1,5 @@
1
1
  // @layer infrastructure
2
+ // @unit harness-api
2
3
  // harness-config-query-adapter.ts — HarnessConfigQueryAdapter
3
4
 
4
5
  import * as fs from 'node:fs/promises';
@@ -12,6 +13,7 @@ interface HarnessConfigJson {
12
13
  name: string;
13
14
  preset: 'minimal' | 'standard' | 'strict';
14
15
  };
16
+ layers?: Partial<Record<LayerId, { enabled?: boolean }>>;
15
17
  paths?: {
16
18
  designDocs?: string;
17
19
  integrationTests?: string;
@@ -46,7 +48,18 @@ export class HarnessConfigQueryAdapter implements ConfigQueryPort {
46
48
  async getPresetInfo(): Promise<PresetInfo> {
47
49
  const config = await this.readConfig();
48
50
  const preset = config.project.preset;
49
- const enabledLayers = PRESET_LAYERS[preset] ?? ['L1', 'L2', 'L3'];
51
+ const presetLayers = PRESET_LAYERS[preset] ?? ['L1', 'L2', 'L3'];
52
+ const enabledLayerSet = new Set<LayerId>(presetLayers);
53
+
54
+ for (const [layerId, layerConfig] of Object.entries(config.layers ?? {}) as [LayerId, { enabled?: boolean }][]) {
55
+ if (layerConfig.enabled === true) {
56
+ enabledLayerSet.add(layerId);
57
+ } else if (layerConfig.enabled === false) {
58
+ enabledLayerSet.delete(layerId);
59
+ }
60
+ }
61
+
62
+ const enabledLayers = (['L1', 'L2', 'L3', 'L4'] as const).filter((layerId) => enabledLayerSet.has(layerId));
50
63
  return { name: preset, enabledLayers };
51
64
  }
52
65
 
@@ -110,7 +110,7 @@ Commands:
110
110
 
111
111
  lint Run lint checks (--json, --target <path>)
112
112
 
113
- validate Run validators (--layer L0|L2|L3|L4|all, --unit, --format human|agent|ci)
113
+ validate Run validators (--layer L2|L3|L4|all; L0 prints runtime hook info, --unit, --format human|agent|ci)
114
114
  ci-check CI check (--quick for quick mode, --fail-on-reject, --dry-run, --files)
115
115
 
116
116
  phasegate:check-ready Check ready status (--json)
@@ -263,10 +263,10 @@ Options:
263
263
  --help, -h Show this help`,
264
264
  validate: `Usage: phasegate validate [options]
265
265
 
266
- Run validators against the project. Without --layer, runs all enabled layers (L0/L2/L3/L4).
266
+ Run validators against the project. Without --layer, runs all enabled validator layers (L2/L3/L4).
267
267
 
268
268
  Options:
269
- --layer <L0|L2|L3|L4> Run only the specified layer
269
+ --layer <L0|L2|L3|L4> Run only the specified validator layer; L0 prints runtime hook info
270
270
  --json Output machine-readable JSON
271
271
  --help, -h Show this help`,
272
272
  lint: `Usage: phasegate lint [options]
@@ -10,7 +10,6 @@ import { ValidationRule } from './domain/value-objects/validation-rule.js';
10
10
  import { ValidatorRegistry } from './domain/services/validator-registry.js';
11
11
  import { ValidatorExecutionService } from './domain/services/validator-execution-service.js';
12
12
  import { ValidationResultContractMapper } from './application/mappers/validation-result-contract-mapper.js';
13
- import { RunL0ValidatorsUseCase } from './application/use-cases/run-l0-validators-usecase.js';
14
13
  import { RunL2ValidatorsUseCase } from './application/use-cases/run-l2-validators-usecase.js';
15
14
  import { RunL3ValidatorsUseCase } from './application/use-cases/run-l3-validators-usecase.js';
16
15
  import { RunL4ValidatorsUseCase } from './application/use-cases/run-l4-validators-usecase.js';
@@ -45,7 +44,6 @@ import { join } from 'node:path';
45
44
  const DEFAULT_CONFIG = {
46
45
  preset: 'standard' as const,
47
46
  layers: {
48
- L0: { enabled: true, validators: ['L0-001', 'L0-002'] },
49
47
  L2: { enabled: true, validators: ['L2-001', 'L2-002', 'L2-003'] },
50
48
  L3: { enabled: true, validators: ['L3-001', 'L3-002', 'L3-003', 'L3-004'], coverageThreshold: 90, bundleSizeLimit: 512000 },
51
49
  L4: { enabled: true, validators: ['L4-001', 'L4-002', 'L4-003'] },
@@ -61,7 +59,7 @@ function buildDefaultRegistry(): ValidatorRegistry {
61
59
  fixExample: null,
62
60
  });
63
61
 
64
- const createDef = (id: string, layer: 'L0' | 'L2' | 'L3' | 'L4', enabledCondition: 'always' | 'strictOnly' = 'always', externalPolicyRef: string | null = null) =>
62
+ const createDef = (id: string, layer: 'L2' | 'L3' | 'L4', enabledCondition: 'always' | 'strictOnly' = 'always', externalPolicyRef: string | null = null) =>
65
63
  ValidatorDefinition.create({
66
64
  validatorId: ValidatorId.create(id),
67
65
  layer,
@@ -75,7 +73,6 @@ function buildDefaultRegistry(): ValidatorRegistry {
75
73
  });
76
74
 
77
75
  const definitions = [
78
- createDef('L0-001', 'L0', 'always'),
79
76
  createDef('L2-001', 'L2', 'always', 'PhaseGatePolicyPort'),
80
77
  createDef('L2-002', 'L2', 'always', 'MetadataPolicyPort'),
81
78
  createDef('L2-003', 'L2', 'always'),
@@ -93,7 +90,6 @@ function buildDefaultRegistry(): ValidatorRegistry {
93
90
 
94
91
  export interface ValidatorSystemModule {
95
92
  registry: ValidatorRegistry;
96
- runL0ValidatorsUseCase: RunL0ValidatorsUseCase;
97
93
  runL1ValidatorsUseCase: RunL1ValidatorsUseCase;
98
94
  runL2ValidatorsUseCase: RunL2ValidatorsUseCase;
99
95
  runL3ValidatorsUseCase: RunL3ValidatorsUseCase;
@@ -124,13 +120,6 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
124
120
  const securityScannerPort = new FileSystemSecurityPatternScannerAdapter();
125
121
  const performanceScannerPort = new AstPerformanceScannerAdapter();
126
122
 
127
- const runL0ValidatorsUseCase = new RunL0ValidatorsUseCase({
128
- validatorRegistry: registry,
129
- validatorExecutionService: executionService,
130
- validatorConfigPort: configPort,
131
- contractMapper,
132
- });
133
-
134
123
  const runL2ValidatorsUseCase = new RunL2ValidatorsUseCase({
135
124
  validatorRegistry: registry,
136
125
  validatorExecutionService: executionService,
@@ -220,7 +209,6 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
220
209
  const handlers = {
221
210
  runValidators: new RunValidatorsHandler({
222
211
  runFullValidationUseCase,
223
- runL0ValidatorsUseCase,
224
212
  runL1ValidatorsUseCase,
225
213
  defaultFailOnWarning,
226
214
  }),
@@ -230,7 +218,6 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
230
218
 
231
219
  return {
232
220
  registry,
233
- runL0ValidatorsUseCase,
234
221
  runL1ValidatorsUseCase,
235
222
  runL2ValidatorsUseCase,
236
223
  runL3ValidatorsUseCase,
@@ -8,6 +8,7 @@
8
8
  export interface DesignDocumentConcept {
9
9
  readonly name: string;
10
10
  readonly type: 'class' | 'interface' | 'type' | 'value-object' | 'service';
11
+ readonly pointers?: readonly string[];
11
12
  }
12
13
 
13
14
  export interface StructuredDesignDoc {
@@ -24,4 +25,6 @@ export interface DesignDocumentPort {
24
25
  getLayerAnnotations?(targetDocs?: readonly string[]): Promise<Record<string, string>>;
25
26
  /** DriftDetectionService用: 要素名一覧取得 */
26
27
  getElements?(targetUnits?: readonly string[]): Promise<string[]>;
28
+ /** DriftDetectionService用: 設計要素から対応する実装ファイルへの明示ポインタ */
29
+ getElementPointers?(targetUnits?: readonly string[]): Promise<Record<string, readonly string[]>>;
27
30
  }
@@ -11,4 +11,5 @@ export interface SourceAnalysisResult {
11
11
  export interface SourceCodeAnalyzerPort {
12
12
  analyzeExports(targetUnits?: readonly string[]): Promise<readonly SourceAnalysisResult[]>;
13
13
  getElements?(targetUnits?: readonly string[]): Promise<string[]>;
14
+ getElementFilePathMap?(targetUnits?: readonly string[]): Promise<Record<string, readonly string[]>>;
14
15
  }
@@ -7,5 +7,5 @@
7
7
  import type { LayerConfig } from '../value-objects/layer-config.js';
8
8
 
9
9
  export interface ValidatorConfigPort {
10
- getLayerConfig(layer: 'L0' | 'L2' | 'L3' | 'L4'): Promise<LayerConfig>;
10
+ getLayerConfig(layer: 'L2' | 'L3' | 'L4'): Promise<LayerConfig>;
11
11
  }
@@ -9,6 +9,11 @@ import { DriftReport } from '../../value-objects/drift-report.js';
9
9
 
10
10
  export interface DriftDetectionDesignDocumentPort {
11
11
  getElements(targetUnits?: readonly string[]): Promise<string[]>;
12
+ /**
13
+ * WI-095: 設計要素 → 実装ファイル path の明示対応。
14
+ * 実装されていれば element 名完全一致に加えて drift 判定に使う。
15
+ */
16
+ getElementPointers?(targetUnits?: readonly string[]): Promise<Record<string, readonly string[]>>;
12
17
  /**
13
18
  * ISSUE-005 P3-9: element → unit 名のマップ。
14
19
  * 実装されていれば DriftReport.unitName の解決に使われる (fallback: 'unknown')。
@@ -18,6 +23,11 @@ export interface DriftDetectionDesignDocumentPort {
18
23
 
19
24
  export interface DriftDetectionSourceCodeAnalyzerPort {
20
25
  getElements(targetUnits?: readonly string[]): Promise<string[]>;
26
+ /**
27
+ * WI-095: export element → 定義ファイル path のマップ。
28
+ * 実装されていれば design pointer と照合する。
29
+ */
30
+ getElementFilePathMap?(targetUnits?: readonly string[]): Promise<Record<string, readonly string[]>>;
21
31
  /**
22
32
  * ISSUE-005 P3-9: element → unit 名のマップ。
23
33
  * 実装されていれば DriftReport.unitName の解決に使われる (fallback: 'unknown')。
@@ -42,6 +52,12 @@ export class DriftDetectionService {
42
52
  async detect(targetUnits?: readonly string[]): Promise<readonly DriftReport[]> {
43
53
  const designElements = await this.designDocumentPort.getElements(targetUnits);
44
54
  const codeElements = await this.sourceCodeAnalyzerPort.getElements(targetUnits);
55
+ const designPointers = this.designDocumentPort.getElementPointers
56
+ ? await this.designDocumentPort.getElementPointers(targetUnits)
57
+ : {};
58
+ const codeFilePathMap = this.sourceCodeAnalyzerPort.getElementFilePathMap
59
+ ? await this.sourceCodeAnalyzerPort.getElementFilePathMap(targetUnits)
60
+ : {};
45
61
 
46
62
  // ISSUE-005 P3-9: element → unit のマップを取得し、DriftReport.unitName の解決に使う
47
63
  const designUnitMap = this.designDocumentPort.getElementUnitMap
@@ -62,12 +78,28 @@ export class DriftDetectionService {
62
78
 
63
79
  const designSet = new Set(designElements);
64
80
  const codeSet = new Set(codeElements);
81
+ const pointerMatchedDesignElements = new Set<string>();
82
+ const pointerMatchedCodeElements = new Set<string>();
83
+
84
+ for (const element of designElements) {
85
+ const pointers = designPointers[element] ?? [];
86
+ if (pointers.length === 0) continue;
87
+ const matchedCodeElements = codeElements.filter((codeElement) =>
88
+ (codeFilePathMap[codeElement] ?? []).some((filePath) => pointers.some((pointer) => isSameOrNestedPath(filePath, pointer)))
89
+ );
90
+ if (matchedCodeElements.length > 0) {
91
+ pointerMatchedDesignElements.add(element);
92
+ for (const codeElement of matchedCodeElements) {
93
+ pointerMatchedCodeElements.add(codeElement);
94
+ }
95
+ }
96
+ }
65
97
 
66
98
  const reports: DriftReport[] = [];
67
99
 
68
100
  // 設計に存在するがコードに存在しない
69
101
  for (const element of designElements) {
70
- if (!codeSet.has(element)) {
102
+ if (!codeSet.has(element) && !pointerMatchedDesignElements.has(element)) {
71
103
  reports.push(
72
104
  DriftReport.create({
73
105
  direction: 'design→code',
@@ -82,7 +114,7 @@ export class DriftDetectionService {
82
114
 
83
115
  // コードに存在するが設計に存在しない
84
116
  for (const element of codeElements) {
85
- if (!designSet.has(element)) {
117
+ if (!designSet.has(element) && !pointerMatchedCodeElements.has(element)) {
86
118
  reports.push(
87
119
  DriftReport.create({
88
120
  direction: 'code→design',
@@ -100,3 +132,13 @@ export class DriftDetectionService {
100
132
  );
101
133
  }
102
134
  }
135
+
136
+ function normalizePath(path: string): string {
137
+ return path.replace(/\\/g, '/').replace(/^\.\//, '');
138
+ }
139
+
140
+ function isSameOrNestedPath(actualPath: string, pointerPath: string): boolean {
141
+ const actual = normalizePath(actualPath);
142
+ const pointer = normalizePath(pointerPath);
143
+ return actual === pointer || actual.endsWith(`/${pointer}`);
144
+ }
@@ -19,7 +19,7 @@ export class ValidatorExecutionError extends Error {
19
19
 
20
20
  export interface ValidatorExecutionServiceDeps {
21
21
  configPort?: {
22
- getLayerConfig(layer: 'L0' | 'L2' | 'L3' | 'L4'): LayerConfig | Promise<LayerConfig>;
22
+ getLayerConfig(layer: 'L2' | 'L3' | 'L4'): LayerConfig | Promise<LayerConfig>;
23
23
  };
24
24
  policyPort?: PhaseGatePolicyPort | { check: (...args: unknown[]) => unknown };
25
25
  [key: string]: unknown;
@@ -132,7 +132,7 @@ export class ValidatorExecutionService {
132
132
  });
133
133
  }
134
134
 
135
- private _getLayerConfigSync(layer: 'L0' | 'L2' | 'L3' | 'L4'): LayerConfig | undefined {
135
+ private _getLayerConfigSync(layer: 'L2' | 'L3' | 'L4'): LayerConfig | undefined {
136
136
  const port = this.deps.configPort;
137
137
  if (!port) return undefined;
138
138
  const result = port.getLayerConfig(layer);
@@ -3,7 +3,7 @@
3
3
  * @unit validator-system
4
4
  *
5
5
  * ValidatorRegistry ドメインサービス
6
- * 全10バリデータ定義のカタログ管理と選択実行インターフェース
6
+ * L2-L4バリデータ定義のカタログ管理と選択実行インターフェース
7
7
  */
8
8
  import { ValidatorId } from '../value-objects/validator-id.js';
9
9
  import { ValidatorDefinition } from '../value-objects/validator-definition.js';
@@ -48,7 +48,7 @@ export class ValidatorRegistry {
48
48
  return this.definitionList;
49
49
  }
50
50
 
51
- listByLayer(layer: 'L0' | 'L2' | 'L3' | 'L4'): readonly ValidatorDefinition[] {
51
+ listByLayer(layer: 'L2' | 'L3' | 'L4'): readonly ValidatorDefinition[] {
52
52
  return this.definitionList.filter((def) => def.layer === layer);
53
53
  }
54
54
 
@@ -8,7 +8,7 @@
8
8
  import { ValidatorId } from './validator-id.js';
9
9
 
10
10
  export interface LayerConfigProps {
11
- readonly layer: 'L0' | 'L2' | 'L3' | 'L4';
11
+ readonly layer: 'L2' | 'L3' | 'L4';
12
12
  readonly enabled: boolean;
13
13
  readonly validatorIds: readonly string[];
14
14
  readonly thresholds: Record<string, number>;
@@ -17,7 +17,7 @@ export interface LayerConfigProps {
17
17
  }
18
18
 
19
19
  export class LayerConfig {
20
- readonly layer: 'L0' | 'L2' | 'L3' | 'L4';
20
+ readonly layer: 'L2' | 'L3' | 'L4';
21
21
  readonly enabled: boolean;
22
22
  readonly validatorIds: readonly string[];
23
23
  readonly thresholds: Readonly<Record<string, number>>;
@@ -10,17 +10,16 @@ import { ValidationRule } from './validation-rule.js';
10
10
 
11
11
  export interface ValidatorDefinitionProps {
12
12
  readonly validatorId: ValidatorId;
13
- readonly layer: 'L0' | 'L2' | 'L3' | 'L4';
13
+ readonly layer: 'L2' | 'L3' | 'L4';
14
14
  readonly name?: string;
15
15
  readonly description?: string;
16
16
  readonly rules: readonly ValidationRule[];
17
17
  readonly enabledCondition: 'always' | 'layerEnabled' | 'strictOnly';
18
18
  readonly externalPolicyRef: string | null;
19
- readonly executionPhase?: 'fuse-hook' | 'pre-commit' | 'ci' | 'scheduled';
19
+ readonly executionPhase?: 'pre-commit' | 'ci' | 'scheduled';
20
20
  }
21
21
 
22
- const LAYER_TO_PHASE: Record<'L0' | 'L2' | 'L3' | 'L4', 'fuse-hook' | 'fuse-hook' | 'pre-commit' | 'ci' | 'scheduled'> = {
23
- L0: 'fuse-hook',
22
+ const LAYER_TO_PHASE: Record<'L2' | 'L3' | 'L4', 'pre-commit' | 'ci' | 'scheduled'> = {
24
23
  L2: 'pre-commit',
25
24
  L3: 'ci',
26
25
  L4: 'scheduled',
@@ -28,13 +27,13 @@ const LAYER_TO_PHASE: Record<'L0' | 'L2' | 'L3' | 'L4', 'fuse-hook' | 'fuse-hook
28
27
 
29
28
  export class ValidatorDefinition {
30
29
  readonly validatorId: ValidatorId;
31
- readonly layer: 'L0' | 'L2' | 'L3' | 'L4';
30
+ readonly layer: 'L2' | 'L3' | 'L4';
32
31
  readonly name: string;
33
32
  readonly description: string;
34
33
  readonly rules: readonly ValidationRule[];
35
34
  readonly enabledCondition: 'always' | 'layerEnabled' | 'strictOnly';
36
35
  readonly externalPolicyRef: string | null;
37
- readonly executionPhase: 'fuse-hook' | 'pre-commit' | 'ci' | 'scheduled';
36
+ readonly executionPhase: 'pre-commit' | 'ci' | 'scheduled';
38
37
 
39
38
  private constructor(props: ValidatorDefinitionProps) {
40
39
  this.validatorId = props.validatorId;
@@ -20,8 +20,6 @@ const VALIDATOR_ID_PATTERN = /^L[0-4]-\d{3}$/;
20
20
 
21
21
  /** バリデータID -> バリデータ名のマップ */
22
22
  const VALIDATOR_NAME_MAP: Record<string, string> = {
23
- 'L0-001': 'fuse-hook-config',
24
- 'L0-002': 'fuse-mount-status',
25
23
  'L1-017': 'it-test-mock-detection',
26
24
  'L1-018': 'stub-comment-detection',
27
25
  'L2-001': 'phase-gate',
@@ -61,6 +61,17 @@ export class BiomeAstSourceCodeAnalyzerAdapter implements SourceCodeAnalyzerPort
61
61
  }
62
62
  return map;
63
63
  }
64
+
65
+ async getElementFilePathMap(targetUnits?: readonly string[]): Promise<Record<string, readonly string[]>> {
66
+ const results = await this.analyzeExports(targetUnits);
67
+ const map: Record<string, string[]> = {};
68
+ for (const result of results) {
69
+ for (const entry of result.exports) {
70
+ map[entry.name] = [...(map[entry.name] ?? []), result.filePath];
71
+ }
72
+ }
73
+ return map;
74
+ }
64
75
  }
65
76
 
66
77
  type ExportType = SourceAnalysisResult['exports'][number]['type'];
@@ -9,7 +9,6 @@ import { LayerConfig } from '../../domain/value-objects/layer-config.js';
9
9
  import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
10
10
 
11
11
  export interface HarnessConfigLayers {
12
- L0?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string };
13
12
  L2?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string };
14
13
  L3?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string; coverageThreshold?: number; bundleSizeLimit?: number };
15
14
  L4?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string; deadCodeGC?: boolean };
@@ -32,13 +31,12 @@ export class HarnessConfigValidatorConfigAdapter implements ValidatorConfigPort
32
31
  this.config = config;
33
32
  }
34
33
 
35
- async getLayerConfig(layer: 'L0' | 'L2' | 'L3' | 'L4'): Promise<LayerConfig> {
34
+ async getLayerConfig(layer: 'L2' | 'L3' | 'L4'): Promise<LayerConfig> {
36
35
  const preset = (this.config.project?.preset ?? this.config.preset ?? 'standard') as 'minimal' | 'standard' | 'strict';
37
36
  const strictOnly = preset === 'strict';
38
37
  const layerData = this.config.layers?.[layer] ?? {};
39
38
 
40
39
  const defaultValidators: Record<string, string[]> = {
41
- L0: ['L0-001', 'L0-002'],
42
40
  L2: ['L2-001', 'L2-002', 'L2-003'],
43
41
  L3: ['L3-001', 'L3-002', 'L3-003', 'L3-004'],
44
42
  L4: ['L4-001', 'L4-002', 'L4-003'],
@@ -30,9 +30,39 @@ function isMetaHeading(name: string): boolean {
30
30
  return DEFAULT_META_HEADING_PATTERNS.some((p) => p.test(name));
31
31
  }
32
32
 
33
- function extractConceptNames(markdown: string): string[] {
33
+ function extractPointerLines(lines: readonly string[], startIndex: number, headingRegex: RegExp): string[] {
34
+ const pointers: string[] = [];
35
+ for (let i = startIndex + 1; i < lines.length; i++) {
36
+ const line = lines[i];
37
+ if (headingRegex.test(line)) break;
38
+
39
+ const commentMatch = /<!--\s*pointers?\s*:\s*(.+?)\s*-->/i.exec(line);
40
+ if (commentMatch) {
41
+ pointers.push(...commentMatch[1].split(',').map((p) => p.trim()).filter(Boolean));
42
+ continue;
43
+ }
44
+
45
+ const blockStartMatch = /^<pointers>\s*$/i.exec(line.trim());
46
+ if (blockStartMatch) {
47
+ for (let j = i + 1; j < lines.length; j++) {
48
+ const blockLine = lines[j].trim();
49
+ if (/^<\/pointers>\s*$/i.test(blockLine)) {
50
+ i = j;
51
+ break;
52
+ }
53
+ const itemMatch = /^-\s+(.+?)\s*$/.exec(blockLine);
54
+ if (itemMatch) pointers.push(itemMatch[1].trim());
55
+ }
56
+ continue;
57
+ }
58
+ }
59
+
60
+ return Array.from(new Set(pointers));
61
+ }
62
+
63
+ function extractConcepts(markdown: string): Array<{ name: string; pointers?: readonly string[] }> {
34
64
  const lines = markdown.split(/\r?\n/);
35
- const headings: string[] = [];
65
+ const concepts: Array<{ name: string; pointers?: readonly string[] }> = [];
36
66
  const headingRegex = /^(#{2,3})\s+(.+?)\s*$/;
37
67
  for (let i = 0; i < lines.length; i++) {
38
68
  const m = headingRegex.exec(lines[i]);
@@ -59,10 +89,11 @@ function extractConceptNames(markdown: string): string[] {
59
89
  .replace(/[((][^))]*[))]/g, '')
60
90
  .trim();
61
91
  if (stripped.length > 0) {
62
- headings.push(stripped);
92
+ const pointers = extractPointerLines(lines, i, headingRegex);
93
+ concepts.push(pointers.length > 0 ? { name: stripped, pointers } : { name: stripped });
63
94
  }
64
95
  }
65
- return headings;
96
+ return concepts;
66
97
  }
67
98
 
68
99
  export class MarkdownDesignDocumentAdapter implements DesignDocumentPort {
@@ -92,7 +123,7 @@ export class MarkdownDesignDocumentAdapter implements DesignDocumentPort {
92
123
  const doc: StructuredDesignDoc = {
93
124
  unitName,
94
125
  docPath,
95
- concepts: extractConceptNames(markdown).map((name) => ({ name, type: 'class' })),
126
+ concepts: extractConcepts(markdown).map((concept) => ({ ...concept, type: 'class' })),
96
127
  layerDependencies: [],
97
128
  adrRefs: Array.from(new Set(markdown.match(ADR_PATTERN) ?? [])),
98
129
  };
@@ -115,6 +146,19 @@ export class MarkdownDesignDocumentAdapter implements DesignDocumentPort {
115
146
  return docs.flatMap((doc) => doc.concepts.map((concept) => concept.name));
116
147
  }
117
148
 
149
+ async getElementPointers(targetUnits?: readonly string[]): Promise<Record<string, readonly string[]>> {
150
+ const docs = await this.loadDesignDocuments(targetUnits);
151
+ const map: Record<string, readonly string[]> = {};
152
+ for (const doc of docs) {
153
+ for (const concept of doc.concepts) {
154
+ if (concept.pointers && concept.pointers.length > 0) {
155
+ map[concept.name] = concept.pointers;
156
+ }
157
+ }
158
+ }
159
+ return map;
160
+ }
161
+
118
162
  /**
119
163
  * ISSUE-005 P3-9: element 名から unit 名を引けるマップを返す。
120
164
  * 同名 element が複数 unit に存在する場合は最初に見つかった unit を採用する。
@@ -5,7 +5,6 @@
5
5
  * RunValidatorsHandler — バリデータ実行 CLIハンドラー
6
6
  */
7
7
  import type { RunFullValidationUseCase } from '../../application/use-cases/run-full-validation-usecase.js';
8
- import type { RunL0ValidatorsUseCase } from '../../application/use-cases/run-l0-validators-usecase.js';
9
8
  import type { RunL1ValidatorsUseCase } from '../../application/use-cases/run-l1-validators-usecase.js';
10
9
  import type { AggregatedValidationReport } from '../../application/dto/aggregated-validation-report.js';
11
10
  import { HumanValidationResultFormatter } from '../formatters/human-validation-result-formatter.js';
@@ -25,7 +24,6 @@ export interface RunValidatorsHandlerArgs {
25
24
 
26
25
  export interface RunValidatorsHandlerDeps {
27
26
  runFullValidationUseCase: RunFullValidationUseCase;
28
- runL0ValidatorsUseCase?: RunL0ValidatorsUseCase;
29
27
  runL1ValidatorsUseCase?: RunL1ValidatorsUseCase;
30
28
  /** WI-094 / ADR-017: config 由来の failOnWarning デフォルト。args.failOnWarning が undefined の場合に適用 */
31
29
  defaultFailOnWarning?: boolean;
@@ -33,13 +31,11 @@ export interface RunValidatorsHandlerDeps {
33
31
 
34
32
  export class RunValidatorsHandler {
35
33
  private readonly useCase: RunFullValidationUseCase;
36
- private readonly l0UseCase: RunL0ValidatorsUseCase | undefined;
37
34
  private readonly l1UseCase: RunL1ValidatorsUseCase | undefined;
38
35
  private readonly defaultFailOnWarning: boolean;
39
36
 
40
37
  constructor(deps: RunValidatorsHandlerDeps) {
41
38
  this.useCase = deps.runFullValidationUseCase;
42
- this.l0UseCase = deps.runL0ValidatorsUseCase;
43
39
  this.l1UseCase = deps.runL1ValidatorsUseCase;
44
40
  this.defaultFailOnWarning = deps.defaultFailOnWarning ?? false;
45
41
  }
@@ -47,37 +43,12 @@ export class RunValidatorsHandler {
47
43
  async execute(args: RunValidatorsHandlerArgs): Promise<{ output: string; exitCode: number }> {
48
44
  try {
49
45
  if (args.layer === 'L0') {
50
- const l0UseCase = this.l0UseCase;
51
- if (!l0UseCase) {
52
- return { output: 'L0 validators not configured', exitCode: 1 };
53
- }
54
- const l0Results = await l0UseCase.execute({
55
- validatorIds: args.validatorIds,
56
- });
57
- const l0Report: AggregatedValidationReport = {
58
- overallPassed: l0Results.every((r) => r.passed || r.skipped),
59
- totalValidators: l0Results.length,
60
- passedValidators: l0Results.filter((r) => r.passed).length,
61
- failedValidators: l0Results.filter((r) => !r.passed && !r.skipped).length,
62
- skippedValidators: l0Results.filter((r) => r.skipped).length,
63
- allErrors: l0Results.flatMap((r) => r.errors),
64
- summary: {
65
- totalErrors: l0Results.flatMap((r) => r.errors).filter((e) => e.severity === 'error').length,
66
- totalWarnings: l0Results.flatMap((r) => r.errors).filter((e) => e.severity === 'warning').length,
67
- errorsByLayer: { L2: 0, L3: 0, L4: 0 },
68
- },
69
- results: l0Results,
70
- };
71
- const format = args.format ?? 'human';
72
- let output: string;
73
- if (format === 'agent') {
74
- output = new AgentValidationResultFormatter().format(l0Report);
75
- } else if (format === 'ci') {
76
- output = new CiValidationResultFormatter().format(l0Report);
77
- } else {
78
- output = new HumanValidationResultFormatter().format(l0Report);
79
- }
80
- return { output, exitCode: l0Report.overallPassed ? 0 : 1 };
46
+ const output = [
47
+ 'L0 validator execution has been retired.',
48
+ 'Runtime L0 enforcement is provided by agent-integration hooks and Husky git hooks.',
49
+ 'Use the configured PreToolUse/PostToolUse/Stop/SessionStart/UserPromptSubmit hooks and .husky/pre-commit / .husky/commit-msg instead of validate --layer L0.',
50
+ ].join('\n');
51
+ return { output, exitCode: 0 };
81
52
  }
82
53
 
83
54
  if (args.layer === 'L1') {
@@ -1,9 +0,0 @@
1
- /**
2
- * @layer application
3
- * @unit validator-system
4
- *
5
- * RunL0ValidatorsInput — L0バリデータ実行 UseCase入力DTO
6
- */
7
- export interface RunL0ValidatorsInput {
8
- readonly validatorIds?: readonly string[];
9
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * @layer application
3
- * @unit validator-system
4
- *
5
- * RunL0ValidatorsUseCase — L0 バリデータ実行
6
- */
7
- import { ValidatorId } from '../../domain/value-objects/validator-id.js';
8
- import { ValidationResult } from '../../domain/value-objects/validation-result.js';
9
- import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
10
- import { ValidatorExecutionService } from '../../domain/services/validator-execution-service.js';
11
- import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
12
- import type { ValidationResultContract } from '../dto/validation-result-contract.js';
13
- import type { RunL0ValidatorsInput } from '../dto/run-l0-validators-input.js';
14
- import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
15
-
16
- export interface RunL0ValidatorsUseCaseDeps {
17
- validatorRegistry: ValidatorRegistry;
18
- validatorExecutionService: ValidatorExecutionService;
19
- validatorConfigPort: ValidatorConfigPort;
20
- contractMapper: ValidationResultContractMapper;
21
- }
22
-
23
- export class RunL0ValidatorsUseCase {
24
- private readonly registry: ValidatorRegistry;
25
- private readonly executionService: ValidatorExecutionService;
26
- private readonly configPort: ValidatorConfigPort;
27
- private readonly mapper: ValidationResultContractMapper;
28
-
29
- constructor(deps: RunL0ValidatorsUseCaseDeps) {
30
- this.registry = deps.validatorRegistry;
31
- this.executionService = deps.validatorExecutionService;
32
- this.configPort = deps.validatorConfigPort;
33
- this.mapper = deps.contractMapper;
34
- }
35
-
36
- async execute(input: RunL0ValidatorsInput): Promise<readonly ValidationResultContract[]> {
37
- let validatorIds: readonly ValidatorId[];
38
- if (input.validatorIds && input.validatorIds.length > 0) {
39
- validatorIds = input.validatorIds.map((id) => ValidatorId.create(id));
40
- } else {
41
- validatorIds = this.registry.listByLayer('L0').map((d) => d.validatorId);
42
- }
43
-
44
- const definitions = this.registry.select(validatorIds);
45
-
46
- const layerConfig = await this.configPort.getLayerConfig('L0');
47
- if (!layerConfig.enabled) {
48
- return [];
49
- }
50
-
51
- const results = this.executionService.execute(definitions, [layerConfig]);
52
- const finalResults = definitions.map(
53
- (definition) => {
54
- const result = results.find((r) => r.validatorId.value === definition.validatorId.value);
55
- return result ?? ValidationResult.skip(definition.validatorId);
56
- },
57
- );
58
- return this.mapper.toContracts(finalResults);
59
- }
60
- }