phasegate 0.16.0 → 0.31.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 (215) hide show
  1. package/README.ja.md +71 -39
  2. package/README.md +69 -9
  3. package/docs/ADR/ADR-013-story-reflection-gate.md +100 -0
  4. package/docs/guide/configuration.md +244 -7
  5. package/package.json +4 -2
  6. package/scripts/harness/adr-foundation/domain/ports/adr-document-parser-port.ts +1 -0
  7. package/scripts/harness/adr-foundation/domain/ports/adr-frontmatter-parser-port.ts +1 -0
  8. package/scripts/harness/adr-foundation/domain/ports/adr-repository-port.ts +1 -0
  9. package/scripts/harness/adr-foundation/domain/value-objects/adr-status.ts +6 -6
  10. package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +3 -1
  11. package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +108 -2
  12. package/scripts/harness/agent-integration/domain/ports/config-query-port.ts +2 -0
  13. package/scripts/harness/agent-integration/domain/ports/phase-gate-query-port.ts +1 -1
  14. package/scripts/harness/agent-integration/domain/ports/story-reflection-query-port.ts +8 -0
  15. package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +258 -0
  16. package/scripts/harness/agent-integration/domain/services/hook-to-cli-translator.ts +21 -4
  17. package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +20 -0
  18. package/scripts/harness/agent-integration/domain/value-objects/story-reflection-query-result.ts +63 -0
  19. package/scripts/harness/agent-integration/infrastructure/adapters/file-system-story-reflection-query-adapter.ts +122 -0
  20. package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +20 -0
  21. package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +17 -3
  22. package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +29 -2
  23. package/scripts/harness/biome-ast-engine/domain/services/lint-runner.ts +24 -0
  24. package/scripts/harness/biome-ast-engine/domain/services/rule-definition-registry.ts +1 -1
  25. package/scripts/harness/biome-ast-engine/domain/value-objects/source-module-snapshot.ts +5 -2
  26. package/scripts/harness/biome-ast-engine/infrastructure/adapters/node-workspace-file-adapter.ts +1 -1
  27. package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +1 -1
  28. package/scripts/harness/biome-ast-engine/infrastructure/parsers/unit-comment-parser.ts +12 -7
  29. package/scripts/harness/ci-governance/composition-root.ts +1 -0
  30. package/scripts/harness/config-foundation/application/mappers/phase-config-section-mapper.ts +44 -0
  31. package/scripts/harness/config-foundation/application/usecases/load-resolved-config-use-case.ts +20 -1
  32. package/scripts/harness/config-foundation/domain/harness-config.ts +2 -1
  33. package/scripts/harness/config-foundation/domain/value-objects/phase-dependencies-config.ts +18 -4
  34. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +185 -10
  35. package/scripts/harness/config-foundation/infrastructure/validators/ajv-config-schema-validator.ts +2 -1
  36. package/scripts/harness/harness-api/application/dto/command-dispatch-input.ts +1 -0
  37. package/scripts/harness/harness-api/application/dto/command-dispatch-output.ts +1 -0
  38. package/scripts/harness/harness-api/application/dto/exit-code-decision-input.ts +1 -0
  39. package/scripts/harness/harness-api/application/dto/exit-code-decision-output.ts +1 -0
  40. package/scripts/harness/harness-api/application/dto/harness-api-response-contract.ts +1 -0
  41. package/scripts/harness/harness-api/application/dto/initialize-registry-input.ts +1 -0
  42. package/scripts/harness/harness-api/application/dto/registry-summary-output.ts +1 -0
  43. package/scripts/harness/harness-api/application/dto/status-derivation-input.ts +1 -0
  44. package/scripts/harness/harness-api/application/mappers/harness-api-response-mapper.ts +1 -0
  45. package/scripts/harness/harness-api/application/usecases/decide-exit-code-usecase.ts +1 -0
  46. package/scripts/harness/harness-api/application/usecases/derive-harness-status-usecase.ts +1 -0
  47. package/scripts/harness/harness-api/application/usecases/dispatch-command-usecase.ts +1 -0
  48. package/scripts/harness/harness-api/application/usecases/initialize-command-registry-usecase.ts +1 -0
  49. package/scripts/harness/harness-api/composition-root.ts +1 -0
  50. package/scripts/harness/harness-api/domain/ports/artifact-scanner-port.ts +1 -0
  51. package/scripts/harness/harness-api/domain/ports/biome-lint-port.ts +1 -0
  52. package/scripts/harness/harness-api/domain/ports/config-query-port.ts +1 -0
  53. package/scripts/harness/harness-api/domain/ports/impact-analysis-port.ts +1 -0
  54. package/scripts/harness/harness-api/domain/ports/phase-gate-query-port.ts +1 -0
  55. package/scripts/harness/harness-api/domain/ports/validator-execution-port.ts +1 -0
  56. package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +1 -0
  57. package/scripts/harness/harness-api/domain/services/command-registry.ts +1 -0
  58. package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +1 -0
  59. package/scripts/harness/harness-api/domain/value-objects/artifact-scan-result.ts +1 -0
  60. package/scripts/harness/harness-api/domain/value-objects/check-ready-result.ts +1 -0
  61. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +1 -0
  62. package/scripts/harness/harness-api/domain/value-objects/cli-command-definition.ts +1 -0
  63. package/scripts/harness/harness-api/domain/value-objects/command-input-spec.ts +1 -0
  64. package/scripts/harness/harness-api/domain/value-objects/drift-report-summary.ts +1 -0
  65. package/scripts/harness/harness-api/domain/value-objects/exit-code-spec.ts +1 -0
  66. package/scripts/harness/harness-api/domain/value-objects/harness-api-response.ts +1 -0
  67. package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +1 -0
  68. package/scripts/harness/harness-api/domain/value-objects/layer-health.ts +1 -0
  69. package/scripts/harness/harness-api/domain/value-objects/phase-info.ts +1 -0
  70. package/scripts/harness/harness-api/index.ts +1 -0
  71. package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +1 -0
  72. package/scripts/harness/harness-api/infrastructure/adapters/file-system-artifact-scanner-adapter.ts +1 -0
  73. package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
  74. package/scripts/harness/harness-api/infrastructure/adapters/nyquist-validation-impact-analysis-adapter.ts +1 -0
  75. package/scripts/harness/harness-api/infrastructure/adapters/phase-dependency-model-query-adapter.ts +1 -0
  76. package/scripts/harness/harness-api/infrastructure/adapters/validator-system-execution-adapter.ts +1 -0
  77. package/scripts/harness/harness-api/presentation/dto/cli-output-options.ts +1 -0
  78. package/scripts/harness/harness-api/presentation/formatters/harness-api-json-formatter.ts +1 -0
  79. package/scripts/harness/harness-api/presentation/handlers/check-phase-handler.ts +1 -0
  80. package/scripts/harness/harness-api/presentation/handlers/check-ready-handler.ts +1 -0
  81. package/scripts/harness/harness-api/presentation/handlers/ci-check-handler.ts +1 -0
  82. package/scripts/harness/harness-api/presentation/handlers/complete-check-handler.ts +1 -0
  83. package/scripts/harness/harness-api/presentation/handlers/detect-drift-handler.ts +1 -0
  84. package/scripts/harness/harness-api/presentation/handlers/impact-analysis-handler.ts +1 -0
  85. package/scripts/harness/harness-api/presentation/handlers/lint-handler.ts +1 -0
  86. package/scripts/harness/harness-api/presentation/handlers/status-handler.ts +1 -0
  87. package/scripts/harness/harness-error/infrastructure/adapters/legacy-error-reporter-adapter.ts +7 -1
  88. package/scripts/harness/integrations/pre-commit.ts +1 -0
  89. package/scripts/harness/main.ts +154 -34
  90. package/scripts/harness/phase-dependency-model/application/dto/resolve-gate-result-dto.ts +14 -0
  91. package/scripts/harness/phase-dependency-model/application/services/phase-gate-result-mapper.ts +20 -0
  92. package/scripts/harness/phase-dependency-model/application/usecases/check-story-reflection-usecase.ts +42 -0
  93. package/scripts/harness/phase-dependency-model/application/usecases/resolve-gate-usecase.ts +161 -0
  94. package/scripts/harness/phase-dependency-model/composition-root.ts +57 -1
  95. package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-dependencies.ts → full-phase-dependencies.ts} +3 -3
  96. package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-nodes.ts → full-phase-nodes.ts} +1 -1
  97. package/scripts/harness/phase-dependency-model/domain/definitions/full-story-reflection-defaults.ts +28 -0
  98. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-dependencies.ts +32 -0
  99. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-nodes.ts +50 -0
  100. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-story-reflection-defaults.ts +8 -0
  101. package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-dependencies.ts +39 -0
  102. package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-nodes.ts +118 -0
  103. package/scripts/harness/phase-dependency-model/domain/definitions/standard-story-reflection-defaults.ts +23 -0
  104. package/scripts/harness/phase-dependency-model/domain/models/phase-structure.ts +95 -23
  105. package/scripts/harness/phase-dependency-model/domain/ports/glob-matcher-port.ts +8 -0
  106. package/scripts/harness/phase-dependency-model/domain/ports/phase-config-provider-port.ts +2 -0
  107. package/scripts/harness/phase-dependency-model/domain/ports/story-annotation-verifier-port.ts +14 -0
  108. package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +16 -0
  109. package/scripts/harness/phase-dependency-model/domain/services/gate-graph.ts +223 -0
  110. package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +66 -0
  111. package/scripts/harness/phase-dependency-model/domain/values/gate-definition.ts +127 -0
  112. package/scripts/harness/phase-dependency-model/domain/values/gate-name.ts +40 -0
  113. package/scripts/harness/phase-dependency-model/domain/values/gate-story-annotation.ts +39 -0
  114. package/scripts/harness/phase-dependency-model/domain/values/phase-customization-policy.ts +16 -4
  115. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-config.ts +44 -0
  116. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-mapping.ts +91 -0
  117. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-result.ts +55 -0
  118. package/scripts/harness/phase-dependency-model/infrastructure/adapters/picomatch-glob-matcher.ts +17 -0
  119. package/scripts/harness/phase-dependency-model/infrastructure/adapters/story-annotation-verifier-adapter.ts +46 -0
  120. package/scripts/harness/phase-dependency-model/infrastructure/config/custom-gates-config-parser.ts +22 -0
  121. package/scripts/harness/phase-dependency-model/infrastructure/config/harness-config-phase-config-provider.ts +78 -2
  122. package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +94 -0
  123. package/scripts/harness/phase-dependency-model/presentation/cli/check-phase-gate-command-handler.ts +12 -4
  124. package/scripts/harness/phase-dependency-model/presentation/cli/story-reflection-status-presenter.ts +87 -0
  125. package/scripts/harness/regression-suite/application/dto/analyze-migration-input.ts +1 -0
  126. package/scripts/harness/regression-suite/application/dto/analyze-migration-output.ts +1 -0
  127. package/scripts/harness/regression-suite/application/dto/configure-ci-gate-input.ts +1 -0
  128. package/scripts/harness/regression-suite/application/dto/configure-ci-gate-output.ts +1 -0
  129. package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-input.ts +1 -0
  130. package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-output.ts +1 -0
  131. package/scripts/harness/regression-suite/application/dto/run-regression-suite-input.ts +1 -0
  132. package/scripts/harness/regression-suite/application/dto/run-regression-suite-output.ts +1 -0
  133. package/scripts/harness/regression-suite/application/mappers/migration-mapping-mapper.ts +1 -0
  134. package/scripts/harness/regression-suite/application/mappers/test-execution-summary-mapper.ts +1 -0
  135. package/scripts/harness/regression-suite/application/usecases/analyze-v0-migration-usecase.ts +1 -0
  136. package/scripts/harness/regression-suite/application/usecases/configure-ci-gate-usecase.ts +1 -0
  137. package/scripts/harness/regression-suite/application/usecases/migrate-v0-tests-usecase.ts +1 -0
  138. package/scripts/harness/regression-suite/application/usecases/run-agent-independence-guard-usecase.ts +1 -0
  139. package/scripts/harness/regression-suite/application/usecases/run-gng-gate-regression-usecase.ts +1 -0
  140. package/scripts/harness/regression-suite/application/usecases/run-k-requirements-regression-usecase.ts +1 -0
  141. package/scripts/harness/regression-suite/application/usecases/run-k14-k15-regression-usecase.ts +1 -0
  142. package/scripts/harness/regression-suite/composition-root.ts +1 -0
  143. package/scripts/harness/regression-suite/domain/aggregates/v0-test-migration.ts +1 -0
  144. package/scripts/harness/regression-suite/domain/ports/ci-gate-result-writer-port.ts +1 -0
  145. package/scripts/harness/regression-suite/domain/ports/config-query-port.ts +1 -0
  146. package/scripts/harness/regression-suite/domain/ports/import-analyzer-port.ts +1 -0
  147. package/scripts/harness/regression-suite/domain/ports/migration-mapping-repository-port.ts +1 -0
  148. package/scripts/harness/regression-suite/domain/ports/suite-registry-port.ts +1 -0
  149. package/scripts/harness/regression-suite/domain/ports/test-runner-port.ts +1 -0
  150. package/scripts/harness/regression-suite/domain/ports/v0-spec-reader-port.ts +1 -0
  151. package/scripts/harness/regression-suite/domain/services/import-guard-service.ts +1 -0
  152. package/scripts/harness/regression-suite/domain/services/migration-analyzer.ts +1 -0
  153. package/scripts/harness/regression-suite/domain/services/regression-runner.ts +1 -0
  154. package/scripts/harness/regression-suite/domain/value-objects/agent-independence-test.ts +1 -0
  155. package/scripts/harness/regression-suite/domain/value-objects/biome-modification-spec.ts +1 -0
  156. package/scripts/harness/regression-suite/domain/value-objects/ci-gate-config.ts +1 -0
  157. package/scripts/harness/regression-suite/domain/value-objects/coverage-rate.ts +1 -0
  158. package/scripts/harness/regression-suite/domain/value-objects/gng-condition-test.ts +1 -0
  159. package/scripts/harness/regression-suite/domain/value-objects/import-violation.ts +1 -0
  160. package/scripts/harness/regression-suite/domain/value-objects/k-requirement-test.ts +1 -0
  161. package/scripts/harness/regression-suite/domain/value-objects/migration-mapping.ts +1 -0
  162. package/scripts/harness/regression-suite/domain/value-objects/regression-suite-definition.ts +1 -0
  163. package/scripts/harness/regression-suite/domain/value-objects/suite-id.ts +1 -0
  164. package/scripts/harness/regression-suite/domain/value-objects/test-execution-summary.ts +1 -0
  165. package/scripts/harness/regression-suite/domain/value-objects/test-failure-detail.ts +1 -0
  166. package/scripts/harness/regression-suite/domain/value-objects/v0-test-id.ts +1 -0
  167. package/scripts/harness/regression-suite/domain/value-objects/v1-test-path.ts +1 -0
  168. package/scripts/harness/regression-suite/infrastructure/adapters/biome-ast-import-analyzer-adapter.ts +1 -0
  169. package/scripts/harness/regression-suite/infrastructure/adapters/file-system-v0-spec-reader-adapter.ts +1 -0
  170. package/scripts/harness/regression-suite/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
  171. package/scripts/harness/regression-suite/infrastructure/adapters/json-ci-gate-result-writer-adapter.ts +1 -0
  172. package/scripts/harness/regression-suite/infrastructure/adapters/markdown-migration-mapping-repository-adapter.ts +1 -0
  173. package/scripts/harness/regression-suite/infrastructure/adapters/static-suite-registry-adapter.ts +1 -0
  174. package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +1 -0
  175. package/scripts/harness/regression-suite/infrastructure/registry/agent-independence-suite-definition.ts +1 -0
  176. package/scripts/harness/regression-suite/infrastructure/registry/gng-gate-suite-definition.ts +1 -0
  177. package/scripts/harness/regression-suite/infrastructure/registry/k-requirements-suite-definition.ts +1 -0
  178. package/scripts/harness/regression-suite/infrastructure/registry/v0-migration-suite-definition.ts +1 -0
  179. package/scripts/harness/setup/skill-deployer.ts +82 -5
  180. package/scripts/harness/shared-kernel/harness-api.ts +1 -0
  181. package/scripts/harness/skill-quality/composition-root.ts +1 -0
  182. package/scripts/harness/skill-quality/infrastructure/adapters/ajv-lesson-artifact-schema-adapter.ts +5 -4
  183. package/scripts/harness/traceability-model/domain/services/metadata-validator.ts +12 -10
  184. package/scripts/harness/traceability-model/infrastructure/parsers/source-metadata-parser.ts +16 -2
  185. package/skills/cascade-updater/SKILL.md +16 -3
  186. package/skills/codebase-mapper/SKILL.md +9 -9
  187. package/skills/codex-delegator/SKILL.md +5 -56
  188. package/skills/consistency-checker/SKILL.md +5 -9
  189. package/skills/consistency-checker/references//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +6 -3
  190. package/skills/doc-freshness-checker/SKILL.md +6 -6
  191. package/skills/engineering-perspective/SKILL.md +2 -58
  192. package/skills/engineering-perspective/references/domain-model.md +4 -135
  193. package/skills/implementation-readiness-checker/SKILL.md +18 -72
  194. package/skills/it-test-designer/SKILL.md +25 -161
  195. package/skills/it-test-designer/references/it-test-design-template.md +131 -0
  196. package/skills/it-test-logic-designer/SKILL.md +13 -388
  197. package/skills/it-test-logic-designer/references/controller-test-patterns.md +100 -0
  198. package/skills/it-test-logic-designer/references/repository-test-patterns.md +206 -0
  199. package/skills/it-test-logic-designer/references/usecase-test-patterns.md +87 -0
  200. package/skills/pointer-validator/SKILL.md +4 -4
  201. package/skills/scenario-test-designer/SKILL.md +4 -68
  202. package/skills/scenario-test-designer/references/scenario-test-design-template.md +41 -0
  203. package/skills/scenario-test-designer/references/scenario-test-plan-template.md +35 -0
  204. package/skills/scenario-test-logic-designer/SKILL.md +21 -304
  205. package/skills/scenario-test-logic-designer/references/msw-patterns.md +36 -0
  206. package/skills/scenario-test-logic-designer/references/playwright-patterns.md +237 -0
  207. package/skills/test-coverage-checker/SKILL.md +39 -121
  208. package/skills/test-coverage-checker/references/coverage-report-template.md +99 -0
  209. package/skills/uiux-designer/SKILL.md +27 -92
  210. package/skills/uiux-designer/references/data-testid-convention.md +38 -0
  211. package/skills/uiux-designer/references/uiux-design-template.md +105 -0
  212. package/skills/unit-test-designer/SKILL.md +5 -106
  213. package/skills/unit-test-designer/references/unit-test-design-template.md +127 -0
  214. package/skills/unit-test-logic-designer/SKILL.md +28 -250
  215. package/skills/unit-test-logic-designer/references/test-patterns.md +207 -0
