phasegate 0.15.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 (229) hide show
  1. package/README.ja.md +103 -71
  2. package/README.md +73 -13
  3. package/bin/{harness → phasegate} +1 -1
  4. package/docs/ADR/ADR-013-story-reflection-gate.md +100 -0
  5. package/docs/guide/cli-reference.md +9 -9
  6. package/docs/guide/configuration.md +245 -8
  7. package/docs/guide/hooks-integration.md +1 -1
  8. package/docs/guide/installation.md +6 -6
  9. package/docs/guide/layer-model.md +5 -5
  10. package/docs/guide/skills-overview.md +1 -1
  11. package/package.json +12 -10
  12. package/scripts/harness/adr-foundation/domain/ports/adr-document-parser-port.ts +1 -0
  13. package/scripts/harness/adr-foundation/domain/ports/adr-frontmatter-parser-port.ts +1 -0
  14. package/scripts/harness/adr-foundation/domain/ports/adr-repository-port.ts +1 -0
  15. package/scripts/harness/adr-foundation/domain/value-objects/adr-status.ts +6 -6
  16. package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +3 -1
  17. package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +109 -3
  18. package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +1 -1
  19. package/scripts/harness/agent-integration/domain/ports/config-query-port.ts +2 -0
  20. package/scripts/harness/agent-integration/domain/ports/phase-gate-query-port.ts +1 -1
  21. package/scripts/harness/agent-integration/domain/ports/story-reflection-query-port.ts +8 -0
  22. package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +258 -0
  23. package/scripts/harness/agent-integration/domain/services/hook-to-cli-translator.ts +25 -8
  24. package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +20 -0
  25. package/scripts/harness/agent-integration/domain/value-objects/story-reflection-query-result.ts +63 -0
  26. package/scripts/harness/agent-integration/infrastructure/adapters/child-process-cli-executor-adapter.ts +2 -2
  27. package/scripts/harness/agent-integration/infrastructure/adapters/file-system-story-reflection-query-adapter.ts +122 -0
  28. package/scripts/harness/agent-integration/infrastructure/adapters/harness-api-cli-command-registry-adapter.ts +10 -10
  29. package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +20 -0
  30. package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +17 -3
  31. package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +29 -2
  32. package/scripts/harness/biome-ast-engine/domain/services/lint-runner.ts +24 -0
  33. package/scripts/harness/biome-ast-engine/domain/services/rule-definition-registry.ts +1 -1
  34. package/scripts/harness/biome-ast-engine/domain/value-objects/source-module-snapshot.ts +5 -2
  35. package/scripts/harness/biome-ast-engine/infrastructure/adapters/node-workspace-file-adapter.ts +1 -1
  36. package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +1 -1
  37. package/scripts/harness/biome-ast-engine/infrastructure/parsers/unit-comment-parser.ts +12 -7
  38. package/scripts/harness/ci-governance/composition-root.ts +1 -0
  39. package/scripts/harness/ci-governance/infrastructure/adapters/agents-md-file-adapter.ts +2 -2
  40. package/scripts/harness/ci-governance/infrastructure/adapters/yaml-template-renderer-adapter.ts +2 -2
  41. package/scripts/harness/config-foundation/application/mappers/phase-config-section-mapper.ts +44 -0
  42. package/scripts/harness/config-foundation/application/usecases/load-resolved-config-use-case.ts +20 -1
  43. package/scripts/harness/config-foundation/domain/harness-config.ts +2 -1
  44. package/scripts/harness/config-foundation/domain/value-objects/phase-dependencies-config.ts +18 -4
  45. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +185 -10
  46. package/scripts/harness/config-foundation/infrastructure/validators/ajv-config-schema-validator.ts +2 -1
  47. package/scripts/harness/harness-api/application/dto/command-dispatch-input.ts +1 -0
  48. package/scripts/harness/harness-api/application/dto/command-dispatch-output.ts +1 -0
  49. package/scripts/harness/harness-api/application/dto/exit-code-decision-input.ts +1 -0
  50. package/scripts/harness/harness-api/application/dto/exit-code-decision-output.ts +1 -0
  51. package/scripts/harness/harness-api/application/dto/harness-api-response-contract.ts +1 -0
  52. package/scripts/harness/harness-api/application/dto/initialize-registry-input.ts +1 -0
  53. package/scripts/harness/harness-api/application/dto/registry-summary-output.ts +1 -0
  54. package/scripts/harness/harness-api/application/dto/status-derivation-input.ts +1 -0
  55. package/scripts/harness/harness-api/application/mappers/harness-api-response-mapper.ts +1 -0
  56. package/scripts/harness/harness-api/application/usecases/decide-exit-code-usecase.ts +4 -3
  57. package/scripts/harness/harness-api/application/usecases/derive-harness-status-usecase.ts +1 -0
  58. package/scripts/harness/harness-api/application/usecases/dispatch-command-usecase.ts +1 -0
  59. package/scripts/harness/harness-api/application/usecases/initialize-command-registry-usecase.ts +1 -0
  60. package/scripts/harness/harness-api/composition-root.ts +1 -0
  61. package/scripts/harness/harness-api/domain/ports/artifact-scanner-port.ts +1 -0
  62. package/scripts/harness/harness-api/domain/ports/biome-lint-port.ts +1 -0
  63. package/scripts/harness/harness-api/domain/ports/config-query-port.ts +1 -0
  64. package/scripts/harness/harness-api/domain/ports/impact-analysis-port.ts +1 -0
  65. package/scripts/harness/harness-api/domain/ports/phase-gate-query-port.ts +1 -0
  66. package/scripts/harness/harness-api/domain/ports/validator-execution-port.ts +1 -0
  67. package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +17 -16
  68. package/scripts/harness/harness-api/domain/services/command-registry.ts +4 -3
  69. package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +1 -0
  70. package/scripts/harness/harness-api/domain/value-objects/artifact-scan-result.ts +1 -0
  71. package/scripts/harness/harness-api/domain/value-objects/check-ready-result.ts +1 -0
  72. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +1 -0
  73. package/scripts/harness/harness-api/domain/value-objects/cli-command-definition.ts +11 -10
  74. package/scripts/harness/harness-api/domain/value-objects/command-input-spec.ts +1 -0
  75. package/scripts/harness/harness-api/domain/value-objects/drift-report-summary.ts +1 -0
  76. package/scripts/harness/harness-api/domain/value-objects/exit-code-spec.ts +1 -0
  77. package/scripts/harness/harness-api/domain/value-objects/harness-api-response.ts +1 -0
  78. package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +1 -0
  79. package/scripts/harness/harness-api/domain/value-objects/layer-health.ts +1 -0
  80. package/scripts/harness/harness-api/domain/value-objects/phase-info.ts +1 -0
  81. package/scripts/harness/harness-api/index.ts +1 -0
  82. package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +1 -0
  83. package/scripts/harness/harness-api/infrastructure/adapters/file-system-artifact-scanner-adapter.ts +1 -0
  84. package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
  85. package/scripts/harness/harness-api/infrastructure/adapters/nyquist-validation-impact-analysis-adapter.ts +1 -0
  86. package/scripts/harness/harness-api/infrastructure/adapters/phase-dependency-model-query-adapter.ts +1 -0
  87. package/scripts/harness/harness-api/infrastructure/adapters/validator-system-execution-adapter.ts +1 -0
  88. package/scripts/harness/harness-api/presentation/dto/cli-output-options.ts +1 -0
  89. package/scripts/harness/harness-api/presentation/formatters/harness-api-json-formatter.ts +1 -0
  90. package/scripts/harness/harness-api/presentation/handlers/check-phase-handler.ts +2 -1
  91. package/scripts/harness/harness-api/presentation/handlers/check-ready-handler.ts +2 -1
  92. package/scripts/harness/harness-api/presentation/handlers/ci-check-handler.ts +2 -1
  93. package/scripts/harness/harness-api/presentation/handlers/complete-check-handler.ts +2 -1
  94. package/scripts/harness/harness-api/presentation/handlers/detect-drift-handler.ts +2 -1
  95. package/scripts/harness/harness-api/presentation/handlers/impact-analysis-handler.ts +2 -1
  96. package/scripts/harness/harness-api/presentation/handlers/lint-handler.ts +2 -1
  97. package/scripts/harness/harness-api/presentation/handlers/status-handler.ts +4 -3
  98. package/scripts/harness/harness-error/infrastructure/adapters/legacy-error-reporter-adapter.ts +7 -1
  99. package/scripts/harness/integrations/pre-commit.ts +1 -0
  100. package/scripts/harness/main.ts +173 -53
  101. package/scripts/harness/phase-dependency-model/application/dto/resolve-gate-result-dto.ts +14 -0
  102. package/scripts/harness/phase-dependency-model/application/services/phase-gate-result-mapper.ts +20 -0
  103. package/scripts/harness/phase-dependency-model/application/usecases/check-story-reflection-usecase.ts +42 -0
  104. package/scripts/harness/phase-dependency-model/application/usecases/resolve-gate-usecase.ts +161 -0
  105. package/scripts/harness/phase-dependency-model/composition-root.ts +57 -1
  106. package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-dependencies.ts → full-phase-dependencies.ts} +3 -3
  107. package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-nodes.ts → full-phase-nodes.ts} +1 -1
  108. package/scripts/harness/phase-dependency-model/domain/definitions/full-story-reflection-defaults.ts +28 -0
  109. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-dependencies.ts +32 -0
  110. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-nodes.ts +50 -0
  111. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-story-reflection-defaults.ts +8 -0
  112. package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-dependencies.ts +39 -0
  113. package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-nodes.ts +118 -0
  114. package/scripts/harness/phase-dependency-model/domain/definitions/standard-story-reflection-defaults.ts +23 -0
  115. package/scripts/harness/phase-dependency-model/domain/models/phase-structure.ts +95 -23
  116. package/scripts/harness/phase-dependency-model/domain/ports/glob-matcher-port.ts +8 -0
  117. package/scripts/harness/phase-dependency-model/domain/ports/phase-config-provider-port.ts +2 -0
  118. package/scripts/harness/phase-dependency-model/domain/ports/story-annotation-verifier-port.ts +14 -0
  119. package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +16 -0
  120. package/scripts/harness/phase-dependency-model/domain/services/gate-graph.ts +223 -0
  121. package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +66 -0
  122. package/scripts/harness/phase-dependency-model/domain/values/gate-definition.ts +127 -0
  123. package/scripts/harness/phase-dependency-model/domain/values/gate-name.ts +40 -0
  124. package/scripts/harness/phase-dependency-model/domain/values/gate-story-annotation.ts +39 -0
  125. package/scripts/harness/phase-dependency-model/domain/values/phase-customization-policy.ts +16 -4
  126. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-config.ts +44 -0
  127. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-mapping.ts +91 -0
  128. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-result.ts +55 -0
  129. package/scripts/harness/phase-dependency-model/infrastructure/adapters/picomatch-glob-matcher.ts +17 -0
  130. package/scripts/harness/phase-dependency-model/infrastructure/adapters/story-annotation-verifier-adapter.ts +46 -0
  131. package/scripts/harness/phase-dependency-model/infrastructure/config/custom-gates-config-parser.ts +22 -0
  132. package/scripts/harness/phase-dependency-model/infrastructure/config/harness-config-phase-config-provider.ts +78 -2
  133. package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +94 -0
  134. package/scripts/harness/phase-dependency-model/presentation/cli/check-phase-gate-command-handler.ts +12 -4
  135. package/scripts/harness/phase-dependency-model/presentation/cli/story-reflection-status-presenter.ts +87 -0
  136. package/scripts/harness/quick-mode/application/usecases/execute-quick-ci-check-usecase.ts +1 -1
  137. package/scripts/harness/quick-mode/presentation/handlers/ci-check-quick-mode-handler.ts +1 -1
  138. package/scripts/harness/regression-suite/application/dto/analyze-migration-input.ts +1 -0
  139. package/scripts/harness/regression-suite/application/dto/analyze-migration-output.ts +1 -0
  140. package/scripts/harness/regression-suite/application/dto/configure-ci-gate-input.ts +1 -0
  141. package/scripts/harness/regression-suite/application/dto/configure-ci-gate-output.ts +1 -0
  142. package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-input.ts +1 -0
  143. package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-output.ts +1 -0
  144. package/scripts/harness/regression-suite/application/dto/run-regression-suite-input.ts +1 -0
  145. package/scripts/harness/regression-suite/application/dto/run-regression-suite-output.ts +1 -0
  146. package/scripts/harness/regression-suite/application/mappers/migration-mapping-mapper.ts +1 -0
  147. package/scripts/harness/regression-suite/application/mappers/test-execution-summary-mapper.ts +1 -0
  148. package/scripts/harness/regression-suite/application/usecases/analyze-v0-migration-usecase.ts +1 -0
  149. package/scripts/harness/regression-suite/application/usecases/configure-ci-gate-usecase.ts +1 -0
  150. package/scripts/harness/regression-suite/application/usecases/migrate-v0-tests-usecase.ts +1 -0
  151. package/scripts/harness/regression-suite/application/usecases/run-agent-independence-guard-usecase.ts +1 -0
  152. package/scripts/harness/regression-suite/application/usecases/run-gng-gate-regression-usecase.ts +1 -0
  153. package/scripts/harness/regression-suite/application/usecases/run-k-requirements-regression-usecase.ts +1 -0
  154. package/scripts/harness/regression-suite/application/usecases/run-k14-k15-regression-usecase.ts +1 -0
  155. package/scripts/harness/regression-suite/composition-root.ts +1 -0
  156. package/scripts/harness/regression-suite/domain/aggregates/v0-test-migration.ts +1 -0
  157. package/scripts/harness/regression-suite/domain/ports/ci-gate-result-writer-port.ts +1 -0
  158. package/scripts/harness/regression-suite/domain/ports/config-query-port.ts +1 -0
  159. package/scripts/harness/regression-suite/domain/ports/import-analyzer-port.ts +1 -0
  160. package/scripts/harness/regression-suite/domain/ports/migration-mapping-repository-port.ts +1 -0
  161. package/scripts/harness/regression-suite/domain/ports/suite-registry-port.ts +1 -0
  162. package/scripts/harness/regression-suite/domain/ports/test-runner-port.ts +1 -0
  163. package/scripts/harness/regression-suite/domain/ports/v0-spec-reader-port.ts +1 -0
  164. package/scripts/harness/regression-suite/domain/services/import-guard-service.ts +1 -0
  165. package/scripts/harness/regression-suite/domain/services/migration-analyzer.ts +1 -0
  166. package/scripts/harness/regression-suite/domain/services/regression-runner.ts +1 -0
  167. package/scripts/harness/regression-suite/domain/value-objects/agent-independence-test.ts +1 -0
  168. package/scripts/harness/regression-suite/domain/value-objects/biome-modification-spec.ts +1 -0
  169. package/scripts/harness/regression-suite/domain/value-objects/ci-gate-config.ts +1 -0
  170. package/scripts/harness/regression-suite/domain/value-objects/coverage-rate.ts +1 -0
  171. package/scripts/harness/regression-suite/domain/value-objects/gng-condition-test.ts +1 -0
  172. package/scripts/harness/regression-suite/domain/value-objects/import-violation.ts +1 -0
  173. package/scripts/harness/regression-suite/domain/value-objects/k-requirement-test.ts +1 -0
  174. package/scripts/harness/regression-suite/domain/value-objects/migration-mapping.ts +1 -0
  175. package/scripts/harness/regression-suite/domain/value-objects/regression-suite-definition.ts +1 -0
  176. package/scripts/harness/regression-suite/domain/value-objects/suite-id.ts +1 -0
  177. package/scripts/harness/regression-suite/domain/value-objects/test-execution-summary.ts +1 -0
  178. package/scripts/harness/regression-suite/domain/value-objects/test-failure-detail.ts +1 -0
  179. package/scripts/harness/regression-suite/domain/value-objects/v0-test-id.ts +1 -0
  180. package/scripts/harness/regression-suite/domain/value-objects/v1-test-path.ts +1 -0
  181. package/scripts/harness/regression-suite/infrastructure/adapters/biome-ast-import-analyzer-adapter.ts +1 -0
  182. package/scripts/harness/regression-suite/infrastructure/adapters/file-system-v0-spec-reader-adapter.ts +1 -0
  183. package/scripts/harness/regression-suite/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
  184. package/scripts/harness/regression-suite/infrastructure/adapters/json-ci-gate-result-writer-adapter.ts +1 -0
  185. package/scripts/harness/regression-suite/infrastructure/adapters/markdown-migration-mapping-repository-adapter.ts +1 -0
  186. package/scripts/harness/regression-suite/infrastructure/adapters/static-suite-registry-adapter.ts +1 -0
  187. package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +1 -0
  188. package/scripts/harness/regression-suite/infrastructure/registry/agent-independence-suite-definition.ts +1 -0
  189. package/scripts/harness/regression-suite/infrastructure/registry/gng-gate-suite-definition.ts +1 -0
  190. package/scripts/harness/regression-suite/infrastructure/registry/k-requirements-suite-definition.ts +1 -0
  191. package/scripts/harness/regression-suite/infrastructure/registry/v0-migration-suite-definition.ts +1 -0
  192. package/scripts/harness/setup/skill-deployer.ts +82 -5
  193. package/scripts/harness/shared-kernel/harness-api.ts +1 -0
  194. package/scripts/harness/skill-quality/composition-root.ts +1 -0
  195. package/scripts/harness/skill-quality/infrastructure/adapters/ajv-lesson-artifact-schema-adapter.ts +5 -4
  196. package/scripts/harness/traceability-model/domain/services/metadata-validator.ts +12 -10
  197. package/scripts/harness/traceability-model/infrastructure/parsers/source-metadata-parser.ts +16 -2
  198. package/scripts/harness/validator-system/composition-root.ts +3 -3
  199. package/skills/cascade-updater/SKILL.md +16 -3
  200. package/skills/codebase-mapper/SKILL.md +9 -9
  201. package/skills/codex-delegator/SKILL.md +5 -56
  202. package/skills/consistency-checker/SKILL.md +5 -9
  203. 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
  204. package/skills/doc-freshness-checker/SKILL.md +6 -6
  205. package/skills/engineering-perspective/SKILL.md +2 -58
  206. package/skills/engineering-perspective/references/domain-model.md +4 -135
  207. package/skills/implementation-readiness-checker/SKILL.md +18 -72
  208. package/skills/it-test-designer/SKILL.md +25 -161
  209. package/skills/it-test-designer/references/it-test-design-template.md +131 -0
  210. package/skills/it-test-logic-designer/SKILL.md +13 -388
  211. package/skills/it-test-logic-designer/references/controller-test-patterns.md +100 -0
  212. package/skills/it-test-logic-designer/references/repository-test-patterns.md +206 -0
  213. package/skills/it-test-logic-designer/references/usecase-test-patterns.md +87 -0
  214. package/skills/pointer-validator/SKILL.md +4 -4
  215. package/skills/scenario-test-designer/SKILL.md +4 -68
  216. package/skills/scenario-test-designer/references/scenario-test-design-template.md +41 -0
  217. package/skills/scenario-test-designer/references/scenario-test-plan-template.md +35 -0
  218. package/skills/scenario-test-logic-designer/SKILL.md +21 -304
  219. package/skills/scenario-test-logic-designer/references/msw-patterns.md +36 -0
  220. package/skills/scenario-test-logic-designer/references/playwright-patterns.md +237 -0
  221. package/skills/test-coverage-checker/SKILL.md +39 -121
  222. package/skills/test-coverage-checker/references/coverage-report-template.md +99 -0
  223. package/skills/uiux-designer/SKILL.md +27 -92
  224. package/skills/uiux-designer/references/data-testid-convention.md +38 -0
  225. package/skills/uiux-designer/references/uiux-design-template.md +105 -0
  226. package/skills/unit-test-designer/SKILL.md +5 -106
  227. package/skills/unit-test-designer/references/unit-test-design-template.md +127 -0
  228. package/skills/unit-test-logic-designer/SKILL.md +28 -250
  229. package/skills/unit-test-logic-designer/references/test-patterns.md +207 -0
