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
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// @unit agent-integration
|
|
2
|
+
// @layer infrastructure
|
|
3
|
+
/**
|
|
4
|
+
* FileSystemStoryReflectionQueryAdapter
|
|
5
|
+
*
|
|
6
|
+
* agent-integration ドメインの `StoryReflectionQueryPort` を、
|
|
7
|
+
* phase-dependency-model 側の CheckStoryReflectionUseCase +
|
|
8
|
+
* FileSystemStoryReflectionAdapter + HarnessConfigPhaseConfigProvider を
|
|
9
|
+
* オーケストレートして実装するアダプタ。
|
|
10
|
+
*
|
|
11
|
+
* 役割:
|
|
12
|
+
* 1. phasegate.config.json を読み込み、PhaseConfigSection へ正規化
|
|
13
|
+
* 2. HarnessConfigPhaseConfigProvider 経由で storyReflection 設定を解決
|
|
14
|
+
* 3. StoryReflectionChecker + FileSystemStoryReflectionAdapter 経由で
|
|
15
|
+
* inception → product の @story-id 反映を検証
|
|
16
|
+
* 4. 結果を agent-integration の StoryReflectionQueryResult に写像
|
|
17
|
+
*
|
|
18
|
+
* 例外: 設定ファイルが存在しない/読めない場合は "skipped" を返し、
|
|
19
|
+
* hook 全体をブロックしない(Fail-safe)。
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFile } from 'node:fs/promises';
|
|
23
|
+
|
|
24
|
+
import type { StoryReflectionQueryPort } from '../../domain/ports/story-reflection-query-port.js';
|
|
25
|
+
import { StoryReflectionQueryResult } from '../../domain/value-objects/story-reflection-query-result.js';
|
|
26
|
+
import { FileSystemStoryReflectionAdapter } from '../../../phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.js';
|
|
27
|
+
import { StoryReflectionChecker } from '../../../phase-dependency-model/domain/services/story-reflection-checker.js';
|
|
28
|
+
import { CheckStoryReflectionUseCase } from '../../../phase-dependency-model/application/usecases/check-story-reflection-usecase.js';
|
|
29
|
+
import {
|
|
30
|
+
HarnessConfigPhaseConfigProvider,
|
|
31
|
+
type PhaseConfigSection,
|
|
32
|
+
type PhasePresetInput,
|
|
33
|
+
type StoryReflectionSectionInput,
|
|
34
|
+
} from '../../../phase-dependency-model/infrastructure/config/harness-config-phase-config-provider.js';
|
|
35
|
+
import type { StoryReflectionResult } from '../../../phase-dependency-model/domain/values/story-reflection-result.js';
|
|
36
|
+
|
|
37
|
+
export interface FileSystemStoryReflectionQueryAdapterDeps {
|
|
38
|
+
readonly rootDir: string;
|
|
39
|
+
readonly configPath: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface RawPhaseDependenciesSection {
|
|
43
|
+
readonly preset?: PhasePresetInput;
|
|
44
|
+
readonly override?: boolean;
|
|
45
|
+
readonly storyReflection?: StoryReflectionSectionInput;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface RawConfigDocument {
|
|
49
|
+
readonly phaseDependencies?: RawPhaseDependenciesSection;
|
|
50
|
+
readonly reporting?: { readonly outputDir?: string };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class FileSystemStoryReflectionQueryAdapter
|
|
54
|
+
implements StoryReflectionQueryPort
|
|
55
|
+
{
|
|
56
|
+
private readonly rootDir: string;
|
|
57
|
+
private readonly configPath: string;
|
|
58
|
+
|
|
59
|
+
constructor(deps: FileSystemStoryReflectionQueryAdapterDeps) {
|
|
60
|
+
this.rootDir = deps.rootDir;
|
|
61
|
+
this.configPath = deps.configPath;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async checkReflection(unitId: string): Promise<StoryReflectionQueryResult> {
|
|
65
|
+
let raw: RawConfigDocument;
|
|
66
|
+
try {
|
|
67
|
+
const content = await readFile(this.configPath, 'utf8');
|
|
68
|
+
raw = JSON.parse(content) as RawConfigDocument;
|
|
69
|
+
} catch {
|
|
70
|
+
// 設定が読めない場合は fail-safe で skipped 扱い
|
|
71
|
+
return StoryReflectionQueryResult.skipped();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const phaseConfigSection: PhaseConfigSection = {
|
|
75
|
+
customization: {
|
|
76
|
+
preset: raw.phaseDependencies?.preset,
|
|
77
|
+
overrideEnabled: raw.phaseDependencies?.override ?? false,
|
|
78
|
+
},
|
|
79
|
+
storyReflection: raw.phaseDependencies?.storyReflection,
|
|
80
|
+
reportingOutputDir: raw.reporting?.outputDir,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const provider = new HarnessConfigPhaseConfigProvider({
|
|
84
|
+
config: phaseConfigSection,
|
|
85
|
+
defaultOutputDir: raw.reporting?.outputDir ?? '.harness/reports',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const config = await provider.getStoryReflectionConfig();
|
|
89
|
+
|
|
90
|
+
if (!config.enabled) {
|
|
91
|
+
return StoryReflectionQueryResult.skipped();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fsAdapter = new FileSystemStoryReflectionAdapter({ rootDir: this.rootDir });
|
|
95
|
+
const checker = new StoryReflectionChecker(fsAdapter);
|
|
96
|
+
const useCase = new CheckStoryReflectionUseCase({ checker });
|
|
97
|
+
|
|
98
|
+
const result = await useCase.execute({ unitId, config });
|
|
99
|
+
|
|
100
|
+
return FileSystemStoryReflectionQueryAdapter.mapResult(result);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private static mapResult(
|
|
104
|
+
result: StoryReflectionResult,
|
|
105
|
+
): StoryReflectionQueryResult {
|
|
106
|
+
const warnings = result.warnings.map(
|
|
107
|
+
(w) =>
|
|
108
|
+
`${w.storyId}: ${w.productPath} に @story-id ${w.storyId} が未反映 (optional, inception: ${w.inceptionPath})`,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (!result.isBlocked()) {
|
|
112
|
+
return StoryReflectionQueryResult.pass();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const blockers = result.violations.map(
|
|
116
|
+
(v) =>
|
|
117
|
+
`${v.productPath} に @story-id ${v.storyId} が反映されていません (inception: ${v.inceptionPath})`,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return StoryReflectionQueryResult.block(blockers, warnings);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -31,9 +31,19 @@ interface HarnessesSection {
|
|
|
31
31
|
deadCodeGC?: boolean;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
interface ProtectedFilesSection {
|
|
35
|
+
exclude?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface QuickModeSection {
|
|
39
|
+
relaxedGates?: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
interface HarnessConfigDocument {
|
|
35
43
|
harnesses?: HarnessesSection;
|
|
36
44
|
project?: ProjectSection;
|
|
45
|
+
protectedFiles?: ProtectedFilesSection;
|
|
46
|
+
quickMode?: QuickModeSection;
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
export class HarnessConfigConfigQueryAdapter implements ConfigQueryPort {
|
|
@@ -77,6 +87,16 @@ export class HarnessConfigConfigQueryAdapter implements ConfigQueryPort {
|
|
|
77
87
|
return [];
|
|
78
88
|
}
|
|
79
89
|
|
|
90
|
+
async getProtectedFileExclusions(): Promise<string[]> {
|
|
91
|
+
const config = this.loadConfig();
|
|
92
|
+
return config.protectedFiles?.exclude ?? [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getRelaxedGates(): Promise<readonly string[]> {
|
|
96
|
+
const config = this.loadConfig();
|
|
97
|
+
return config.quickMode?.relaxedGates ?? [];
|
|
98
|
+
}
|
|
99
|
+
|
|
80
100
|
getProjectPaths(): ProjectPaths {
|
|
81
101
|
const config = this.loadConfig();
|
|
82
102
|
const paths = config.project?.paths;
|
package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts
CHANGED
|
@@ -4,16 +4,26 @@
|
|
|
4
4
|
import type { PhaseGateQueryPort } from '../../domain/ports/phase-gate-query-port.js';
|
|
5
5
|
import type { WriteTargetScope } from '../../domain/value-objects/write-target-scope.js';
|
|
6
6
|
import { PhaseGateQueryResult } from '../../domain/value-objects/phase-gate-query-result.js';
|
|
7
|
+
import { toPhaseConfigSection } from '../../../config-foundation/application/mappers/phase-config-section-mapper.js';
|
|
8
|
+
import { createConfigFoundationModule } from '../../../config-foundation/composition-root.js';
|
|
9
|
+
import { ConfigValidationError } from '../../../config-foundation/domain/errors/config-validation-error.js';
|
|
7
10
|
|
|
8
11
|
export class PhaseGateQueryAdapter implements PhaseGateQueryPort {
|
|
9
|
-
async checkGate(scope: WriteTargetScope): Promise<PhaseGateQueryResult> {
|
|
12
|
+
async checkGate(scope: WriteTargetScope, targetFilePath?: string): Promise<PhaseGateQueryResult> {
|
|
10
13
|
try {
|
|
14
|
+
const configModule = createConfigFoundationModule();
|
|
15
|
+
const resolvedConfig = await configModule.usecases.loadResolvedConfigUseCase.execute();
|
|
11
16
|
const { createPhaseDependencyModelModule } = await import('../../../phase-dependency-model/composition-root.js');
|
|
12
|
-
const mod = createPhaseDependencyModelModule({
|
|
17
|
+
const mod = createPhaseDependencyModelModule({
|
|
18
|
+
rootDir: process.cwd(),
|
|
19
|
+
phaseConfig: toPhaseConfigSection(resolvedConfig.config),
|
|
20
|
+
reportOutputDir: resolvedConfig.config.reporting.outputDir,
|
|
21
|
+
});
|
|
13
22
|
const result = await mod.checkPhaseGateCommandHandler.execute({
|
|
14
23
|
targetLevel: scope.level,
|
|
15
24
|
unitId: scope.unitId,
|
|
16
25
|
storyId: scope.storyId,
|
|
26
|
+
targetFilePath,
|
|
17
27
|
});
|
|
18
28
|
|
|
19
29
|
if (result.exitCode === 0) {
|
|
@@ -25,7 +35,11 @@ export class PhaseGateQueryAdapter implements PhaseGateQueryPort {
|
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
return PhaseGateQueryResult.create(true, [], ['phase gate check returned error']);
|
|
28
|
-
} catch {
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error instanceof ConfigValidationError) {
|
|
40
|
+
return PhaseGateQueryResult.create(false, [error.message], []);
|
|
41
|
+
}
|
|
42
|
+
|
|
29
43
|
return PhaseGateQueryResult.create(true, [], ['phase-dependency-model not available']);
|
|
30
44
|
}
|
|
31
45
|
}
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { HandlePreToolUseUseCase } from '../application/usecases/handle-pre-tool-use-usecase.js';
|
|
11
|
+
import { BashWriteTargetExtractor } from '../domain/services/bash-write-target-extractor.js';
|
|
11
12
|
import { HarnessConfigConfigQueryAdapter } from '../infrastructure/adapters/harness-config-config-query-adapter.js';
|
|
12
13
|
import { PhaseGateQueryAdapter } from '../infrastructure/adapters/phase-gate-query-adapter.js';
|
|
14
|
+
import { FileSystemStoryReflectionQueryAdapter } from '../infrastructure/adapters/file-system-story-reflection-query-adapter.js';
|
|
13
15
|
import * as path from 'node:path';
|
|
14
16
|
import * as fs from 'node:fs/promises';
|
|
15
17
|
|
|
@@ -20,6 +22,7 @@ interface PreToolUseHookInput {
|
|
|
20
22
|
path?: string;
|
|
21
23
|
file_path?: string;
|
|
22
24
|
paths?: string[];
|
|
25
|
+
command?: string;
|
|
23
26
|
[key: string]: unknown;
|
|
24
27
|
};
|
|
25
28
|
}
|
|
@@ -91,13 +94,37 @@ async function main(): Promise<void> {
|
|
|
91
94
|
targetFilePaths.push(...input.tool_input.paths.map(toRelative));
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
// Bash 経由書き込みのフェーズゲート対応 (A-2.5)
|
|
98
|
+
// Bash command 文字列からリダイレクト・tee・sed -i・cp・mv・touch 等の
|
|
99
|
+
// 書き込み先ファイルパスを抽出し、フェーズゲートチェック対象に含める。
|
|
100
|
+
// Bash 書き込みを検出した場合、effectiveToolName を 'Write' に偽装して
|
|
101
|
+
// translator の WRITE_TOOLS チェックを通過させる(Bash のままではフェーズゲートが
|
|
102
|
+
// スキップされるため)。
|
|
103
|
+
let effectiveToolName = toolName;
|
|
104
|
+
if (toolName === 'Bash' && typeof input.tool_input?.command === 'string') {
|
|
105
|
+
const extractor = new BashWriteTargetExtractor();
|
|
106
|
+
const bashTargets = extractor.extract(input.tool_input.command);
|
|
107
|
+
if (bashTargets.length > 0) {
|
|
108
|
+
targetFilePaths.push(...bashTargets.map(toRelative));
|
|
109
|
+
effectiveToolName = 'Write';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
94
113
|
try {
|
|
95
114
|
const configPath = await findConfigPath();
|
|
96
115
|
const configQueryPort = new HarnessConfigConfigQueryAdapter(configPath);
|
|
97
116
|
const phaseGateQueryPort = new PhaseGateQueryAdapter();
|
|
98
|
-
const
|
|
117
|
+
const storyReflectionQueryPort = new FileSystemStoryReflectionQueryAdapter({
|
|
118
|
+
rootDir: path.dirname(configPath),
|
|
119
|
+
configPath,
|
|
120
|
+
});
|
|
121
|
+
const useCase = new HandlePreToolUseUseCase({
|
|
122
|
+
configQueryPort,
|
|
123
|
+
phaseGateQueryPort,
|
|
124
|
+
storyReflectionQueryPort,
|
|
125
|
+
});
|
|
99
126
|
|
|
100
|
-
const output = await useCase.execute({ toolName, targetFilePaths });
|
|
127
|
+
const output = await useCase.execute({ toolName: effectiveToolName, targetFilePaths });
|
|
101
128
|
|
|
102
129
|
if (output.shouldBlock) {
|
|
103
130
|
const msg = output.error?.message
|
|
@@ -26,6 +26,9 @@ const toStringArray = (value: unknown, fallback: readonly string[]): readonly st
|
|
|
26
26
|
? Object.freeze([...value])
|
|
27
27
|
: fallback;
|
|
28
28
|
|
|
29
|
+
const isTestFile = (snapshot: SourceModuleSnapshot): boolean =>
|
|
30
|
+
snapshot.filePath.toString().includes('__tests__/');
|
|
31
|
+
|
|
29
32
|
export class LintRunner {
|
|
30
33
|
private readonly ruleDefinitionRegistry: RuleDefinitionRegistry;
|
|
31
34
|
|
|
@@ -98,6 +101,10 @@ export class LintRunner {
|
|
|
98
101
|
LayerBoundary.standardMatrix(),
|
|
99
102
|
layerByFile
|
|
100
103
|
)) {
|
|
104
|
+
const fromSnapshot = snapshotByPath.get(edge.from.toString());
|
|
105
|
+
if (fromSnapshot && isTestFile(fromSnapshot)) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
101
108
|
ruleViolations.push(
|
|
102
109
|
RuleViolation.create({
|
|
103
110
|
filePath: edge.from,
|
|
@@ -112,6 +119,10 @@ export class LintRunner {
|
|
|
112
119
|
|
|
113
120
|
for (const cycle of params.importGraph.detectCycles()) {
|
|
114
121
|
const firstPath = cycle.path[0];
|
|
122
|
+
const firstSnapshot = snapshotByPath.get(firstPath.toString());
|
|
123
|
+
if (firstSnapshot && isTestFile(firstSnapshot)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
115
126
|
ruleViolations.push(
|
|
116
127
|
RuleViolation.create({
|
|
117
128
|
filePath: firstPath,
|
|
@@ -127,6 +138,9 @@ export class LintRunner {
|
|
|
127
138
|
}
|
|
128
139
|
case 'enforce-folder-structure': {
|
|
129
140
|
for (const snapshot of params.snapshots) {
|
|
141
|
+
if (isTestFile(snapshot)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
130
144
|
if (snapshot.declaredLayer !== null && !snapshot.belongsToLayerDirectory()) {
|
|
131
145
|
ruleViolations.push(
|
|
132
146
|
RuleViolation.create({
|
|
@@ -147,6 +161,9 @@ export class LintRunner {
|
|
|
147
161
|
const maxAnyRatio = toNumber(rule.config.maxAnyRatio, 0.05);
|
|
148
162
|
|
|
149
163
|
for (const snapshot of params.snapshots) {
|
|
164
|
+
if (isTestFile(snapshot)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
150
167
|
if (
|
|
151
168
|
snapshot.anyTypeCount > maxAnyCount ||
|
|
152
169
|
snapshot.anyRatio() > maxAnyRatio
|
|
@@ -223,8 +240,15 @@ export class LintRunner {
|
|
|
223
240
|
case 'no-comment-flood': {
|
|
224
241
|
const maxCommentRatio = toNumber(rule.config.maxCommentRatio, 0.35);
|
|
225
242
|
const maxRepeatedBlocks = toNumber(rule.config.maxRepeatedBlocks, 1);
|
|
243
|
+
const minLogicalLines = toNumber(rule.config.minLogicalLines, 15);
|
|
226
244
|
|
|
227
245
|
for (const snapshot of params.snapshots) {
|
|
246
|
+
if (isTestFile(snapshot)) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (snapshot.logicalLineCount < minLogicalLines) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
228
252
|
if (
|
|
229
253
|
snapshot.commentDensity() > maxCommentRatio ||
|
|
230
254
|
snapshot.repeatedCommentBlocks > maxRepeatedBlocks
|
|
@@ -92,7 +92,7 @@ export class RuleDefinitionRegistry {
|
|
|
92
92
|
'no-comment-flood',
|
|
93
93
|
'L1-008',
|
|
94
94
|
Object.freeze([REQUIRED_INPUT.sourceModuleSnapshots]),
|
|
95
|
-
Object.freeze({ maxCommentRatio: 0.35, maxRepeatedBlocks: 1 }),
|
|
95
|
+
Object.freeze({ maxCommentRatio: 0.35, maxRepeatedBlocks: 1, minLogicalLines: 15 }),
|
|
96
96
|
'コメント過多を検出する',
|
|
97
97
|
'不要なコメントを整理し自己説明的なコードへ改善する'
|
|
98
98
|
),
|
|
@@ -59,8 +59,9 @@ export class SourceModuleSnapshot {
|
|
|
59
59
|
readonly duplicationFingerprints: readonly string[];
|
|
60
60
|
readonly exportedSymbols: readonly string[];
|
|
61
61
|
readonly isEntrypointCandidate: boolean;
|
|
62
|
+
readonly hasRawLayerAnnotation: boolean;
|
|
62
63
|
|
|
63
|
-
private constructor(props: Omit<SourceModuleSnapshotProps, 'declaredLayer'> & { declaredLayer: LayerName | null }) {
|
|
64
|
+
private constructor(props: Omit<SourceModuleSnapshotProps, 'declaredLayer'> & { declaredLayer: LayerName | null; hasRawLayerAnnotation: boolean }) {
|
|
64
65
|
this.filePath = props.filePath;
|
|
65
66
|
this.declaredUnit = props.declaredUnit;
|
|
66
67
|
this.declaredLayer = props.declaredLayer;
|
|
@@ -73,6 +74,7 @@ export class SourceModuleSnapshot {
|
|
|
73
74
|
this.duplicationFingerprints = props.duplicationFingerprints;
|
|
74
75
|
this.exportedSymbols = props.exportedSymbols;
|
|
75
76
|
this.isEntrypointCandidate = props.isEntrypointCandidate;
|
|
77
|
+
this.hasRawLayerAnnotation = props.hasRawLayerAnnotation;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
static create(props: SourceModuleSnapshotProps): SourceModuleSnapshot {
|
|
@@ -83,6 +85,7 @@ export class SourceModuleSnapshot {
|
|
|
83
85
|
filePath: props.filePath,
|
|
84
86
|
declaredUnit: props.declaredUnit,
|
|
85
87
|
declaredLayer,
|
|
88
|
+
hasRawLayerAnnotation: props.declaredLayer !== null,
|
|
86
89
|
imports: Object.freeze([...props.imports]),
|
|
87
90
|
anyTypeCount: ensureNonNegative(props.anyTypeCount, 'anyTypeCount'),
|
|
88
91
|
typedNodeCount: ensureNonNegative(props.typedNodeCount, 'typedNodeCount'),
|
|
@@ -104,7 +107,7 @@ export class SourceModuleSnapshot {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
hasLayerComment(): boolean {
|
|
107
|
-
return this.
|
|
110
|
+
return this.hasRawLayerAnnotation;
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
anyRatio(): number {
|
package/scripts/harness/biome-ast-engine/infrastructure/adapters/node-workspace-file-adapter.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { FilePath as FilePathVO } from '../../domain/value-objects/file-path.js'
|
|
|
11
11
|
import type { WorkspaceFilePort } from '../../domain/ports/workspace-file-port.js';
|
|
12
12
|
|
|
13
13
|
const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);
|
|
14
|
-
const EXCLUDED_DIRS = new Set(['node_modules', 'dist', 'coverage', '__fixtures__']);
|
|
14
|
+
const EXCLUDED_DIRS = new Set(['node_modules', 'dist', 'coverage', '__fixtures__', 'fixtures']);
|
|
15
15
|
|
|
16
16
|
export interface NodeWorkspaceFileAdapterDeps {
|
|
17
17
|
readonly rootDir: string;
|
|
@@ -64,7 +64,7 @@ export class TypeScriptSourceModuleAnalyzerAdapter implements SourceModuleAnalyz
|
|
|
64
64
|
snapshots.push(
|
|
65
65
|
SourceModuleSnapshot.create({
|
|
66
66
|
filePath,
|
|
67
|
-
declaredUnit: unitResult.
|
|
67
|
+
declaredUnit: unitResult.unitNames[0],
|
|
68
68
|
declaredLayer: layerResult.layerName,
|
|
69
69
|
imports,
|
|
70
70
|
anyTypeCount: anyCount,
|
|
@@ -3,24 +3,29 @@
|
|
|
3
3
|
* @unit biome-ast-engine
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const UNIT_COMMENT_PATTERN = /^\s*(?:\/\/|\/\*\*?\s*|\*)\s*@unit\s+(
|
|
6
|
+
const UNIT_COMMENT_PATTERN = /^\s*(?:\/\/|\/\*\*?\s*|\*)\s*@unit\s+(.+)/gm;
|
|
7
7
|
|
|
8
8
|
export type UnitCommentResult = {
|
|
9
|
-
readonly
|
|
9
|
+
readonly unitNames: readonly string[];
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* ソースコードから `// @unit {unit}` コメントを抽出する。
|
|
14
14
|
* JSDoc (`/** @unit ... * /`) 形式にも対応する。
|
|
15
|
+
* カンマ区切りで複数ユニットを指定でき、複数行の @unit も収集する。
|
|
16
|
+
* 結果は重複除去済み。
|
|
15
17
|
*/
|
|
16
18
|
export const parseUnitComment = (sourceCode: string): UnitCommentResult => {
|
|
17
|
-
const
|
|
19
|
+
const matches = sourceCode.matchAll(UNIT_COMMENT_PATTERN);
|
|
20
|
+
const names: string[] = [];
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
for (const match of matches) {
|
|
23
|
+
const raw = match[1].replace(/\s*\*\/$/, '').trim();
|
|
24
|
+
const parts = raw.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
25
|
+
names.push(...parts);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
const
|
|
28
|
+
const deduplicated = [...new Set(names)];
|
|
24
29
|
|
|
25
|
-
return {
|
|
30
|
+
return { unitNames: Object.freeze(deduplicated) };
|
|
26
31
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @layer application
|
|
3
|
+
* @unit config-foundation
|
|
4
|
+
*/
|
|
5
|
+
import type { HarnessConfigV2 } from '../../domain/harness-config.js';
|
|
6
|
+
|
|
7
|
+
function toPhasePreset(
|
|
8
|
+
value: string,
|
|
9
|
+
): 'default' | 'full' | 'standard' | 'minimal' | 'custom' {
|
|
10
|
+
if (
|
|
11
|
+
value === 'default'
|
|
12
|
+
|| value === 'full'
|
|
13
|
+
|| value === 'standard'
|
|
14
|
+
|| value === 'minimal'
|
|
15
|
+
|| value === 'custom'
|
|
16
|
+
) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return 'default';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type PhaseDependenciesWithGates = HarnessConfigV2['phaseDependencies'] & {
|
|
24
|
+
readonly gates?: readonly unknown[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function toPhaseConfigSection(resolvedConfig: HarnessConfigV2) {
|
|
28
|
+
const phaseDependencies = resolvedConfig.phaseDependencies as PhaseDependenciesWithGates;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
planningMode: resolvedConfig.planningMode,
|
|
32
|
+
customization: {
|
|
33
|
+
preset: toPhasePreset(phaseDependencies.preset),
|
|
34
|
+
overrideEnabled: phaseDependencies.override,
|
|
35
|
+
rules: phaseDependencies.customRules.map((rule) => ({
|
|
36
|
+
targetPhase: rule.phase,
|
|
37
|
+
condition: 'requires' as const,
|
|
38
|
+
action: rule.requires,
|
|
39
|
+
})),
|
|
40
|
+
gates: phaseDependencies.gates ?? [],
|
|
41
|
+
},
|
|
42
|
+
reportingOutputDir: resolvedConfig.reporting.outputDir,
|
|
43
|
+
};
|
|
44
|
+
}
|
package/scripts/harness/config-foundation/application/usecases/load-resolved-config-use-case.ts
CHANGED
|
@@ -42,7 +42,26 @@ function formatHarnessErrors(errors: readonly HarnessError[]): string {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function toSourceDocument(document: unknown): HarnessConfigSourceDocument {
|
|
45
|
-
|
|
45
|
+
const sourceDocument = document as HarnessConfigSourceDocument & {
|
|
46
|
+
readonly phaseDependencies?: HarnessConfigSourceDocument['phaseDependencies'] & {
|
|
47
|
+
readonly gates?: readonly unknown[];
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
sourceDocument.phaseDependencies === undefined
|
|
53
|
+
|| sourceDocument.phaseDependencies.gates === undefined
|
|
54
|
+
) {
|
|
55
|
+
return sourceDocument;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
...sourceDocument,
|
|
60
|
+
phaseDependencies: {
|
|
61
|
+
...sourceDocument.phaseDependencies,
|
|
62
|
+
gates: sourceDocument.phaseDependencies.gates ?? [],
|
|
63
|
+
},
|
|
64
|
+
} as HarnessConfigSourceDocument;
|
|
46
65
|
}
|
|
47
66
|
|
|
48
67
|
export function validateDocumentOrThrow(
|
|
@@ -23,6 +23,7 @@ import type { L4Config } from './value-objects/l4-config.js';
|
|
|
23
23
|
export type LayerId = 'L1' | 'L2' | 'L3' | 'L4';
|
|
24
24
|
export type PresetId = 'minimal' | 'standard' | 'strict';
|
|
25
25
|
export type PlanningModeValue = 'interactive' | 'embedded-qa';
|
|
26
|
+
export type PhaseDependenciesPresetId = 'default' | 'full' | 'standard' | 'minimal' | 'custom';
|
|
26
27
|
type DeepPartial<T> = {
|
|
27
28
|
[K in keyof T]?: T[K] extends Array<infer TItem>
|
|
28
29
|
? Array<DeepPartial<TItem>>
|
|
@@ -76,7 +77,7 @@ export interface HarnessConfigResolvedDocument {
|
|
|
76
77
|
relaxedGates: string[];
|
|
77
78
|
};
|
|
78
79
|
phaseDependencies: {
|
|
79
|
-
preset:
|
|
80
|
+
preset: PhaseDependenciesPresetId;
|
|
80
81
|
override: boolean;
|
|
81
82
|
customRules: Array<{
|
|
82
83
|
phase: string;
|
|
@@ -7,17 +7,20 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { CustomPhaseRule } from './custom-phase-rule.js';
|
|
9
9
|
import type { CustomPhaseRuleProps } from './custom-phase-rule.js';
|
|
10
|
+
import type { PhaseDependenciesPresetId } from '../harness-config.js';
|
|
10
11
|
|
|
11
12
|
export interface PhaseDependenciesConfigProps {
|
|
12
|
-
readonly preset:
|
|
13
|
+
readonly preset: PhaseDependenciesPresetId;
|
|
13
14
|
readonly override: boolean;
|
|
14
15
|
readonly customRules: readonly CustomPhaseRuleProps[];
|
|
16
|
+
readonly gates?: readonly unknown[];
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export class PhaseDependenciesConfig {
|
|
18
|
-
readonly preset:
|
|
20
|
+
readonly preset: PhaseDependenciesPresetId;
|
|
19
21
|
readonly override: boolean;
|
|
20
22
|
readonly customRules: readonly CustomPhaseRule[];
|
|
23
|
+
readonly gates: readonly unknown[];
|
|
21
24
|
|
|
22
25
|
constructor(props: PhaseDependenciesConfigProps) {
|
|
23
26
|
this.preset = props.preset;
|
|
@@ -25,6 +28,7 @@ export class PhaseDependenciesConfig {
|
|
|
25
28
|
this.customRules = props.customRules.map(
|
|
26
29
|
(rule) => new CustomPhaseRule(rule)
|
|
27
30
|
);
|
|
31
|
+
this.gates = Object.freeze([...(props.gates ?? [])]);
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
static create(raw: PhaseDependenciesConfigProps): PhaseDependenciesConfig {
|
|
@@ -35,12 +39,22 @@ export class PhaseDependenciesConfig {
|
|
|
35
39
|
return this.customRules.length > 0;
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
hasCustomGates(): boolean {
|
|
43
|
+
return this.gates.length > 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
equals(other: PhaseDependenciesConfig): boolean {
|
|
39
47
|
if (this.preset !== other.preset) return false;
|
|
40
48
|
if (this.override !== other.override) return false;
|
|
41
49
|
if (this.customRules.length !== other.customRules.length) return false;
|
|
42
|
-
|
|
50
|
+
if (
|
|
51
|
+
!this.customRules.every((rule, idx) =>
|
|
43
52
|
rule.equals(other.customRules[idx])
|
|
44
|
-
|
|
53
|
+
)
|
|
54
|
+
) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return JSON.stringify(this.gates) === JSON.stringify(other.gates);
|
|
45
59
|
}
|
|
46
60
|
}
|