@@ -8,10 +8,18 @@
8
8
  */
9
9
 
10
10
  import { dirname, join, resolve } from 'node:path';
11
+ import { readFile as fsReadFile } from 'node:fs/promises';
11
12
  import { createConfigFoundationModule } from './config-foundation/composition-root.js';
13
+ import { toPhaseConfigSection } from './config-foundation/application/mappers/phase-config-section-mapper.js';
12
14
  import { createHarnessErrorModule } from './harness-error/composition-root.js';
13
15
  import { createTraceabilityModelModule } from './traceability-model/composition-root.js';
14
16
  import { createPhaseDependencyModelModule } from './phase-dependency-model/composition-root.js';
17
+ import { HarnessConfigPhaseConfigProvider, type PhaseConfigSection as PhaseDepConfigSection } from './phase-dependency-model/infrastructure/config/harness-config-phase-config-provider.js';
18
+ import { StoryReflectionChecker } from './phase-dependency-model/domain/services/story-reflection-checker.js';
19
+ import { CheckStoryReflectionUseCase } from './phase-dependency-model/application/usecases/check-story-reflection-usecase.js';
20
+ import { FileSystemStoryReflectionAdapter } from './phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.js';
21
+ import { StoryReflectionStatusPresenter } from './phase-dependency-model/presentation/cli/story-reflection-status-presenter.js';
22
+ import { StoryReflectionResult } from './phase-dependency-model/domain/values/story-reflection-result.js';
15
23
  import { createAdrFoundationModule } from './adr-foundation/composition-root.js';