@@ -3,9 +3,16 @@
3
3
  * @unit phase-dependency-model
4
4
  */
5
5
 
6
- import { DEFAULT_PHASE_DEPENDENCIES } from '../definitions/default-phase-dependencies.js';
7
- import { DEFAULT_PHASE_NODES } from '../definitions/default-phase-nodes.js';
6
+ import { FULL_PHASE_DEPENDENCIES } from '../definitions/full-phase-dependencies.js';
7
+ import { FULL_PHASE_NODES } from '../definitions/full-phase-nodes.js';
8
+ import { MINIMAL_PHASE_DEPENDENCIES } from '../definitions/minimal-phase-dependencies.js';
9
+ import { MINIMAL_PHASE_NODES } from '../definitions/minimal-phase-nodes.js';
10
+ import { STANDARD_PHASE_DEPENDENCIES } from '../definitions/standard-phase-dependencies.js';
11
+ import { STANDARD_PHASE_NODES } from '../definitions/standard-phase-nodes.js';
12
+ import { GateGraph } from '../services/gate-graph.js';
13
+ import { Artifact } from '../values/artifact.js';
8
14
  import { CustomRule, InvalidCustomRuleError } from '../values/custom-rule.js';
15
+ import { GateDefinition } from '../values/gate-definition.js';
9
16
  import { PhaseCustomizationPolicy } from '../values/phase-customization-policy.js';
