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.
- package/README.ja.md +71 -39
- package/README.md +69 -9
- package/docs/ADR/ADR-013-story-reflection-gate.md +100 -0
- package/docs/guide/configuration.md +244 -7
- package/package.json +4 -2
- package/scripts/harness/adr-foundation/domain/ports/adr-document-parser-port.ts +1 -0
- package/scripts/harness/adr-foundation/domain/ports/adr-frontmatter-parser-port.ts +1 -0
- package/scripts/harness/adr-foundation/domain/ports/adr-repository-port.ts +1 -0
- package/scripts/harness/adr-foundation/domain/value-objects/adr-status.ts +6 -6
- package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +3 -1
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +108 -2
- package/scripts/harness/agent-integration/domain/ports/config-query-port.ts +2 -0
- package/scripts/harness/agent-integration/domain/ports/phase-gate-query-port.ts +1 -1
- package/scripts/harness/agent-integration/domain/ports/story-reflection-query-port.ts +8 -0
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +258 -0
- package/scripts/harness/agent-integration/domain/services/hook-to-cli-translator.ts +21 -4
- package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +20 -0
- package/scripts/harness/agent-integration/domain/value-objects/story-reflection-query-result.ts +63 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/file-system-story-reflection-query-adapter.ts +122 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +20 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +17 -3
- package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +29 -2
- package/scripts/harness/biome-ast-engine/domain/services/lint-runner.ts +24 -0
- package/scripts/harness/biome-ast-engine/domain/services/rule-definition-registry.ts +1 -1
- package/scripts/harness/biome-ast-engine/domain/value-objects/source-module-snapshot.ts +5 -2
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/node-workspace-file-adapter.ts +1 -1
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +1 -1
- package/scripts/harness/biome-ast-engine/infrastructure/parsers/unit-comment-parser.ts +12 -7
- package/scripts/harness/ci-governance/composition-root.ts +1 -0
- package/scripts/harness/config-foundation/application/mappers/phase-config-section-mapper.ts +44 -0
- package/scripts/harness/config-foundation/application/usecases/load-resolved-config-use-case.ts +20 -1
- package/scripts/harness/config-foundation/domain/harness-config.ts +2 -1
- package/scripts/harness/config-foundation/domain/value-objects/phase-dependencies-config.ts +18 -4
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +185 -10
- package/scripts/harness/config-foundation/infrastructure/validators/ajv-config-schema-validator.ts +2 -1
- package/scripts/harness/harness-api/application/dto/command-dispatch-input.ts +1 -0
- package/scripts/harness/harness-api/application/dto/command-dispatch-output.ts +1 -0
- package/scripts/harness/harness-api/application/dto/exit-code-decision-input.ts +1 -0
- package/scripts/harness/harness-api/application/dto/exit-code-decision-output.ts +1 -0
- package/scripts/harness/harness-api/application/dto/harness-api-response-contract.ts +1 -0
- package/scripts/harness/harness-api/application/dto/initialize-registry-input.ts +1 -0
- package/scripts/harness/harness-api/application/dto/registry-summary-output.ts +1 -0
- package/scripts/harness/harness-api/application/dto/status-derivation-input.ts +1 -0
- package/scripts/harness/harness-api/application/mappers/harness-api-response-mapper.ts +1 -0
- package/scripts/harness/harness-api/application/usecases/decide-exit-code-usecase.ts +1 -0
- package/scripts/harness/harness-api/application/usecases/derive-harness-status-usecase.ts +1 -0
- package/scripts/harness/harness-api/application/usecases/dispatch-command-usecase.ts +1 -0
- package/scripts/harness/harness-api/application/usecases/initialize-command-registry-usecase.ts +1 -0
- package/scripts/harness/harness-api/composition-root.ts +1 -0
- package/scripts/harness/harness-api/domain/ports/artifact-scanner-port.ts +1 -0
- package/scripts/harness/harness-api/domain/ports/biome-lint-port.ts +1 -0
- package/scripts/harness/harness-api/domain/ports/config-query-port.ts +1 -0
- package/scripts/harness/harness-api/domain/ports/impact-analysis-port.ts +1 -0
- package/scripts/harness/harness-api/domain/ports/phase-gate-query-port.ts +1 -0
- package/scripts/harness/harness-api/domain/ports/validator-execution-port.ts +1 -0
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +1 -0
- package/scripts/harness/harness-api/domain/services/command-registry.ts +1 -0
- package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/artifact-scan-result.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/check-ready-result.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/cli-command-definition.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/command-input-spec.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/drift-report-summary.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/exit-code-spec.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/harness-api-response.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/layer-health.ts +1 -0
- package/scripts/harness/harness-api/domain/value-objects/phase-info.ts +1 -0
- package/scripts/harness/harness-api/index.ts +1 -0
- package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +1 -0
- package/scripts/harness/harness-api/infrastructure/adapters/file-system-artifact-scanner-adapter.ts +1 -0
- package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
- package/scripts/harness/harness-api/infrastructure/adapters/nyquist-validation-impact-analysis-adapter.ts +1 -0
- package/scripts/harness/harness-api/infrastructure/adapters/phase-dependency-model-query-adapter.ts +1 -0
- package/scripts/harness/harness-api/infrastructure/adapters/validator-system-execution-adapter.ts +1 -0
- package/scripts/harness/harness-api/presentation/dto/cli-output-options.ts +1 -0
- package/scripts/harness/harness-api/presentation/formatters/harness-api-json-formatter.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/check-phase-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/check-ready-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/ci-check-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/complete-check-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/detect-drift-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/impact-analysis-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/lint-handler.ts +1 -0
- package/scripts/harness/harness-api/presentation/handlers/status-handler.ts +1 -0
- package/scripts/harness/harness-error/infrastructure/adapters/legacy-error-reporter-adapter.ts +7 -1
- package/scripts/harness/integrations/pre-commit.ts +1 -0
- package/scripts/harness/main.ts +154 -34
- package/scripts/harness/phase-dependency-model/application/dto/resolve-gate-result-dto.ts +14 -0
- package/scripts/harness/phase-dependency-model/application/services/phase-gate-result-mapper.ts +20 -0
- package/scripts/harness/phase-dependency-model/application/usecases/check-story-reflection-usecase.ts +42 -0
- package/scripts/harness/phase-dependency-model/application/usecases/resolve-gate-usecase.ts +161 -0
- package/scripts/harness/phase-dependency-model/composition-root.ts +57 -1
- package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-dependencies.ts → full-phase-dependencies.ts} +3 -3
- package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-nodes.ts → full-phase-nodes.ts} +1 -1
- package/scripts/harness/phase-dependency-model/domain/definitions/full-story-reflection-defaults.ts +28 -0
- package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-dependencies.ts +32 -0
- package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-nodes.ts +50 -0
- package/scripts/harness/phase-dependency-model/domain/definitions/minimal-story-reflection-defaults.ts +8 -0
- package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-dependencies.ts +39 -0
- package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-nodes.ts +118 -0
- package/scripts/harness/phase-dependency-model/domain/definitions/standard-story-reflection-defaults.ts +23 -0
- package/scripts/harness/phase-dependency-model/domain/models/phase-structure.ts +95 -23
- package/scripts/harness/phase-dependency-model/domain/ports/glob-matcher-port.ts +8 -0
- package/scripts/harness/phase-dependency-model/domain/ports/phase-config-provider-port.ts +2 -0
- package/scripts/harness/phase-dependency-model/domain/ports/story-annotation-verifier-port.ts +14 -0
- package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +16 -0
- package/scripts/harness/phase-dependency-model/domain/services/gate-graph.ts +223 -0
- package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +66 -0
- package/scripts/harness/phase-dependency-model/domain/values/gate-definition.ts +127 -0
- package/scripts/harness/phase-dependency-model/domain/values/gate-name.ts +40 -0
- package/scripts/harness/phase-dependency-model/domain/values/gate-story-annotation.ts +39 -0
- package/scripts/harness/phase-dependency-model/domain/values/phase-customization-policy.ts +16 -4
- package/scripts/harness/phase-dependency-model/domain/values/story-reflection-config.ts +44 -0
- package/scripts/harness/phase-dependency-model/domain/values/story-reflection-mapping.ts +91 -0
- package/scripts/harness/phase-dependency-model/domain/values/story-reflection-result.ts +55 -0
- package/scripts/harness/phase-dependency-model/infrastructure/adapters/picomatch-glob-matcher.ts +17 -0
- package/scripts/harness/phase-dependency-model/infrastructure/adapters/story-annotation-verifier-adapter.ts +46 -0
- package/scripts/harness/phase-dependency-model/infrastructure/config/custom-gates-config-parser.ts +22 -0
- package/scripts/harness/phase-dependency-model/infrastructure/config/harness-config-phase-config-provider.ts +78 -2
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +94 -0
- package/scripts/harness/phase-dependency-model/presentation/cli/check-phase-gate-command-handler.ts +12 -4
- package/scripts/harness/phase-dependency-model/presentation/cli/story-reflection-status-presenter.ts +87 -0
- package/scripts/harness/regression-suite/application/dto/analyze-migration-input.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/analyze-migration-output.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/configure-ci-gate-input.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/configure-ci-gate-output.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-input.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-output.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/run-regression-suite-input.ts +1 -0
- package/scripts/harness/regression-suite/application/dto/run-regression-suite-output.ts +1 -0
- package/scripts/harness/regression-suite/application/mappers/migration-mapping-mapper.ts +1 -0
- package/scripts/harness/regression-suite/application/mappers/test-execution-summary-mapper.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/analyze-v0-migration-usecase.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/configure-ci-gate-usecase.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/migrate-v0-tests-usecase.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/run-agent-independence-guard-usecase.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/run-gng-gate-regression-usecase.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/run-k-requirements-regression-usecase.ts +1 -0
- package/scripts/harness/regression-suite/application/usecases/run-k14-k15-regression-usecase.ts +1 -0
- package/scripts/harness/regression-suite/composition-root.ts +1 -0
- package/scripts/harness/regression-suite/domain/aggregates/v0-test-migration.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/ci-gate-result-writer-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/config-query-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/import-analyzer-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/migration-mapping-repository-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/suite-registry-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/test-runner-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/ports/v0-spec-reader-port.ts +1 -0
- package/scripts/harness/regression-suite/domain/services/import-guard-service.ts +1 -0
- package/scripts/harness/regression-suite/domain/services/migration-analyzer.ts +1 -0
- package/scripts/harness/regression-suite/domain/services/regression-runner.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/agent-independence-test.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/biome-modification-spec.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/ci-gate-config.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/coverage-rate.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/gng-condition-test.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/import-violation.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/k-requirement-test.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/migration-mapping.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/regression-suite-definition.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/suite-id.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/test-execution-summary.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/test-failure-detail.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/v0-test-id.ts +1 -0
- package/scripts/harness/regression-suite/domain/value-objects/v1-test-path.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/biome-ast-import-analyzer-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/file-system-v0-spec-reader-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/json-ci-gate-result-writer-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/markdown-migration-mapping-repository-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/static-suite-registry-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/registry/agent-independence-suite-definition.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/registry/gng-gate-suite-definition.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/registry/k-requirements-suite-definition.ts +1 -0
- package/scripts/harness/regression-suite/infrastructure/registry/v0-migration-suite-definition.ts +1 -0
- package/scripts/harness/setup/skill-deployer.ts +82 -5
- package/scripts/harness/shared-kernel/harness-api.ts +1 -0
- package/scripts/harness/skill-quality/composition-root.ts +1 -0
- package/scripts/harness/skill-quality/infrastructure/adapters/ajv-lesson-artifact-schema-adapter.ts +5 -4
- package/scripts/harness/traceability-model/domain/services/metadata-validator.ts +12 -10
- package/scripts/harness/traceability-model/infrastructure/parsers/source-metadata-parser.ts +16 -2
- package/skills/cascade-updater/SKILL.md +16 -3
- package/skills/codebase-mapper/SKILL.md +9 -9
- package/skills/codex-delegator/SKILL.md +5 -56
- package/skills/consistency-checker/SKILL.md +5 -9
- 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
- package/skills/doc-freshness-checker/SKILL.md +6 -6
- package/skills/engineering-perspective/SKILL.md +2 -58
- package/skills/engineering-perspective/references/domain-model.md +4 -135
- package/skills/implementation-readiness-checker/SKILL.md +18 -72
- package/skills/it-test-designer/SKILL.md +25 -161
- package/skills/it-test-designer/references/it-test-design-template.md +131 -0
- package/skills/it-test-logic-designer/SKILL.md +13 -388
- package/skills/it-test-logic-designer/references/controller-test-patterns.md +100 -0
- package/skills/it-test-logic-designer/references/repository-test-patterns.md +206 -0
- package/skills/it-test-logic-designer/references/usecase-test-patterns.md +87 -0
- package/skills/pointer-validator/SKILL.md +4 -4
- package/skills/scenario-test-designer/SKILL.md +4 -68
- package/skills/scenario-test-designer/references/scenario-test-design-template.md +41 -0
- package/skills/scenario-test-designer/references/scenario-test-plan-template.md +35 -0
- package/skills/scenario-test-logic-designer/SKILL.md +21 -304
- package/skills/scenario-test-logic-designer/references/msw-patterns.md +36 -0
- package/skills/scenario-test-logic-designer/references/playwright-patterns.md +237 -0
- package/skills/test-coverage-checker/SKILL.md +39 -121
- package/skills/test-coverage-checker/references/coverage-report-template.md +99 -0
- package/skills/uiux-designer/SKILL.md +27 -92
- package/skills/uiux-designer/references/data-testid-convention.md +38 -0
- package/skills/uiux-designer/references/uiux-design-template.md +105 -0
- package/skills/unit-test-designer/SKILL.md +5 -106
- package/skills/unit-test-designer/references/unit-test-design-template.md +127 -0
- package/skills/unit-test-logic-designer/SKILL.md +28 -250
- package/skills/unit-test-logic-designer/references/test-patterns.md +207 -0
package/scripts/harness/main.ts
CHANGED
|
@@ -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
|
|
212
|
+
type InitPhasePreset = 'full' | 'standard' | 'minimal' | 'custom';
|
|
203
213
|
|
|
204
|
-
function
|
|
205
|
-
if (value ===
|
|
206
|
-
|
|
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) から
|
|
240
|
+
* HarnessConfigV2 (resolved) から biome-ast-engine が期待する L1 Config を抽出する。
|
|
228
241
|
*/
|
|
229
|
-
function
|
|
242
|
+
function toL1Config(resolvedConfig: HarnessConfigV2) {
|
|
230
243
|
return {
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
*
|
|
250
|
+
* phasegate.config.json を直接読み、storyReflection 設定解決用の provider を返す。
|
|
251
|
+
* config-foundation の HarnessConfigV2 は storyReflection 未サポートのため raw JSON 経由。
|
|
247
252
|
*/
|
|
248
|
-
function
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
|
294
|
-
|
|
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
|
-
|
|
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
|
-
|
|
937
|
+
|
|
938
|
+
const grouped: Record<string, string[]> = { core: [], aidlc: [], utility: [], unknown: [] };
|
|
837
939
|
for (const name of skills) {
|
|
838
|
-
|
|
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
|
+
}
|
package/scripts/harness/phase-dependency-model/application/services/phase-gate-result-mapper.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
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'),
|