16
24
  import { createBiomeAstEngineModule } from './biome-ast-engine/composition-root.js';
17
25
  import { createValidatorSystemModule } from './validator-system/composition-root.js';
@@ -21,8 +29,10 @@ import { buildCiGovernance } from './ci-governance/composition-root.js';
21
29
  import { createSkillQualityHandlers } from './skill-quality/composition-root.js';
22
30
  import { buildRegressionSuite } from './regression-suite/composition-root.js';
23
31
  import { buildPhase2Extensions } from './phase2-extensions/composition-root.js';
24
- import { deploySkills, deployHookScripts, getDeployedVersion, getHarnessVersion, initHarnessConfig } from './setup/skill-deployer.js';
32
+ import { deploySkills, deployHookScripts, getDeployedVersion, getHarnessVersion, initHarnessConfig, SKILL_CATEGORIES, getCategoryForSkill } from './setup/skill-deployer.js';
33
+ import type { SkillSet } from './setup/skill-deployer.js';
25
34
  import type { HarnessConfigV2 } from './config-foundation/domain/harness-config.js';
35
+ import { ConfigValidationError } from './config-foundation/domain/errors/config-validation-error.js';
26
36
 
27
37
  /**
28
38
  * main.ts (scripts/harness/main.ts) から2階層上がパッケージルート。
@@ -42,7 +52,7 @@ Usage: phasegate <command> [options]
42
52
 
43
53
  Setup:
44
54
  init Initialize project: deploy skills + create phasegate.config.json
45
- (--name <project-name>)
55
+ (--name <project-name>, --preset <full|standard|minimal|custom>)
46
56
  update-skills Re-deploy skills from current harness version
47
57
 
48
58
  Commands:
@@ -199,11 +209,14 @@ function parseCoverageThreshold(raw: string | undefined): number {
199
209
  return n;
200
210
  }
201
211
 
202
- type PhasePreset = 'default' | 'custom';
212
+ type InitPhasePreset = 'full' | 'standard' | 'minimal' | 'custom';
203
213
 
204
- function toPhasePreset(value: string): PhasePreset {
205
- if (value === 'default' || value === 'custom') return value;
206
- return 'default';
214
+ function parseInitPhasePreset(value: string | undefined): InitPhasePreset | undefined {
215
+ if (value === undefined) return undefined;
216
+ if (value === 'full' || value === 'standard' || value === 'minimal' || value === 'custom') {
217
+ return value;
218
+ }
219
+ return undefined;
207
220
  }
208
221
 
209
222
  type RuleSeverity = 'error' | 'warning' | 'off';
@@ -224,32 +237,82 @@ function toRuleSeverityMap(
224
237
  }
225
238
 
226
239
  /**
227
- * HarnessConfigV2 (resolved) から phase-dependency-model が期待する PhaseConfigSection を抽出する。
240
+ * HarnessConfigV2 (resolved) から biome-ast-engine が期待する L1 Config を抽出する。
228
241
  */