10
17
  import { PhaseDependency } from '../values/phase-dependency.js';
11
18
  import { PhaseGateResult } from '../values/phase-gate-result.js';
@@ -115,6 +122,34 @@ const createAuditPayload = (
115
122
  });
116
123
  };
117
124
 
125
+ const resolvePresetDefinitions = (preset: string): {
126
+ nodes: readonly PhaseNode[];
127
+ dependencies: readonly PhaseDependency[];
128
+ } => {
129
+ switch (preset) {
130
+ case 'standard':
131
+ return { nodes: STANDARD_PHASE_NODES, dependencies: STANDARD_PHASE_DEPENDENCIES };
132
+ case 'minimal':
133
+ return { nodes: MINIMAL_PHASE_NODES, dependencies: MINIMAL_PHASE_DEPENDENCIES };
134
+ case 'full':
135
+ case 'custom':
136
+ default:
137
+ return { nodes: FULL_PHASE_NODES, dependencies: FULL_PHASE_DEPENDENCIES };
138
+ }
139
+ };
140
+
141
+ const buildNonRelaxableDependencies = (
142
+ dependencies: readonly PhaseDependency[],
143
+ ): readonly PhaseDependency[] =>
144
+ Object.freeze(
145
+ dependencies.filter(
146
+ (dependency) =>
147
+ dependency.isLevelTransition() ||
148
+ dependency.to.nodeKey() === '3:implementation-readiness-checker' ||
149
+ dependency.to.nodeKey() === '3:story-implementor',
150
+ ),
151
+ );
152
+
118
153
  export class PhaseStructure {
119
154
  readonly levels: ReadonlyMap<1 | 2 | 3, readonly PhaseNode[]>;
120
155
  readonly nodeIndex: ReadonlyMap<string, PhaseNode>;
@@ -140,30 +175,43 @@ export class PhaseStructure {
140
175
  }
141
176
 
142
177
  static createDefault(policy: PhaseCustomizationPolicy): PhaseStructure {
143
- const levels = buildLevels(DEFAULT_PHASE_NODES);
144
- const nodeIndex = buildNodeIndex(DEFAULT_PHASE_NODES);
145
- const nonRelaxableDependencies = Object.freeze(
146
- DEFAULT_PHASE_DEPENDENCIES.filter(
147
- (dependency) =>
148
- dependency.isLevelTransition() ||
149
- dependency.to.nodeKey() === '3:implementation-readiness-checker' ||
150
- dependency.to.nodeKey() === '3:story-implementor',
178
+ const { nodes, dependencies } = resolvePresetDefinitions(policy.preset);
179
+
180
+ return PhaseStructure.createFromNodesAndDependencies(nodes, dependencies, policy);
181
+ }
182
+
183
+ static fromGates(gates: GateDefinition[], policy: PhaseCustomizationPolicy): PhaseStructure {
184
+ GateGraph.build(gates);
185
+
186
+ const nodes = gates.map((gate) =>
187
+ PhaseNode.create({
188
+ skillName: gate.name.value,
189
+ level: gate.level,
190
+ artifacts: gate.requires.map((requirement, index) =>
191
+ Artifact.create({
192
+ name: `${gate.name.value}-${index + 1}`,
193
+ path: requirement.path,
194
+ required: requirement.required,
195
+ }),
196
+ ),
197
+ }),
198
+ );
199
+ const nodeByGateName = new Map<string, PhaseNode>(
200
+ gates.map((gate, index) => [gate.name.value, nodes[index]]),
201
+ );
202
+ const dependencies = Object.freeze(
203
+ gates.flatMap((gate) =>
204
+ gate.dependsOn.map((dependencyName) =>
205
+ PhaseDependency.create({
206
+ from: nodeByGateName.get(dependencyName.value) as PhaseNode,
207
+ to: nodeByGateName.get(gate.name.value) as PhaseNode,
208
+ type: 'requires',
209
+ }),
210
+ ),
151
211
  ),
152
212
  );
153
- const base = new PhaseStructure({
154
- levels,
155
- nodeIndex,
156
- defaultDependencies: DEFAULT_PHASE_DEPENDENCIES,
157
- effectiveDependencies: DEFAULT_PHASE_DEPENDENCIES,
158
- customizationPolicy: policy,
159
- nonRelaxableDependencies,
160
- });
161
213
 
162
- if (!policy.hasRules()) {
163
- return base;
164
- }
165
-
166
- return base.applyCustomization(policy);
214
+ return PhaseStructure.createFromNodesAndDependencies(nodes, dependencies, policy);
167
215
  }
168
216
 
169
217
  checkPhaseGate(
@@ -486,4 +534,28 @@ export class PhaseStructure {
486
534
  visit(nodeKey);
487
535
  }
488
536
  }
537
+
538
+ private static createFromNodesAndDependencies(
539
+ nodes: readonly PhaseNode[],
540
+ dependencies: readonly PhaseDependency[],
541
+ policy: PhaseCustomizationPolicy,
542
+ ): PhaseStructure {
543
+ const levels = buildLevels(nodes);
544
+ const nodeIndex = buildNodeIndex(nodes);
545
+ const nonRelaxableDependencies = buildNonRelaxableDependencies(dependencies);
546
+ const base = new PhaseStructure({
547
+ levels,
548
+ nodeIndex,
549
+ defaultDependencies: dependencies,
550
+ effectiveDependencies: dependencies,
551
+ customizationPolicy: policy,
552
+ nonRelaxableDependencies,
553
+ });
554
+
555
+ if (!policy.hasRules()) {
556
+ return base;
557
+ }
558
+
559
+ return base.applyCustomization(policy);
560
+ }
489
561
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ export interface GlobMatcherPort {
7
+ match(pattern: string, path: string): boolean;
8
+ }
@@ -5,9 +5,11 @@
5
5
 
6
6
  import type { PhaseCustomizationPolicy } from '../values/phase-customization-policy.js';
7
7
  import type { PlanningMode } from '../values/planning-mode.js';
8
+ import type { StoryReflectionConfig } from '../values/story-reflection-config.js';
8
9
 
9
10
  export interface PhaseConfigProviderPort {
10
11
  getPlanningMode(scope: { unitId?: string; storyId?: string }): Promise<PlanningMode>;
11
12
  getCustomizationPolicy(): Promise<PhaseCustomizationPolicy>;
12
13
  getReportingOutputDir(): Promise<string>;
14
+ getStoryReflectionConfig(): Promise<StoryReflectionConfig>;
13
15
  }
@@ -0,0 +1,14 @@
1
+ // @unit phase-dependency-model
2
+ // @layer domain
3
+
4
+ export interface StoryAnnotationVerificationResult {
5
+ readonly hasAnnotation: boolean;
6
+ readonly storyId?: string;
7
+ }
8
+
9
+ export interface StoryAnnotationVerifierPort {
10
+ verify(
11
+ targetFilePath: string,
12
+ tag: string,
13
+ ): Promise<StoryAnnotationVerificationResult>;
14
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ export interface StoryReflectionFileSystemPort {
7
+ /** inception/{unit}/ 配下の storyId ディレクトリ名一覧を返す */
8
+ listStoryDirectories(unitId: string): Promise<readonly string[]>;
9
+ /** ファイルの存在チェック */
10
+ fileExists(path: string): Promise<boolean>;
11
+ /** product 文書内に @story-id {storyId} アノテーションが含まれているか */
12
+ fileContainsStoryAnnotation(
13
+ productPath: string,
14
+ storyId: string,
15
+ ): Promise<boolean>;
16
+ }
@@ -0,0 +1,223 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ import { GateDefinition } from '../values/gate-definition.js';
7
+ import { GateName } from '../values/gate-name.js';
8
+
9
+ export interface GateGraphViolation {
10
+ readonly code:
11
+ | 'DUPLICATE_GATE_NAME'
12
+ | 'UNKNOWN_DEPENDENCY'
13
+ | 'LEVEL_ORDER_VIOLATION'
14
+ | 'CYCLIC_DEPENDENCY';
15
+ readonly message: string;
16
+ readonly gateNames: readonly string[];
17
+ }
18
+
19
+ export class GateGraphValidationError extends Error {
20
+ readonly violations: readonly GateGraphViolation[];
21
+
22
+ constructor(violations: readonly GateGraphViolation[]) {
23
+ super(violations.map((violation) => violation.message).join('\n'));
24
+ this.name = 'GateGraphValidationError';
25
+ this.violations = Object.freeze([...violations]);
26
+ }
27
+ }
28
+
29
+ const toViolation = (
30
+ code: GateGraphViolation['code'],
31
+ message: string,
32
+ gateNames: readonly string[],
33
+ ): GateGraphViolation =>
34
+ Object.freeze({
35
+ code,
36
+ message,
37
+ gateNames: Object.freeze([...gateNames]),
38
+ });
39
+
40
+ export class GateGraph {
41
+ readonly gates: ReadonlyMap<string, GateDefinition>;
42
+
43
+ private constructor(gates: ReadonlyMap<string, GateDefinition>) {
44
+ this.gates = gates;
45
+ Object.freeze(this);
46
+ }
47
+
48
+ static build(gates: GateDefinition[]): GateGraph {
49
+ const violations: GateGraphViolation[] = [];
50
+ const duplicateNames = new Set<string>();
51
+ const gateMap = new Map<string, GateDefinition>();
52
+
53
+ for (const gate of gates) {
54
+ const name = gate.name.value;
55
+ if (gateMap.has(name)) {
56
+ duplicateNames.add(name);
57
+ continue;
58
+ }
59
+ gateMap.set(name, gate);
60
+ }
61
+
62
+ for (const duplicateName of duplicateNames) {
63
+ violations.push(
64
+ toViolation(
65
+ 'DUPLICATE_GATE_NAME',
66
+ `重複する GateName です: ${duplicateName}`,
67
+ [duplicateName],
68
+ ),
69
+ );
70
+ }
71
+
72
+ for (const gate of gateMap.values()) {
73
+ for (const dependencyName of gate.dependsOn) {
74
+ const dependency = gateMap.get(dependencyName.value);
75
+ if (!dependency) {
76
+ violations.push(
77
+ toViolation(
78
+ 'UNKNOWN_DEPENDENCY',
79
+ `未知の dependsOn 参照です: ${gate.name.value} -> ${dependencyName.value}`,
80
+ [gate.name.value, dependencyName.value],
81
+ ),
82
+ );
83
+ continue;
84
+ }
85
+
86
+ if (dependency.level.value > gate.level.value) {
87
+ violations.push(
88
+ toViolation(
89
+ 'LEVEL_ORDER_VIOLATION',
90
+ `レベル逆行です: ${gate.name.value} -> ${dependencyName.value}`,
91
+ [gate.name.value, dependencyName.value],
92
+ ),
93
+ );
94
+ }
95
+ }
96
+ }
97
+
98
+ const graph = new Map<string, string[]>();
99
+ for (const gate of gateMap.values()) {
100
+ graph.set(gate.name.value, []);
101
+ }
102
+ for (const gate of gateMap.values()) {
103
+ const neighbors = graph.get(gate.name.value);
104
+ if (!neighbors) {
105
+ continue;
106
+ }
107
+ for (const dependencyName of gate.dependsOn) {
108
+ if (gateMap.has(dependencyName.value)) {
109
+ neighbors.push(dependencyName.value);
110
+ }
111
+ }
112
+ }
113
+
114
+ for (const cycle of GateGraph.detectCycles(graph)) {
115
+ violations.push(
116
+ toViolation(
117
+ 'CYCLIC_DEPENDENCY',
118
+ `循環依存が検出されました: ${cycle.join(' -> ')}`,
119
+ cycle,
120
+ ),
121
+ );
122
+ }
123
+
124
+ if (violations.length > 0) {
125
+ throw new GateGraphValidationError(violations);
126
+ }
127
+
128
+ return new GateGraph(gateMap);
129
+ }
130
+
131
+ resolveAncestors(name: GateName): GateName[] {
132
+ const target = this.gates.get(name.value);
133
+ if (!target) {
134
+ return [];
135
+ }
136
+
137
+ const visited = new Set<string>();
138
+ const ordered: GateName[] = [];
139
+
140
+ const visit = (gate: GateDefinition): void => {
141
+ for (const dependencyName of gate.dependsOn) {
142
+ if (visited.has(dependencyName.value)) {
143
+ continue;
144
+ }
145
+
146
+ const dependency = this.gates.get(dependencyName.value);
147
+ if (!dependency) {
148
+ continue;
149
+ }
150
+
151
+ visit(dependency);
152
+ visited.add(dependencyName.value);
153
+ ordered.push(dependencyName);
154
+ }
155
+ };
156
+
157
+ visit(target);
158
+
159
+ return ordered;
160
+ }
161
+
162
+ private static detectCycles(graph: ReadonlyMap<string, readonly string[]>): readonly string[][] {
163
+ let index = 0;
164
+ const indexMap = new Map<string, number>();
165
+ const lowLinkMap = new Map<string, number>();
166
+ const stack: string[] = [];
167
+ const inStack = new Set<string>();
168
+ const cycles: string[][] = [];
169
+
170
+ const strongConnect = (node: string): void => {
171
+ indexMap.set(node, index);
172
+ lowLinkMap.set(node, index);
173
+ index += 1;
174
+ stack.push(node);
175
+ inStack.add(node);
176
+
177
+ for (const next of graph.get(node) ?? []) {
178
+ if (!indexMap.has(next)) {
179
+ strongConnect(next);
180
+ lowLinkMap.set(node, Math.min(lowLinkMap.get(node) ?? 0, lowLinkMap.get(next) ?? 0));
181
+ continue;
182
+ }
183
+
184
+ if (inStack.has(next)) {
185
+ lowLinkMap.set(node, Math.min(lowLinkMap.get(node) ?? 0, indexMap.get(next) ?? 0));
186
+ }
187
+ }
188
+
189
+ if ((lowLinkMap.get(node) ?? -1) !== (indexMap.get(node) ?? -1)) {
190
+ return;
191
+ }
192
+
193
+ const component: string[] = [];
194
+ let current = '';
195
+ do {
196
+ current = stack.pop() ?? '';
197
+ if (current.length === 0) {
198
+ break;
199
+ }
200
+ inStack.delete(current);
201
+ component.push(current);
202
+ } while (current !== node);
203
+
204
+ if (component.length > 1) {
205
+ cycles.push(component.sort());
206
+ return;
207
+ }
208
+
209
+ const [single] = component;
210
+ if ((graph.get(single) ?? []).includes(single)) {
211
+ cycles.push(component);
212
+ }
213
+ };
214
+
215
+ for (const node of graph.keys()) {
216
+ if (!indexMap.has(node)) {
217
+ strongConnect(node);
218
+ }
219
+ }
220
+
221
+ return Object.freeze(cycles);
222
+ }
223
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ import type { StoryReflectionConfig } from '../values/story-reflection-config.js';
7
+ import type { StoryReflectionFileSystemPort } from '../ports/story-reflection-file-system-port.js';
8
+ import {
9
+ StoryReflectionResult,
10
+ type StoryReflectionViolation,
11
+ } from '../values/story-reflection-result.js';
12
+
13
+ export class StoryReflectionChecker {
14
+ constructor(private readonly fsPort: StoryReflectionFileSystemPort) {}
15
+
16
+ async check(
17
+ unitId: string,
18
+ config: StoryReflectionConfig,
19
+ ): Promise<StoryReflectionResult> {
20
+ if (!config.enabled) {
21
+ return StoryReflectionResult.pass();
22
+ }
23
+
24
+ const storyIds = await this.fsPort.listStoryDirectories(unitId);
25
+
26
+ if (storyIds.length === 0) {
27
+ return StoryReflectionResult.pass();
28
+ }
29
+
30
+ const violations: StoryReflectionViolation[] = [];
31
+ const warnings: StoryReflectionViolation[] = [];
32
+
33
+ for (const storyId of storyIds) {
34
+ for (const mapping of config.mappings) {
35
+ const { inception: inceptionPath, product: productPath } =
36
+ mapping.resolve({ unitId, storyId });
37
+
38
+ const inceptionExists = await this.fsPort.fileExists(inceptionPath);
39
+
40
+ if (!inceptionExists) {
41
+ continue;
42
+ }
43
+
44
+ const hasAnnotation =
45
+ await this.fsPort.fileContainsStoryAnnotation(productPath, storyId);
46
+
47
+ if (!hasAnnotation) {
48
+ const violation: StoryReflectionViolation = {
49
+ storyId,
50
+ mapping,
51
+ inceptionPath,
52
+ productPath,
53
+ };
54
+
55
+ if (mapping.required) {
56
+ violations.push(violation);
57
+ } else {
58
+ warnings.push(violation);
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ return StoryReflectionResult.create({ violations, warnings });
65
+ }
66
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ import { GateName } from './gate-name.js';
7
+ import { GateStoryAnnotation } from './gate-story-annotation.js';
8
+ import { PhaseLevel } from './phase-level.js';
9
+
10
+ export interface GateRequirement {
11
+ readonly path: string;
12
+ readonly required: boolean;
13
+ }
14
+
15
+ export interface GateDefinitionCreateArgs {
16
+ readonly name: GateName;
17
+ readonly level: PhaseLevel;
18
+ readonly requires: readonly GateRequirement[];
19
+ readonly blocks: readonly string[];
20
+ readonly dependsOn: readonly GateName[];
21
+ readonly storyAnnotation?: GateStoryAnnotation;
22
+ }
23
+
24
+ export class InvalidGateDefinitionError extends Error {
25
+ constructor(message: string) {
26
+ super(message);
27
+ this.name = 'InvalidGateDefinitionError';
28
+ }
29
+ }
30
+
31
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
32
+ typeof value === 'object' && value !== null;
33
+
34
+ const parseRequirement = (raw: unknown): GateRequirement => {
35
+ if (!isRecord(raw) || typeof raw.path !== 'string' || typeof raw.required !== 'boolean') {
36
+ throw new InvalidGateDefinitionError('requires の形式が不正です');
37
+ }
38
+
39
+ const path = raw.path.trim();
40
+ if (path.length === 0) {
41
+ throw new InvalidGateDefinitionError('requires.path は必須です');
42
+ }
43
+
44
+ return Object.freeze({
45
+ path,
46
+ required: raw.required,
47
+ });
48
+ };
49
+
50
+ const parseStringArray = (raw: unknown, fieldName: string): readonly string[] => {
51
+ if (!Array.isArray(raw) || raw.some((entry) => typeof entry !== 'string')) {
52
+ throw new InvalidGateDefinitionError(`${fieldName} の形式が不正です`);
53
+ }
54
+
55
+ return Object.freeze(raw.map((entry) => entry.trim()));
56
+ };
57
+
58
+ export class GateDefinition {
59
+ readonly name: GateName;
60
+ readonly level: PhaseLevel;
61
+ readonly requires: readonly GateRequirement[];
62
+ readonly blocks: readonly string[];
63
+ readonly dependsOn: readonly GateName[];
64
+ readonly storyAnnotation?: GateStoryAnnotation;
65
+
66
+ private constructor(args: GateDefinitionCreateArgs) {
67
+ this.name = args.name;
68
+ this.level = args.level;
69
+ this.requires = Object.freeze([...args.requires]);
70
+ this.blocks = Object.freeze([...args.blocks]);
71
+ this.dependsOn = Object.freeze([...args.dependsOn]);
72
+ this.storyAnnotation = args.storyAnnotation;
73
+ Object.freeze(this);
74
+ }
75
+
76
+ static create(args: GateDefinitionCreateArgs): GateDefinition {
77
+ if (args.storyAnnotation && args.level.value !== 3) {
78
+ throw new InvalidGateDefinitionError('storyAnnotation は Level 3 のゲートにのみ指定できます');
79
+ }
80
+
81
+ return new GateDefinition(args);
82
+ }
83
+
84
+ static fromRaw(raw: unknown): GateDefinition {
85
+ if (!isRecord(raw)) {
86
+ throw new InvalidGateDefinitionError('GateDefinition の形式が不正です');
87
+ }
88
+
89
+ const name = GateName.create(String(raw.name ?? ''));
90
+ const level = PhaseLevel.create(Number(raw.level));
91
+ const requires = Array.isArray(raw.requires)
92
+ ? Object.freeze(raw.requires.map((requirement) => parseRequirement(requirement)))
93
+ : (() => {
94
+ throw new InvalidGateDefinitionError('requires の形式が不正です');
95
+ })();
96
+ const blocks = parseStringArray(raw.blocks ?? [], 'blocks');
97
+ const dependsOn = parseStringArray(raw.dependsOn ?? [], 'dependsOn').map((value) =>
98
+ GateName.create(value),
99
+ );
100
+ const storyAnnotation =
101
+ raw.storyAnnotation === undefined
102
+ ? undefined
103
+ : (() => {
104
+ if (
105
+ !isRecord(raw.storyAnnotation) ||
106
+ typeof raw.storyAnnotation.required !== 'boolean' ||
107
+ typeof raw.storyAnnotation.tag !== 'string'
108
+ ) {
109
+ throw new InvalidGateDefinitionError('storyAnnotation の形式が不正です');
110
+ }
111
+
112
+ return GateStoryAnnotation.create({
113
+ required: raw.storyAnnotation.required,
114
+ tag: raw.storyAnnotation.tag,
115
+ });
116
+ })();
117
+
118
+ return GateDefinition.create({
119
+ name,
120
+ level,
121
+ requires,
122
+ blocks,
123
+ dependsOn: Object.freeze(dependsOn),
124
+ storyAnnotation,
125
+ });
126
+ }
127
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ const GATE_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
7
+
8
+ export class InvalidGateNameError extends Error {
9
+ constructor(value: string) {
10
+ super(`GateNameが不正です: ${value}`);
11
+ this.name = 'InvalidGateNameError';
12
+ }
13
+ }
14
+
15
+ export class GateName {
16
+ readonly value: string;
17
+
18
+ private constructor(value: string) {
19
+ this.value = value;
20
+ Object.freeze(this);
21
+ }
22
+
23
+ static create(value: string): GateName {
24
+ const normalizedValue = value.trim();
25
+
26
+ if (
27
+ normalizedValue.length === 0 ||
28
+ !GATE_NAME_PATTERN.test(normalizedValue) ||
29
+ normalizedValue.endsWith('-')
30
+ ) {
31
+ throw new InvalidGateNameError(value);
32
+ }
33
+
34
+ return new GateName(normalizedValue);
35
+ }
36
+
37
+ equals(other: GateName): boolean {
38
+ return this.value === other.value;
39
+ }
40
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit phase-dependency-model
4
+ */
5
+
6
+ export interface GateStoryAnnotationCreateArgs {
7
+ readonly required: boolean;
8
+ readonly tag: string;
9
+ }
10
+
11
+ export class InvalidGateStoryAnnotationError extends Error {
12
+ constructor(message: string) {
13
+ super(message);
14
+ this.name = 'InvalidGateStoryAnnotationError';
15
+ }
16
+ }
17
+
18
+ export class GateStoryAnnotation {
19
+ readonly required: boolean;
20
+ readonly tag: string;
21
+
22
+ private constructor(args: GateStoryAnnotationCreateArgs) {
23
+ this.required = args.required;
24
+ this.tag = args.tag.trim();
25
+ Object.freeze(this);
26
+ }
27
+
28
+ static create(args: GateStoryAnnotationCreateArgs): GateStoryAnnotation {
29
+ if (args.tag.trim().length === 0) {
30
+ throw new InvalidGateStoryAnnotationError('tagは必須です');
31
+ }
32
+
33
+ return new GateStoryAnnotation(args);
34
+ }
35
+
36
+ equals(other: GateStoryAnnotation): boolean {
37
+ return this.required === other.required && this.tag === other.tag;
38
+ }
39
+ }