229
- function toPhaseConfigSection(resolvedConfig: HarnessConfigV2) {
242
+ function toL1Config(resolvedConfig: HarnessConfigV2) {
230
243
  return {
231
- planningMode: resolvedConfig.planningMode,
232
- customization: {
233
- preset: toPhasePreset(resolvedConfig.phaseDependencies.preset),
234
- overrideEnabled: resolvedConfig.phaseDependencies.override,
235
- rules: resolvedConfig.phaseDependencies.customRules.map((r) => ({
236
- targetPhase: r.phase,
237
- condition: 'requires',
238
- action: r.requires,
239
- })),
240
- },
241
- reportingOutputDir: resolvedConfig.reporting.outputDir,
244
+ enabled: resolvedConfig.layers.L1.enabled,
245
+ rules: toRuleSeverityMap(resolvedConfig.layers.L1.rules),
242
246
  };
243
247
  }
244
248
 
245
249
  /**
246
- * HarnessConfigV2 (resolved) から biome-ast-engine が期待する L1 Config を抽出する。
250
+ * phasegate.config.json を直接読み、storyReflection 設定解決用の provider を返す。
251
+ * config-foundation の HarnessConfigV2 は storyReflection 未サポートのため raw JSON 経由。
247
252
  */
248
- function toL1Config(resolvedConfig: HarnessConfigV2) {
249
- return {
250
- enabled: resolvedConfig.layers.L1.enabled,
251
- rules: toRuleSeverityMap(resolvedConfig.layers.L1.rules),
253
+ async function loadStoryReflectionProvider(
254
+ rootDir: string,
255
+ ): Promise<HarnessConfigPhaseConfigProvider | null> {
256
+ const configPath = join(rootDir, 'phasegate.config.json');
257
+ let raw: {
258
+ phaseDependencies?: {
259
+ preset?: 'default' | 'full' | 'standard' | 'minimal' | 'custom';
260
+ override?: boolean;
261
+ storyReflection?: {
262
+ enabled?: boolean;
263
+ mappings?: ReadonlyArray<{ inception: string; product: string; required: boolean }>;
264
+ };
265
+ };
266
+ reporting?: { outputDir?: string };
267
+ };
268
+ try {
269
+ const content = await fsReadFile(configPath, 'utf8');
270
+ raw = JSON.parse(content);
271
+ } catch {
272
+ return null;
273
+ }
274
+ const section: PhaseDepConfigSection = {
275
+ customization: {
276
+ preset: raw.phaseDependencies?.preset,
277
+ overrideEnabled: raw.phaseDependencies?.override ?? false,
278
+ },
279
+ storyReflection: raw.phaseDependencies?.storyReflection,
280
+ reportingOutputDir: raw.reporting?.outputDir,
252
281
  };
282
+ return new HarnessConfigPhaseConfigProvider({
283
+ config: section,
284
+ defaultOutputDir: raw.reporting?.outputDir ?? '.harness/reports',
285
+ });
286
+ }
287
+
288
+ async function printStoryReflectionValidationSummary(
289
+ rootDir: string,
290
+ unit: string | undefined,
291
+ ): Promise<void> {
292
+ const provider = await loadStoryReflectionProvider(rootDir);
293
+ if (provider === null) return;
294
+ const config = await provider.getStoryReflectionConfig();
295
+ const policy = await provider.getCustomizationPolicy();
296
+ const presenter = new StoryReflectionStatusPresenter();
297
+ let result = StoryReflectionResult.pass();
298
+ if (config.enabled && unit) {
299
+ const fsAdapter = new FileSystemStoryReflectionAdapter({ rootDir });
300
+ const checker = new StoryReflectionChecker(fsAdapter);
301
+ const useCase = new CheckStoryReflectionUseCase({ checker });
302
+ result = await useCase.execute({ unitId: unit, config });
303
+ }
304
+ console.log(
305
+ presenter.formatValidationSummary({ config, preset: policy.preset, result }),
306
+ );
307
+ }
308
+
309
+ async function printStoryReflectionStatusLine(rootDir: string): Promise<void> {
310
+ const provider = await loadStoryReflectionProvider(rootDir);
311
+ if (provider === null) return;
312
+ const config = await provider.getStoryReflectionConfig();
313
+ const policy = await provider.getCustomizationPolicy();
314
+ const presenter = new StoryReflectionStatusPresenter();
315
+ console.log(presenter.formatStatusLine({ config, preset: policy.preset }));
253
316
  }
254
317
 
255
318
  async function loadResolvedConfig(): Promise<HarnessConfigV2 | undefined> {
@@ -257,7 +320,12 @@ async function loadResolvedConfig(): Promise<HarnessConfigV2 | undefined> {
257
320
  const configModule = createConfigFoundationModule();
258
321
  const result = await configModule.usecases.loadResolvedConfigUseCase.execute();
259
322
  return result.config;
260
- } catch {
323
+ } catch (error) {
324
+ if (error instanceof ConfigValidationError) {
325
+ process.stderr.write(`Invalid phasegate.config.json: ${error.message}\n`);
326
+ process.exit(2);
327
+ }
328
+
261
329
  return undefined;
262
330
  }
263
331
  }
@@ -290,10 +358,28 @@ async function main(): Promise<void> {
290
358
  // ── harness setup ──
291
359
  case 'init': {
292
360
  const projectName = parseFlag(args, '--name') ?? 'my-project';
293
- const result = await deploySkills(harnessRoot, rootDir);
294
- const configResult = await initHarnessConfig(rootDir, projectName);
361
+ const rawPhasePreset = parseFlag(args, '--preset');
362
+ if (
363
+ rawPhasePreset !== undefined
364
+ && rawPhasePreset !== 'full'
365
+ && rawPhasePreset !== 'standard'
366
+ && rawPhasePreset !== 'minimal'
367
+ && rawPhasePreset !== 'custom'
368
+ ) {
369
+ console.error(`Invalid --preset value: "${rawPhasePreset}". Use "full", "standard", "minimal", or "custom".`);
370
+ process.exit(2);
371
+ }
372
+ const phasePreset = parseInitPhasePreset(rawPhasePreset);
373
+ const skillSetRaw = parseFlag(args, '--skills') ?? 'all';
374
+ if (skillSetRaw !== 'core' && skillSetRaw !== 'all') {
375
+ console.error(`Invalid --skills value: "${skillSetRaw}". Use "core" or "all".`);
376
+ process.exit(2);
377
+ }
378
+ const skillSet: SkillSet = skillSetRaw;
379
+ const result = await deploySkills(harnessRoot, rootDir, skillSet);
380
+ const configResult = await initHarnessConfig(rootDir, projectName, phasePreset);
295
381
  const hooksResult = await deployHookScripts(harnessRoot, rootDir);
296
- console.log(`✓ Skills deployed to ${result.targetDir} (${result.deployedSkills.length} skills)`);
382
+ console.log(`✓ Skills deployed to ${result.targetDir} (${result.deployedSkills.length} skills, set: ${skillSet})`);
297
383
  if (configResult.created) {
298
384
  console.log(`✓ phasegate.config.json created`);
299
385
  } else {
@@ -310,7 +396,11 @@ async function main(): Promise<void> {
310
396
  console.log(`✓ Harness v${result.version} initialized`);
311
397
  console.log('');
312
398
  console.log('Next steps:');
313
- console.log(' 1. Run the product-architect skill to start AIDLC');
399
+ if (skillSet === 'core') {
400
+ console.log(' 1. Core skills only — quality defense tools are ready');
401
+ } else {
402
+ console.log(' 1. Run the product-architect skill to start AIDLC');
403
+ }
314
404
  console.log(' 2. Customize phasegate.config.json if needed');
315
405
  console.log(' 3. Edit .claude/scripts/hook-config.json to set target directories');
316
406
  process.exit(0);
@@ -320,14 +410,19 @@ async function main(): Promise<void> {
320
410
  case 'update-skills': {
321
411
  const deployed = await getDeployedVersion(rootDir);
322
412
  const current = await getHarnessVersion(harnessRoot);
413
+ const previousSkillSet: SkillSet = deployed?.skillSet ?? 'all';
414
+ const overrideSkillSet = parseFlag(args, '--skills');
415
+ const updateSkillSet: SkillSet = overrideSkillSet === 'core' || overrideSkillSet === 'all'
416
+ ? overrideSkillSet
417
+ : previousSkillSet;
323
418
  if (deployed) {
324
- console.log(`Previously deployed: v${deployed.version} (${deployed.deployedAt})`);
419
+ console.log(`Previously deployed: v${deployed.version} (${deployed.deployedAt}, set: ${previousSkillSet})`);
325
420
  } else {
326
421
  console.log('No previously deployed skills found');
327
422
  }
328
423
  console.log(`Current harness version: v${current}`);
329
- const result = await deploySkills(harnessRoot, rootDir);
330
- console.log(`✓ Skills updated (${result.deployedSkills.length} skills redeployed)`);
424
+ const result = await deploySkills(harnessRoot, rootDir, updateSkillSet);
425
+ console.log(`✓ Skills updated (${result.deployedSkills.length} skills redeployed, set: ${updateSkillSet})`);
331
426
  process.exit(0);
332
427
  break;
333
428
  }
@@ -443,11 +538,13 @@ async function main(): Promise<void> {
443
538
  const level = Number(parseFlag(args, '--level') ?? '1');
444
539
  const unitId = parseFlag(args, '--unit');
445
540
  const storyId = parseFlag(args, '--story');
541
+ const targetFilePath = parseFlag(args, '--target-file');
446
542
  const result =
447
543
  await mod.checkPhaseGateCommandHandler.execute({
448
544
  targetLevel: level,
449
545
  unitId,
450
546
  storyId,
547
+ targetFilePath,
451
548
  json,
452
549
  });
453
550
  console.log(result.text);
@@ -523,6 +620,9 @@ async function main(): Promise<void> {
523
620
  targetPaths,
524
621
  });
525
622
  console.log(result.output);
623
+ if (layer === 'L2' || layer === 'all') {
624
+ await printStoryReflectionValidationSummary(rootDir, unit);
625
+ }
526
626
  process.exit(result.exitCode);
527
627
  break;
528
628
  }
@@ -585,6 +685,7 @@ async function main(): Promise<void> {
585
685
  const flags: Record<string, boolean | string> = {};
586
686
  if (json) flags.json = true;
587
687
  await mod.handlers.status.handle({}, flags);
688
+ await printStoryReflectionStatusLine(rootDir);
588
689
  break;
589
690
  }
590
691
 
@@ -833,9 +934,28 @@ async function main(): Promise<void> {
833
934
  }
834
935
  }
835
936
  skills.sort();
836
- console.log(`Available skills (${skills.length}):\n`);
937
+
938
+ const grouped: Record<string, string[]> = { core: [], aidlc: [], utility: [], unknown: [] };
837
939
  for (const name of skills) {
838
- console.log(` /${name}`);
940
+ const cat = getCategoryForSkill(name) ?? 'unknown';
941
+ grouped[cat].push(name);
942
+ }
943
+
944
+ console.log(`Available skills (${skills.length}):\n`);
945
+
946
+ const labels: Record<string, string> = {
947
+ core: 'Core — Quality Defense',
948
+ aidlc: 'AIDLC — Development Workflow',
949
+ utility: 'Utility',
950
+ };
951
+ for (const cat of ['core', 'aidlc', 'utility', 'unknown'] as const) {
952
+ if (grouped[cat].length === 0) continue;
953
+ const label = labels[cat] ?? 'Other';
954
+ console.log(` [${label}] (${grouped[cat].length})`);
955
+ for (const name of grouped[cat]) {
956
+ console.log(` /${name}`);
957
+ }
958
+ console.log('');
839
959
  }
840
960
  process.exit(0);
841
961
  }
@@ -0,0 +1,14 @@
1
+ // @unit phase-dependency-model
2
+ // @layer application
3
+
4
+ export interface ResolveGateFindingDto {
5
+ readonly gateName: string;
6
+ readonly path: string;
7
+ readonly reason: string;
8
+ }
9
+
10
+ export interface ResolveGateResultDto {
11
+ readonly matchedGates: readonly string[];
12
+ readonly blockers: readonly ResolveGateFindingDto[];
13
+ readonly warnings: readonly ResolveGateFindingDto[];
14
+ }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { PhaseGateResultDto } from '../dto/phase-gate-result-dto.js';
7
+ import type { ResolveGateResultDto } from '../dto/resolve-gate-result-dto.js';
7
8
  import type { PhaseGateResult } from '../../domain/values/phase-gate-result.js';
8
9
 
9
10
  export class PhaseGateResultMapper {
@@ -22,4 +23,23 @@ export class PhaseGateResultMapper {
22
23
  auditRecorded: context.auditRecorded,
23
24
  });
24
25
  }
26
+
27
+ mapFromResolve(
28
+ result: ResolveGateResultDto,
29
+ context: {
30
+ readonly targetLevel: 1 | 2 | 3;
31
+ },
32
+ ): PhaseGateResultDto {
33
+ return Object.freeze({
34
+ passed: result.blockers.length === 0,
35
+ targetLevel: context.targetLevel,
36
+ blockers: Object.freeze(
37
+ result.blockers.map((blocker) => `${blocker.gateName}: ${blocker.reason} (${blocker.path})`),
38
+ ),
39
+ warnings: Object.freeze(
40
+ result.warnings.map((warning) => `${warning.gateName}: ${warning.reason} (${warning.path})`),
41
+ ),
42
+ auditRecorded: false,
43
+ });
44
+ }
25
45
  }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @layer application
3
+ * @unit phase-dependency-model
4
+ *
5
+ * CheckStoryReflectionUseCase
6
+ * StoryReflectionChecker をオーケストレーションし、inception → product への
7
+ * storyId 反映状況を検証するユースケース。
8
+ *
9
+ * 入力: unitId, StoryReflectionConfig
10
+ * 出力: StoryReflectionResult(pass/fail + violations/warnings)
11
+ *
12
+ * enabled=false の場合は即座に pass を返す。
13
+ */
14
+
15
+ import type { StoryReflectionChecker } from '../../domain/services/story-reflection-checker.js';
16
+ import type { StoryReflectionConfig } from '../../domain/values/story-reflection-config.js';
17
+ import { StoryReflectionResult } from '../../domain/values/story-reflection-result.js';
18
+
19
+ export interface CheckStoryReflectionInput {
20
+ readonly unitId: string;
21
+ readonly config: StoryReflectionConfig;
22
+ }
23
+
24
+ export interface CheckStoryReflectionUseCaseDeps {
25
+ readonly checker: StoryReflectionChecker;
26
+ }
27
+
28
+ export class CheckStoryReflectionUseCase {
29
+ private readonly checker: StoryReflectionChecker;
30
+
31
+ constructor(deps: CheckStoryReflectionUseCaseDeps) {
32
+ this.checker = deps.checker;
33
+ }
34
+
35
+ async execute(input: CheckStoryReflectionInput): Promise<StoryReflectionResult> {
36
+ if (!input.config.enabled) {
37
+ return StoryReflectionResult.pass();
38
+ }
39
+
40
+ return this.checker.check(input.unitId, input.config);
41
+ }
42
+ }
@@ -0,0 +1,161 @@
1
+ // @unit phase-dependency-model
2
+ // @layer application
3
+
4
+ import type { ResolveGateResultDto, ResolveGateFindingDto } from '../dto/resolve-gate-result-dto.js';
5
+ import { GateGraph } from '../../domain/services/gate-graph.js';
6
+ import type { ArtifactExistenceCheckerPort } from '../../domain/ports/artifact-existence-checker-port.js';
7
+ import type { GlobMatcherPort } from '../../domain/ports/glob-matcher-port.js';
8
+ import type { StoryAnnotationVerifierPort } from '../../domain/ports/story-annotation-verifier-port.js';
9
+ import { Artifact } from '../../domain/values/artifact.js';
10
+ import type { GateDefinition, GateRequirement } from '../../domain/values/gate-definition.js';
11
+
12
+ export interface ResolveGateInput {
13
+ readonly targetFilePath: string;
14
+ readonly gates: readonly GateDefinition[];
15
+ readonly scope?: {
16
+ readonly unitId?: string;
17
+ readonly storyId?: string;
18
+ };
19
+ }
20
+
21
+ export interface ResolveGateUseCaseDeps {
22
+ readonly globMatcher: GlobMatcherPort;
23
+ readonly artifactExistenceChecker: ArtifactExistenceCheckerPort;
24
+ readonly storyAnnotationVerifier?: StoryAnnotationVerifierPort;
25
+ }
26
+
27
+ interface RequirementEvaluation {
28
+ readonly gateName: string;
29
+ readonly requirement: GateRequirement;
30
+ readonly resolvedPath: string;
31
+ }
32
+
33
+ export class ResolveGateUseCase {
34
+ private readonly globMatcher: GlobMatcherPort;
35
+ private readonly artifactExistenceChecker: ArtifactExistenceCheckerPort;
36
+ private readonly storyAnnotationVerifier?: StoryAnnotationVerifierPort;
37
+
38
+ constructor(deps: ResolveGateUseCaseDeps) {
39
+ this.globMatcher = deps.globMatcher;
40
+ this.artifactExistenceChecker = deps.artifactExistenceChecker;
41
+ this.storyAnnotationVerifier = deps.storyAnnotationVerifier;
42
+ }
43
+
44
+ async execute(input: ResolveGateInput): Promise<ResolveGateResultDto> {
45
+ const graph = GateGraph.build([...input.gates]);
46
+ const matchedGates = input.gates.filter((gate) =>
47
+ gate.blocks.some((pattern) => this.globMatcher.match(pattern, input.targetFilePath)),
48
+ );
49
+
50
+ if (matchedGates.length === 0) {
51
+ return Object.freeze({
52
+ matchedGates: Object.freeze([]),
53
+ blockers: Object.freeze([]),
54
+ warnings: Object.freeze([]),
55
+ });
56
+ }
57
+
58
+ const evaluationOrder = new Map<string, GateDefinition>();
59
+ for (const gate of matchedGates) {
60
+ for (const ancestorName of graph.resolveAncestors(gate.name)) {
61
+ const ancestor = graph.gates.get(ancestorName.value);
62
+ if (ancestor && !evaluationOrder.has(ancestor.name.value)) {
63
+ evaluationOrder.set(ancestor.name.value, ancestor);
64
+ }
65
+ }
66
+
67
+ if (!evaluationOrder.has(gate.name.value)) {
68
+ evaluationOrder.set(gate.name.value, gate);
69
+ }
70
+ }
71
+
72
+ const evaluations = [...evaluationOrder.values()].flatMap((gate) =>
73
+ gate.requires.map((requirement) => ({
74
+ gateName: gate.name.value,
75
+ requirement,
76
+ resolvedPath: this.expandPlaceholders(requirement.path, input.scope),
77
+ })),
78
+ );
79
+ const artifacts = evaluations.map((evaluation, index) =>
80
+ Artifact.create({
81
+ name: `${evaluation.gateName}-${index + 1}`,
82
+ path: evaluation.resolvedPath,
83
+ required: evaluation.requirement.required,
84
+ }),
85
+ );
86
+ const statuses = await this.artifactExistenceChecker.checkAll(artifacts, input.scope ?? {});
87
+
88
+ const blockers: ResolveGateFindingDto[] = [];
89
+ const warnings: ResolveGateFindingDto[] = [];
90
+
91
+ for (const evaluation of evaluations) {
92
+ if (statuses.get(evaluation.resolvedPath) === true) {
93
+ continue;
94
+ }
95
+
96
+ const finding = Object.freeze({
97
+ gateName: evaluation.gateName,
98
+ path: evaluation.resolvedPath,
99
+ reason: evaluation.requirement.required
100
+ ? '必須アーティファクトが不足しています'
101
+ : '推奨アーティファクトが不足しています',
102
+ });
103
+
104
+ if (evaluation.requirement.required) {
105
+ blockers.push(finding);
106
+ } else {
107
+ warnings.push(finding);
108
+ }
109
+ }
110
+
111
+ if (this.storyAnnotationVerifier) {
112
+ for (const gate of matchedGates) {
113
+ if (!gate.storyAnnotation) {
114
+ continue;
115
+ }
116
+
117
+ const verification = await this.storyAnnotationVerifier.verify(
118
+ input.targetFilePath,
119
+ gate.storyAnnotation.tag,
120
+ );
121
+
122
+ if (verification.hasAnnotation) {
123
+ continue;
124
+ }
125
+
126
+ const finding = Object.freeze({
127
+ gateName: gate.name.value,
128
+ path: input.targetFilePath,
129
+ reason: gate.storyAnnotation.required
130
+ ? `${gate.storyAnnotation.tag} 注釈が必要です`
131
+ : `${gate.storyAnnotation.tag} 注釈を推奨します`,
132
+ });
133
+
134
+ if (gate.storyAnnotation.required) {
135
+ blockers.push(finding);
136
+ continue;
137
+ }
138
+
139
+ warnings.push(finding);
140
+ }
141
+ }
142
+
143
+ return Object.freeze({
144
+ matchedGates: Object.freeze(matchedGates.map((gate) => gate.name.value)),
145
+ blockers: Object.freeze(blockers),
146
+ warnings: Object.freeze(warnings),
147
+ });
148
+ }
149
+
150
+ private expandPlaceholders(
151
+ path: string,
152
+ scope?: {
153
+ readonly unitId?: string;
154
+ readonly storyId?: string;
155
+ },
156
+ ): string {
157
+ return path
158
+ .replaceAll('{unit}', scope?.unitId ?? '{unit}')
159
+ .replaceAll('{storyId}', scope?.storyId ?? '{storyId}');
160
+ }
161
+ }
@@ -7,13 +7,19 @@
7
7
  */
8
8
  import { FileSystemArtifactExistenceChecker } from './infrastructure/filesystem/file-system-artifact-existence-checker.js';
9
9
  import { MarkdownPlanDocumentReader } from './infrastructure/filesystem/markdown-plan-document-reader.js';
10
+ import { PicomatchGlobMatcher } from './infrastructure/adapters/picomatch-glob-matcher.js';
11
+ import { StoryAnnotationVerifierAdapter } from './infrastructure/adapters/story-annotation-verifier-adapter.js';
12
+ import { CustomGatesConfigParser } from './infrastructure/config/custom-gates-config-parser.js';
13
+ import { MarkdownDesignDocumentGateway } from '../traceability-model/infrastructure/gateways/markdown-design-document-gateway.js';
10
14
  import {
11
15
  HarnessConfigPhaseConfigProvider,
12
16
  type PhaseConfigSection,
13
17
  } from './infrastructure/config/harness-config-phase-config-provider.js';
14
18
  import { PhaseOverrideAuditLogger } from './infrastructure/logging/phase-override-audit-logger.js';
15
19
  import { EvidenceBundleAssembler } from './application/services/evidence-bundle-assembler.js';
20
+ import { PhaseGateResultMapper } from './application/services/phase-gate-result-mapper.js';
16
21
  import { CheckPhaseGateUseCase } from './application/usecases/check-phase-gate-usecase.js';
22
+ import { ResolveGateUseCase } from './application/usecases/resolve-gate-usecase.js';
17
23
  import { CheckPhaseGateCommandHandler } from './presentation/cli/check-phase-gate-command-handler.js';
18
24
 
19
25
  const DEFAULT_REPORT_OUTPUT_DIR = '.harness/reports';
@@ -35,6 +41,16 @@ export function createPhaseDependencyModelModule(
35
41
  ) {
36
42
  const phaseConfig = (config.phaseConfig as PhaseConfigSection | undefined) ?? defaultPhaseConfig;
37
43
  const reportOutputDir = config.reportOutputDir ?? DEFAULT_REPORT_OUTPUT_DIR;
44
+ const rawCustomGates =
45
+ ((config.phaseConfig as {
46
+ readonly gates?: readonly unknown[];
47
+ readonly customization?: { readonly gates?: readonly unknown[]; readonly preset?: string };
48
+ } | undefined)?.gates) ??
49
+ ((config.phaseConfig as {
50
+ readonly customization?: { readonly gates?: readonly unknown[]; readonly preset?: string };
51
+ } | undefined)?.customization?.gates) ??
52
+ [];
53
+ const isCustomPreset = phaseConfig.customization?.preset === 'custom';
38
54
 
39
55
  // Infrastructure
40
56
  const artifactExistenceChecker = new FileSystemArtifactExistenceChecker({
@@ -47,9 +63,17 @@ export function createPhaseDependencyModelModule(
47
63
  config: phaseConfig,
48
64
  defaultOutputDir: reportOutputDir,
49
65
  });
66
+ const designDocumentGateway = new MarkdownDesignDocumentGateway({
67
+ rootDir: config.rootDir,
68
+ });
50
69
  const auditLogger = new PhaseOverrideAuditLogger({
51
70
  outputDir: reportOutputDir,
52
71
  });
72
+ const globMatcher = new PicomatchGlobMatcher();
73
+ const storyAnnotationVerifier = new StoryAnnotationVerifierAdapter({
74
+ designDocumentGateway,
75
+ });
76
+ const customGatesConfigParser = new CustomGatesConfigParser();
53
77
 
54
78
  // Application services
55
79
  const evidenceBundleAssembler = new EvidenceBundleAssembler({
@@ -57,6 +81,7 @@ export function createPhaseDependencyModelModule(
57
81
  planDocumentReader,
58
82
  phaseConfigProvider,
59
83
  });
84
+ const phaseGateResultMapper = new PhaseGateResultMapper();
60
85
 
61
86
  // Usecases
62
87
  const checkPhaseGateUseCase = new CheckPhaseGateUseCase({
@@ -64,10 +89,41 @@ export function createPhaseDependencyModelModule(
64
89
  evidenceBundleAssembler,
65
90
  auditLogger,
66
91
  });
92
+ const resolveGateUseCase = new ResolveGateUseCase({
93
+ globMatcher,
94
+ artifactExistenceChecker,
95
+ storyAnnotationVerifier,
96
+ });
97
+ const checkPhaseGateFacade = {
98
+ execute: async (input: {
99
+ readonly targetLevel: 1 | 2 | 3;
100
+ readonly unitId?: string;
101
+ readonly storyId?: string;
102
+ readonly targetFilePath?: string;
103
+ }) => {
104
+ if (isCustomPreset && input.targetFilePath) {
105
+ const { gates } = customGatesConfigParser.parse(rawCustomGates);
106
+ const resolved = await resolveGateUseCase.execute({
107
+ targetFilePath: input.targetFilePath,
108
+ gates,
109
+ scope: { unitId: input.unitId, storyId: input.storyId },
110
+ });
111
+ return phaseGateResultMapper.mapFromResolve(resolved, {
112
+ targetLevel: input.targetLevel,
113
+ });
114
+ }
115
+
116
+ return checkPhaseGateUseCase.execute({
117
+ targetLevel: input.targetLevel,
118
+ unitId: input.unitId,
119
+ storyId: input.storyId,
120
+ });
121
+ },
122
+ };
67
123
 
68
124
  // Presentation handlers
69
125
  const checkPhaseGateCommandHandler = new CheckPhaseGateCommandHandler({
70
- checkPhaseGateUseCase,
126
+ checkPhaseGateUseCase: checkPhaseGateFacade,
71
127
  });
72
128
 
73
129
  return {
@@ -3,12 +3,12 @@
3
3
  * @unit phase-dependency-model
4
4
  */
5
5
 
6
- import { DEFAULT_PHASE_NODES } from './default-phase-nodes.js';
6
+ import { FULL_PHASE_NODES } from './full-phase-nodes.js';
7
7
  import { PhaseDependency } from '../values/phase-dependency.js';
8
8
  import { PhaseNode } from '../values/phase-node.js';
9
9
 
10
10
  const nodeIndex = new Map<string, PhaseNode>(
11
- DEFAULT_PHASE_NODES.map((node) => [node.nodeKey(), node]),
11
+ FULL_PHASE_NODES.map((node) => [node.nodeKey(), node]),
12
12
  );
13
13
 
14
14
  const getNode = (nodeKey: string): PhaseNode => {
@@ -27,7 +27,7 @@ const createDependency = (
27
27
  type: 'requires' | 'recommends' = 'requires',
28
28
  ): PhaseDependency => PhaseDependency.create({ from: getNode(from), to: getNode(to), type });
29
29
 
30
- export const DEFAULT_PHASE_DEPENDENCIES: readonly PhaseDependency[] = Object.freeze([
30
+ export const FULL_PHASE_DEPENDENCIES: readonly PhaseDependency[] = Object.freeze([
31
31
  createDependency('1:product-architect', '1:story-writer'),
32
32
  createDependency('1:story-writer', '1:story-mapper'),
33
33
  createDependency('1:story-mapper', '1:unit-designer'),