phasegate 0.15.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.ja.md +103 -71
  2. package/README.md +73 -13
  3. package/bin/{harness → phasegate} +1 -1
  4. package/docs/ADR/ADR-013-story-reflection-gate.md +100 -0
  5. package/docs/guide/cli-reference.md +9 -9
  6. package/docs/guide/configuration.md +245 -8
  7. package/docs/guide/hooks-integration.md +1 -1
  8. package/docs/guide/installation.md +6 -6
  9. package/docs/guide/layer-model.md +5 -5
  10. package/docs/guide/skills-overview.md +1 -1
  11. package/package.json +12 -10
  12. package/scripts/harness/adr-foundation/domain/ports/adr-document-parser-port.ts +1 -0
  13. package/scripts/harness/adr-foundation/domain/ports/adr-frontmatter-parser-port.ts +1 -0
  14. package/scripts/harness/adr-foundation/domain/ports/adr-repository-port.ts +1 -0
  15. package/scripts/harness/adr-foundation/domain/value-objects/adr-status.ts +6 -6
  16. package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +3 -1
  17. package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +109 -3
  18. package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +1 -1
  19. package/scripts/harness/agent-integration/domain/ports/config-query-port.ts +2 -0
  20. package/scripts/harness/agent-integration/domain/ports/phase-gate-query-port.ts +1 -1
  21. package/scripts/harness/agent-integration/domain/ports/story-reflection-query-port.ts +8 -0
  22. package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +258 -0
  23. package/scripts/harness/agent-integration/domain/services/hook-to-cli-translator.ts +25 -8
  24. package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +20 -0
  25. package/scripts/harness/agent-integration/domain/value-objects/story-reflection-query-result.ts +63 -0
  26. package/scripts/harness/agent-integration/infrastructure/adapters/child-process-cli-executor-adapter.ts +2 -2
  27. package/scripts/harness/agent-integration/infrastructure/adapters/file-system-story-reflection-query-adapter.ts +122 -0
  28. package/scripts/harness/agent-integration/infrastructure/adapters/harness-api-cli-command-registry-adapter.ts +10 -10
  29. package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +20 -0
  30. package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +17 -3
  31. package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +29 -2
  32. package/scripts/harness/biome-ast-engine/domain/services/lint-runner.ts +24 -0
  33. package/scripts/harness/biome-ast-engine/domain/services/rule-definition-registry.ts +1 -1
  34. package/scripts/harness/biome-ast-engine/domain/value-objects/source-module-snapshot.ts +5 -2
  35. package/scripts/harness/biome-ast-engine/infrastructure/adapters/node-workspace-file-adapter.ts +1 -1
  36. package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +1 -1
  37. package/scripts/harness/biome-ast-engine/infrastructure/parsers/unit-comment-parser.ts +12 -7
  38. package/scripts/harness/ci-governance/composition-root.ts +1 -0
  39. package/scripts/harness/ci-governance/infrastructure/adapters/agents-md-file-adapter.ts +2 -2
  40. package/scripts/harness/ci-governance/infrastructure/adapters/yaml-template-renderer-adapter.ts +2 -2
  41. package/scripts/harness/config-foundation/application/mappers/phase-config-section-mapper.ts +44 -0
  42. package/scripts/harness/config-foundation/application/usecases/load-resolved-config-use-case.ts +20 -1
  43. package/scripts/harness/config-foundation/domain/harness-config.ts +2 -1
  44. package/scripts/harness/config-foundation/domain/value-objects/phase-dependencies-config.ts +18 -4
  45. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +185 -10
  46. package/scripts/harness/config-foundation/infrastructure/validators/ajv-config-schema-validator.ts +2 -1
  47. package/scripts/harness/harness-api/application/dto/command-dispatch-input.ts +1 -0
  48. package/scripts/harness/harness-api/application/dto/command-dispatch-output.ts +1 -0
  49. package/scripts/harness/harness-api/application/dto/exit-code-decision-input.ts +1 -0
  50. package/scripts/harness/harness-api/application/dto/exit-code-decision-output.ts +1 -0
  51. package/scripts/harness/harness-api/application/dto/harness-api-response-contract.ts +1 -0
  52. package/scripts/harness/harness-api/application/dto/initialize-registry-input.ts +1 -0
  53. package/scripts/harness/harness-api/application/dto/registry-summary-output.ts +1 -0
  54. package/scripts/harness/harness-api/application/dto/status-derivation-input.ts +1 -0
  55. package/scripts/harness/harness-api/application/mappers/harness-api-response-mapper.ts +1 -0
  56. package/scripts/harness/harness-api/application/usecases/decide-exit-code-usecase.ts +4 -3
  57. package/scripts/harness/harness-api/application/usecases/derive-harness-status-usecase.ts +1 -0
  58. package/scripts/harness/harness-api/application/usecases/dispatch-command-usecase.ts +1 -0
  59. package/scripts/harness/harness-api/application/usecases/initialize-command-registry-usecase.ts +1 -0
  60. package/scripts/harness/harness-api/composition-root.ts +1 -0
  61. package/scripts/harness/harness-api/domain/ports/artifact-scanner-port.ts +1 -0
  62. package/scripts/harness/harness-api/domain/ports/biome-lint-port.ts +1 -0
  63. package/scripts/harness/harness-api/domain/ports/config-query-port.ts +1 -0
  64. package/scripts/harness/harness-api/domain/ports/impact-analysis-port.ts +1 -0
  65. package/scripts/harness/harness-api/domain/ports/phase-gate-query-port.ts +1 -0
  66. package/scripts/harness/harness-api/domain/ports/validator-execution-port.ts +1 -0
  67. package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +17 -16
  68. package/scripts/harness/harness-api/domain/services/command-registry.ts +4 -3
  69. package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +1 -0
  70. package/scripts/harness/harness-api/domain/value-objects/artifact-scan-result.ts +1 -0
  71. package/scripts/harness/harness-api/domain/value-objects/check-ready-result.ts +1 -0
  72. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +1 -0
  73. package/scripts/harness/harness-api/domain/value-objects/cli-command-definition.ts +11 -10
  74. package/scripts/harness/harness-api/domain/value-objects/command-input-spec.ts +1 -0
  75. package/scripts/harness/harness-api/domain/value-objects/drift-report-summary.ts +1 -0
  76. package/scripts/harness/harness-api/domain/value-objects/exit-code-spec.ts +1 -0
  77. package/scripts/harness/harness-api/domain/value-objects/harness-api-response.ts +1 -0
  78. package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +1 -0
  79. package/scripts/harness/harness-api/domain/value-objects/layer-health.ts +1 -0
  80. package/scripts/harness/harness-api/domain/value-objects/phase-info.ts +1 -0
  81. package/scripts/harness/harness-api/index.ts +1 -0
  82. package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +1 -0
  83. package/scripts/harness/harness-api/infrastructure/adapters/file-system-artifact-scanner-adapter.ts +1 -0
  84. package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
  85. package/scripts/harness/harness-api/infrastructure/adapters/nyquist-validation-impact-analysis-adapter.ts +1 -0
  86. package/scripts/harness/harness-api/infrastructure/adapters/phase-dependency-model-query-adapter.ts +1 -0
  87. package/scripts/harness/harness-api/infrastructure/adapters/validator-system-execution-adapter.ts +1 -0
  88. package/scripts/harness/harness-api/presentation/dto/cli-output-options.ts +1 -0
  89. package/scripts/harness/harness-api/presentation/formatters/harness-api-json-formatter.ts +1 -0
  90. package/scripts/harness/harness-api/presentation/handlers/check-phase-handler.ts +2 -1
  91. package/scripts/harness/harness-api/presentation/handlers/check-ready-handler.ts +2 -1
  92. package/scripts/harness/harness-api/presentation/handlers/ci-check-handler.ts +2 -1
  93. package/scripts/harness/harness-api/presentation/handlers/complete-check-handler.ts +2 -1
  94. package/scripts/harness/harness-api/presentation/handlers/detect-drift-handler.ts +2 -1
  95. package/scripts/harness/harness-api/presentation/handlers/impact-analysis-handler.ts +2 -1
  96. package/scripts/harness/harness-api/presentation/handlers/lint-handler.ts +2 -1
  97. package/scripts/harness/harness-api/presentation/handlers/status-handler.ts +4 -3
  98. package/scripts/harness/harness-error/infrastructure/adapters/legacy-error-reporter-adapter.ts +7 -1
  99. package/scripts/harness/integrations/pre-commit.ts +1 -0
  100. package/scripts/harness/main.ts +173 -53
  101. package/scripts/harness/phase-dependency-model/application/dto/resolve-gate-result-dto.ts +14 -0
  102. package/scripts/harness/phase-dependency-model/application/services/phase-gate-result-mapper.ts +20 -0
  103. package/scripts/harness/phase-dependency-model/application/usecases/check-story-reflection-usecase.ts +42 -0
  104. package/scripts/harness/phase-dependency-model/application/usecases/resolve-gate-usecase.ts +161 -0
  105. package/scripts/harness/phase-dependency-model/composition-root.ts +57 -1
  106. package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-dependencies.ts → full-phase-dependencies.ts} +3 -3
  107. package/scripts/harness/phase-dependency-model/domain/definitions/{default-phase-nodes.ts → full-phase-nodes.ts} +1 -1
  108. package/scripts/harness/phase-dependency-model/domain/definitions/full-story-reflection-defaults.ts +28 -0
  109. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-dependencies.ts +32 -0
  110. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-phase-nodes.ts +50 -0
  111. package/scripts/harness/phase-dependency-model/domain/definitions/minimal-story-reflection-defaults.ts +8 -0
  112. package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-dependencies.ts +39 -0
  113. package/scripts/harness/phase-dependency-model/domain/definitions/standard-phase-nodes.ts +118 -0
  114. package/scripts/harness/phase-dependency-model/domain/definitions/standard-story-reflection-defaults.ts +23 -0
  115. package/scripts/harness/phase-dependency-model/domain/models/phase-structure.ts +95 -23
  116. package/scripts/harness/phase-dependency-model/domain/ports/glob-matcher-port.ts +8 -0
  117. package/scripts/harness/phase-dependency-model/domain/ports/phase-config-provider-port.ts +2 -0
  118. package/scripts/harness/phase-dependency-model/domain/ports/story-annotation-verifier-port.ts +14 -0
  119. package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +16 -0
  120. package/scripts/harness/phase-dependency-model/domain/services/gate-graph.ts +223 -0
  121. package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +66 -0
  122. package/scripts/harness/phase-dependency-model/domain/values/gate-definition.ts +127 -0
  123. package/scripts/harness/phase-dependency-model/domain/values/gate-name.ts +40 -0
  124. package/scripts/harness/phase-dependency-model/domain/values/gate-story-annotation.ts +39 -0
  125. package/scripts/harness/phase-dependency-model/domain/values/phase-customization-policy.ts +16 -4
  126. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-config.ts +44 -0
  127. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-mapping.ts +91 -0
  128. package/scripts/harness/phase-dependency-model/domain/values/story-reflection-result.ts +55 -0
  129. package/scripts/harness/phase-dependency-model/infrastructure/adapters/picomatch-glob-matcher.ts +17 -0
  130. package/scripts/harness/phase-dependency-model/infrastructure/adapters/story-annotation-verifier-adapter.ts +46 -0
  131. package/scripts/harness/phase-dependency-model/infrastructure/config/custom-gates-config-parser.ts +22 -0
  132. package/scripts/harness/phase-dependency-model/infrastructure/config/harness-config-phase-config-provider.ts +78 -2
  133. package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +94 -0
  134. package/scripts/harness/phase-dependency-model/presentation/cli/check-phase-gate-command-handler.ts +12 -4
  135. package/scripts/harness/phase-dependency-model/presentation/cli/story-reflection-status-presenter.ts +87 -0
  136. package/scripts/harness/quick-mode/application/usecases/execute-quick-ci-check-usecase.ts +1 -1
  137. package/scripts/harness/quick-mode/presentation/handlers/ci-check-quick-mode-handler.ts +1 -1
  138. package/scripts/harness/regression-suite/application/dto/analyze-migration-input.ts +1 -0
  139. package/scripts/harness/regression-suite/application/dto/analyze-migration-output.ts +1 -0
  140. package/scripts/harness/regression-suite/application/dto/configure-ci-gate-input.ts +1 -0
  141. package/scripts/harness/regression-suite/application/dto/configure-ci-gate-output.ts +1 -0
  142. package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-input.ts +1 -0
  143. package/scripts/harness/regression-suite/application/dto/migrate-v0-tests-output.ts +1 -0
  144. package/scripts/harness/regression-suite/application/dto/run-regression-suite-input.ts +1 -0
  145. package/scripts/harness/regression-suite/application/dto/run-regression-suite-output.ts +1 -0
  146. package/scripts/harness/regression-suite/application/mappers/migration-mapping-mapper.ts +1 -0
  147. package/scripts/harness/regression-suite/application/mappers/test-execution-summary-mapper.ts +1 -0
  148. package/scripts/harness/regression-suite/application/usecases/analyze-v0-migration-usecase.ts +1 -0
  149. package/scripts/harness/regression-suite/application/usecases/configure-ci-gate-usecase.ts +1 -0
  150. package/scripts/harness/regression-suite/application/usecases/migrate-v0-tests-usecase.ts +1 -0
  151. package/scripts/harness/regression-suite/application/usecases/run-agent-independence-guard-usecase.ts +1 -0
  152. package/scripts/harness/regression-suite/application/usecases/run-gng-gate-regression-usecase.ts +1 -0
  153. package/scripts/harness/regression-suite/application/usecases/run-k-requirements-regression-usecase.ts +1 -0
  154. package/scripts/harness/regression-suite/application/usecases/run-k14-k15-regression-usecase.ts +1 -0
  155. package/scripts/harness/regression-suite/composition-root.ts +1 -0
  156. package/scripts/harness/regression-suite/domain/aggregates/v0-test-migration.ts +1 -0
  157. package/scripts/harness/regression-suite/domain/ports/ci-gate-result-writer-port.ts +1 -0
  158. package/scripts/harness/regression-suite/domain/ports/config-query-port.ts +1 -0
  159. package/scripts/harness/regression-suite/domain/ports/import-analyzer-port.ts +1 -0
  160. package/scripts/harness/regression-suite/domain/ports/migration-mapping-repository-port.ts +1 -0
  161. package/scripts/harness/regression-suite/domain/ports/suite-registry-port.ts +1 -0
  162. package/scripts/harness/regression-suite/domain/ports/test-runner-port.ts +1 -0
  163. package/scripts/harness/regression-suite/domain/ports/v0-spec-reader-port.ts +1 -0
  164. package/scripts/harness/regression-suite/domain/services/import-guard-service.ts +1 -0
  165. package/scripts/harness/regression-suite/domain/services/migration-analyzer.ts +1 -0
  166. package/scripts/harness/regression-suite/domain/services/regression-runner.ts +1 -0
  167. package/scripts/harness/regression-suite/domain/value-objects/agent-independence-test.ts +1 -0
  168. package/scripts/harness/regression-suite/domain/value-objects/biome-modification-spec.ts +1 -0
  169. package/scripts/harness/regression-suite/domain/value-objects/ci-gate-config.ts +1 -0
  170. package/scripts/harness/regression-suite/domain/value-objects/coverage-rate.ts +1 -0
  171. package/scripts/harness/regression-suite/domain/value-objects/gng-condition-test.ts +1 -0
  172. package/scripts/harness/regression-suite/domain/value-objects/import-violation.ts +1 -0
  173. package/scripts/harness/regression-suite/domain/value-objects/k-requirement-test.ts +1 -0
  174. package/scripts/harness/regression-suite/domain/value-objects/migration-mapping.ts +1 -0
  175. package/scripts/harness/regression-suite/domain/value-objects/regression-suite-definition.ts +1 -0
  176. package/scripts/harness/regression-suite/domain/value-objects/suite-id.ts +1 -0
  177. package/scripts/harness/regression-suite/domain/value-objects/test-execution-summary.ts +1 -0
  178. package/scripts/harness/regression-suite/domain/value-objects/test-failure-detail.ts +1 -0
  179. package/scripts/harness/regression-suite/domain/value-objects/v0-test-id.ts +1 -0
  180. package/scripts/harness/regression-suite/domain/value-objects/v1-test-path.ts +1 -0
  181. package/scripts/harness/regression-suite/infrastructure/adapters/biome-ast-import-analyzer-adapter.ts +1 -0
  182. package/scripts/harness/regression-suite/infrastructure/adapters/file-system-v0-spec-reader-adapter.ts +1 -0
  183. package/scripts/harness/regression-suite/infrastructure/adapters/harness-config-query-adapter.ts +1 -0
  184. package/scripts/harness/regression-suite/infrastructure/adapters/json-ci-gate-result-writer-adapter.ts +1 -0
  185. package/scripts/harness/regression-suite/infrastructure/adapters/markdown-migration-mapping-repository-adapter.ts +1 -0
  186. package/scripts/harness/regression-suite/infrastructure/adapters/static-suite-registry-adapter.ts +1 -0
  187. package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +1 -0
  188. package/scripts/harness/regression-suite/infrastructure/registry/agent-independence-suite-definition.ts +1 -0
  189. package/scripts/harness/regression-suite/infrastructure/registry/gng-gate-suite-definition.ts +1 -0
  190. package/scripts/harness/regression-suite/infrastructure/registry/k-requirements-suite-definition.ts +1 -0
  191. package/scripts/harness/regression-suite/infrastructure/registry/v0-migration-suite-definition.ts +1 -0
  192. package/scripts/harness/setup/skill-deployer.ts +82 -5
  193. package/scripts/harness/shared-kernel/harness-api.ts +1 -0
  194. package/scripts/harness/skill-quality/composition-root.ts +1 -0
  195. package/scripts/harness/skill-quality/infrastructure/adapters/ajv-lesson-artifact-schema-adapter.ts +5 -4
  196. package/scripts/harness/traceability-model/domain/services/metadata-validator.ts +12 -10
  197. package/scripts/harness/traceability-model/infrastructure/parsers/source-metadata-parser.ts +16 -2
  198. package/scripts/harness/validator-system/composition-root.ts +3 -3
  199. package/skills/cascade-updater/SKILL.md +16 -3
  200. package/skills/codebase-mapper/SKILL.md +9 -9
  201. package/skills/codex-delegator/SKILL.md +5 -56
  202. package/skills/consistency-checker/SKILL.md +5 -9
  203. package/skills/consistency-checker/references//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +6 -3
  204. package/skills/doc-freshness-checker/SKILL.md +6 -6
  205. package/skills/engineering-perspective/SKILL.md +2 -58
  206. package/skills/engineering-perspective/references/domain-model.md +4 -135
  207. package/skills/implementation-readiness-checker/SKILL.md +18 -72
  208. package/skills/it-test-designer/SKILL.md +25 -161
  209. package/skills/it-test-designer/references/it-test-design-template.md +131 -0
  210. package/skills/it-test-logic-designer/SKILL.md +13 -388
  211. package/skills/it-test-logic-designer/references/controller-test-patterns.md +100 -0
  212. package/skills/it-test-logic-designer/references/repository-test-patterns.md +206 -0
  213. package/skills/it-test-logic-designer/references/usecase-test-patterns.md +87 -0
  214. package/skills/pointer-validator/SKILL.md +4 -4
  215. package/skills/scenario-test-designer/SKILL.md +4 -68
  216. package/skills/scenario-test-designer/references/scenario-test-design-template.md +41 -0
  217. package/skills/scenario-test-designer/references/scenario-test-plan-template.md +35 -0
  218. package/skills/scenario-test-logic-designer/SKILL.md +21 -304
  219. package/skills/scenario-test-logic-designer/references/msw-patterns.md +36 -0
  220. package/skills/scenario-test-logic-designer/references/playwright-patterns.md +237 -0
  221. package/skills/test-coverage-checker/SKILL.md +39 -121
  222. package/skills/test-coverage-checker/references/coverage-report-template.md +99 -0
  223. package/skills/uiux-designer/SKILL.md +27 -92
  224. package/skills/uiux-designer/references/data-testid-convention.md +38 -0
  225. package/skills/uiux-designer/references/uiux-design-template.md +105 -0
  226. package/skills/unit-test-designer/SKILL.md +5 -106
  227. package/skills/unit-test-designer/references/unit-test-design-template.md +127 -0
  228. package/skills/unit-test-logic-designer/SKILL.md +28 -250
  229. package/skills/unit-test-logic-designer/references/test-patterns.md +207 -0
@@ -5,5 +5,5 @@ import type { WriteTargetScope } from '../value-objects/write-target-scope.js';
5
5
  import type { PhaseGateQueryResult } from '../value-objects/phase-gate-query-result.js';
6
6
 
7
7
  export interface PhaseGateQueryPort {
8
- checkGate(scope: WriteTargetScope): Promise<PhaseGateQueryResult>;
8
+ checkGate(scope: WriteTargetScope, targetFilePath?: string): Promise<PhaseGateQueryResult>;
9
9
  }
@@ -0,0 +1,8 @@
1
+ // @unit agent-integration
2
+ // @layer domain
3
+
4
+ import type { StoryReflectionQueryResult } from '../value-objects/story-reflection-query-result.js';
5
+
6
+ export interface StoryReflectionQueryPort {
7
+ checkReflection(unitId: string): Promise<StoryReflectionQueryResult>;
8
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit agent-integration
4
+ *
5
+ * BashWriteTargetExtractor ドメインサービス
6
+ *
7
+ * Bash コマンド文字列を静的解析し、書き込み対象となるファイルパスを抽出する。
8
+ * Phase Gate フックが Bash 経由のファイル書き込みを検知して保護するための基盤。
9
+ *
10
+ * 副作用なし・純粋関数相当。対応パターンは以下:
11
+ * - リダイレクト `>` / `>>`
12
+ * - heredoc (`<<EOF > path`)
13
+ * - `tee` / `tee -a`
14
+ * - `sed -i` / `sed -i ''` (BSD)
15
+ * - `cp` / `mv` (宛先のみ)
16
+ * - `touch`
17
+ * - 複合コマンド (`&&`, `;`, `||`) とパイプ (`|`) 分割
18
+ * - ダブル/シングルクォート対応
19
+ */
20
+
21
+ /** 引数トークン (値とクォート種別) */
22
+ type Token = {
23
+ readonly value: string;
24
+ readonly quoted: 'none' | 'single' | 'double';
25
+ };
26
+
27
+ /** シェル風の簡易トークナイザ。クォート境界を尊重する。 */
28
+ function tokenize(input: string): Token[] {
29
+ const tokens: Token[] = [];
30
+ let i = 0;
31
+ const len = input.length;
32
+
33
+ while (i < len) {
34
+ const ch = input[i];
35
+
36
+ // 空白スキップ
37
+ if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
38
+ i += 1;
39
+ continue;
40
+ }
41
+
42
+ // 演算子をそのままトークン化
43
+ if (ch === '>' || ch === '<' || ch === '|' || ch === ';' || ch === '&') {
44
+ let op = ch;
45
+ if (i + 1 < len && input[i + 1] === ch && (ch === '>' || ch === '<' || ch === '|' || ch === '&')) {
46
+ op = ch + ch;
47
+ i += 2;
48
+ } else {
49
+ i += 1;
50
+ }
51
+ tokens.push({ value: op, quoted: 'none' });
52
+ continue;
53
+ }
54
+
55
+ // ダブルクォート
56
+ if (ch === '"') {
57
+ let buf = '';
58
+ i += 1;
59
+ while (i < len && input[i] !== '"') {
60
+ if (input[i] === '\\' && i + 1 < len) {
61
+ buf += input[i + 1];
62
+ i += 2;
63
+ } else {
64
+ buf += input[i];
65
+ i += 1;
66
+ }
67
+ }
68
+ i += 1; // closing quote
69
+ tokens.push({ value: buf, quoted: 'double' });
70
+ continue;
71
+ }
72
+
73
+ // シングルクォート
74
+ if (ch === "'") {
75
+ let buf = '';
76
+ i += 1;
77
+ while (i < len && input[i] !== "'") {
78
+ buf += input[i];
79
+ i += 1;
80
+ }
81
+ i += 1; // closing quote
82
+ tokens.push({ value: buf, quoted: 'single' });
83
+ continue;
84
+ }
85
+
86
+ // 通常トークン
87
+ let buf = '';
88
+ while (i < len) {
89
+ const c = input[i];
90
+ if (c === ' ' || c === '\t' || c === '\n' || c === '\r') break;
91
+ if (c === '>' || c === '<' || c === '|' || c === ';' || c === '&') break;
92
+ if (c === '"' || c === "'") break;
93
+ buf += c;
94
+ i += 1;
95
+ }
96
+ if (buf.length > 0) {
97
+ tokens.push({ value: buf, quoted: 'none' });
98
+ }
99
+ }
100
+
101
+ return tokens;
102
+ }
103
+
104
+ const OPERATOR_SEPARATORS = new Set(['&&', '||', ';', '|']);
105
+
106
+ /** トークン列を複合・パイプ境界で分割 */
107
+ function splitByOperators(tokens: Token[]): Token[][] {
108
+ const groups: Token[][] = [];
109
+ let current: Token[] = [];
110
+ for (const t of tokens) {
111
+ if (t.quoted === 'none' && OPERATOR_SEPARATORS.has(t.value)) {
112
+ if (current.length > 0) {
113
+ groups.push(current);
114
+ current = [];
115
+ }
116
+ continue;
117
+ }
118
+ current.push(t);
119
+ }
120
+ if (current.length > 0) groups.push(current);
121
+ return groups;
122
+ }
123
+
124
+ /** コマンドグループ先頭の環境変数代入 (`FOO=bar`) をスキップして実コマンドを返す */
125
+ function getCommandName(tokens: Token[]): string | undefined {
126
+ for (const t of tokens) {
127
+ if (t.quoted === 'none' && /^[A-Za-z_][A-Za-z0-9_]*=/.test(t.value)) continue;
128
+ return t.value;
129
+ }
130
+ return undefined;
131
+ }
132
+
133
+ /**
134
+ * 1 コマンド分のトークン列から書き込み先を抽出する。
135
+ * リダイレクト先はコマンド種別によらず検出する (全コマンド共通)。
136
+ */
137
+ function extractFromSingleCommand(tokens: Token[]): string[] {
138
+ const results: string[] = [];
139
+
140
+ // 1) リダイレクト `>` / `>>` の右辺 (heredoc の `>` もこのパスで拾える)
141
+ for (let i = 0; i < tokens.length; i += 1) {
142
+ const t = tokens[i];
143
+ if (t.quoted === 'none' && (t.value === '>' || t.value === '>>')) {
144
+ const next = tokens[i + 1];
145
+ if (next !== undefined) {
146
+ results.push(next.value);
147
+ }
148
+ }
149
+ }
150
+
151
+ const cmd = getCommandName(tokens);
152
+ if (cmd === undefined) return results;
153
+
154
+ // コマンド別の特殊処理
155
+ const baseName = cmd.split('/').pop() ?? cmd;
156
+
157
+ if (baseName === 'tee') {
158
+ // `tee [-a] FILE...`
159
+ for (let i = 1; i < tokens.length; i += 1) {
160
+ const t = tokens[i];
161
+ if (t.quoted === 'none' && t.value.startsWith('-')) continue;
162
+ if (t.quoted === 'none' && (t.value === '>' || t.value === '>>' || t.value === '|' || t.value === '<')) break;
163
+ results.push(t.value);
164
+ }
165
+ } else if (baseName === 'sed') {
166
+ // `sed -i [''] SCRIPT FILE` — `-i` の直後の空文字列 (BSD) はバックアップ拡張子
167
+ const argTokens = tokens.slice(1);
168
+ let inPlace = false;
169
+ let skipBackupExt = false;
170
+ const positionals: Token[] = [];
171
+ for (let i = 0; i < argTokens.length; i += 1) {
172
+ const t = argTokens[i];
173
+ if (t.quoted === 'none' && t.value === '-i') {
174
+ inPlace = true;
175
+ skipBackupExt = true;
176
+ continue;
177
+ }
178
+ if (t.quoted === 'none' && t.value.startsWith('-i')) {
179
+ // -iBAK
180
+ inPlace = true;
181
+ continue;
182
+ }
183
+ if (t.quoted === 'none' && t.value.startsWith('-')) continue;
184
+ if (skipBackupExt && t.quoted !== 'none' && t.value === '') {
185
+ // BSD `sed -i ''` — 空文字はバックアップ拡張子扱いで無視
186
+ skipBackupExt = false;
187
+ continue;
188
+ }
189
+ skipBackupExt = false;
190
+ positionals.push(t);
191
+ }
192
+ if (inPlace && positionals.length >= 2) {
193
+ // 先頭はスクリプト、残りはファイル
194
+ for (let i = 1; i < positionals.length; i += 1) {
195
+ results.push(positionals[i].value);
196
+ }
197
+ }
198
+ } else if (baseName === 'cp' || baseName === 'mv') {
199
+ // 宛先は最後の非オプション引数
200
+ const positionals: Token[] = [];
201
+ for (let i = 1; i < tokens.length; i += 1) {
202
+ const t = tokens[i];
203
+ if (t.quoted === 'none' && t.value.startsWith('-')) continue;
204
+ if (t.quoted === 'none' && (t.value === '>' || t.value === '>>' || t.value === '|' || t.value === '<')) break;
205
+ positionals.push(t);
206
+ }
207
+ if (positionals.length >= 2) {
208
+ results.push(positionals[positionals.length - 1].value);
209
+ }
210
+ } else if (baseName === 'touch') {
211
+ for (let i = 1; i < tokens.length; i += 1) {
212
+ const t = tokens[i];
213
+ if (t.quoted === 'none' && t.value.startsWith('-')) continue;
214
+ if (t.quoted === 'none' && (t.value === '>' || t.value === '>>' || t.value === '|' || t.value === '<')) break;
215
+ results.push(t.value);
216
+ }
217
+ }
218
+
219
+ return results;
220
+ }
221
+
222
+ export class BashWriteTargetExtractor {
223
+ /**
224
+ * Bash コマンド文字列から書き込み先ファイルパスを抽出する。
225
+ * 副作用なし、純粋関数相当。
226
+ *
227
+ * @param command Bash コマンド文字列
228
+ * @returns 書き込み先ファイルパスの配列 (重複除去済み・入力順)
229
+ */
230
+ extract(command: string): readonly string[] {
231
+ if (typeof command !== 'string' || command.length === 0) {
232
+ return Object.freeze([]);
233
+ }
234
+
235
+ const tokens = tokenize(command);
236
+ const groups = splitByOperators(tokens);
237
+
238
+ const collected: string[] = [];
239
+ for (const group of groups) {
240
+ const found = extractFromSingleCommand(group);
241
+ for (const path of found) {
242
+ collected.push(path);
243
+ }
244
+ }
245
+
246
+ // 重複除去 (挿入順保持)
247
+ const seen = new Set<string>();
248
+ const unique: string[] = [];
249
+ for (const p of collected) {
250
+ if (p.length === 0) continue;
251
+ if (seen.has(p)) continue;
252
+ seen.add(p);
253
+ unique.push(p);
254
+ }
255
+
256
+ return Object.freeze(unique);
257
+ }
258
+ }
@@ -120,7 +120,7 @@ export class HookToCliTranslator {
120
120
  }
121
121
 
122
122
  // Check command registry
123
- const commandName = 'harness:lint';
123
+ const commandName = 'phasegate:lint';
124
124
  const hasCommand = (() => {
125
125
  const reg = this.cliCommandRegistryPort;
126
126
  if (reg.has) {
@@ -154,7 +154,7 @@ export class HookToCliTranslator {
154
154
  return HookTranslationResult.skip('REENTRY_DETECTED');
155
155
  }
156
156
 
157
- return HookTranslationResult.execute('harness:complete-check', [], 0);
157
+ return HookTranslationResult.execute('phasegate:complete-check', [], 0);
158
158
  }
159
159
  }
160
160
 
@@ -169,7 +169,7 @@ export class AsyncHookToCliTranslator {
169
169
  hasCommand(commandName: string): Promise<boolean>;
170
170
  };
171
171
  private readonly phaseGateQueryPort: {
172
- checkGate(scope: WriteTargetScope): Promise<{
172
+ checkGate(scope: WriteTargetScope, targetFilePath?: string): Promise<{
173
173
  hasPassed(): boolean;
174
174
  getBlockers(): readonly string[];
175
175
  getWarnings(): readonly string[];
@@ -181,7 +181,7 @@ export class AsyncHookToCliTranslator {
181
181
  reentryGuard: ReentryGuard;
182
182
  cliCommandRegistryPort: { hasCommand(commandName: string): Promise<boolean> };
183
183
  phaseGateQueryPort?: {
184
- checkGate(scope: WriteTargetScope): Promise<{
184
+ checkGate(scope: WriteTargetScope, targetFilePath?: string): Promise<{
185
185
  hasPassed(): boolean;
186
186
  getBlockers(): readonly string[];
187
187
  getWarnings(): readonly string[];
@@ -222,7 +222,11 @@ export class AsyncHookToCliTranslator {
222
222
  private async translatePreToolUse(event: PreToolUseEvent): Promise<HookTranslationResult> {
223
223
  // Step 1: Protected file check (applies to all tools)
224
224
  const additionalPatterns = await this.configQueryPort.getProtectedFilePatterns();
225
- const protectedFileList = ProtectedFileList.createWithAdditional(additionalPatterns);
225
+ const exclusions = await this.configQueryPort.getProtectedFileExclusions();
226
+ const protectedFileList = ProtectedFileList.createWithAdditionalAndExclusions(
227
+ additionalPatterns,
228
+ exclusions,
229
+ );
226
230
 
227
231
  const blockedPath = event.targetFilePaths.find((fp) => protectedFileList.matches(fp));
228
232
  if (blockedPath !== undefined) {
@@ -256,7 +260,20 @@ export class AsyncHookToCliTranslator {
256
260
  });
257
261
  }
258
262
 
259
- const phaseGateResult = await this.phaseGateQueryPort.checkGate(detectedScope);
263
+ // Step 3: Check if phase gate is relaxed via quickMode.relaxedGates
264
+ const relaxedGates = await this.configQueryPort.getRelaxedGates();
265
+ if (relaxedGates.includes('phase-gate')) {
266
+ return HookTranslationResult.create({
267
+ shouldBlock: false,
268
+ cliArgs: [],
269
+ expectedExitCode: 0,
270
+ });
271
+ }
272
+
273
+ const phaseGateResult = await this.phaseGateQueryPort.checkGate(
274
+ detectedScope,
275
+ event.targetFilePaths[0],
276
+ );
260
277
  if (!phaseGateResult.hasPassed()) {
261
278
  return HookTranslationResult.block({
262
279
  reason: 'PHASE_GATE',
@@ -281,7 +298,7 @@ export class AsyncHookToCliTranslator {
281
298
  return HookTranslationResult.skip('HOOK_DISABLED');
282
299
  }
283
300
 
284
- const commandName = 'harness:lint';
301
+ const commandName = 'phasegate:lint';
285
302
  const hasCommand = await this.cliCommandRegistryPort.hasCommand(commandName);
286
303
  if (!hasCommand) {
287
304
  throw new CommandNotRegisteredError(commandName);
@@ -295,6 +312,6 @@ export class AsyncHookToCliTranslator {
295
312
  if (isActive) {
296
313
  return HookTranslationResult.skip('REENTRY_DETECTED');
297
314
  }
298
- return HookTranslationResult.execute('harness:complete-check', [], 0);
315
+ return HookTranslationResult.execute('phasegate:complete-check', [], 0);
299
316
  }
300
317
  }
@@ -69,6 +69,26 @@ export class ProtectedFileList {
69
69
  return new ProtectedFileList(allPatterns);
70
70
  }
71
71
 
72
+ static createWithExclusions(exclusions: string[]): ProtectedFileList {
73
+ const filtered = DEFAULT_PATTERNS.filter((p) => !exclusions.includes(p));
74
+ if (filtered.length === 0) {
75
+ return new ProtectedFileList([...DEFAULT_PATTERNS]);
76
+ }
77
+ return new ProtectedFileList(filtered);
78
+ }
79
+
80
+ static createWithAdditionalAndExclusions(
81
+ additionalPatterns: string[],
82
+ exclusions: string[],
83
+ ): ProtectedFileList {
84
+ const base = DEFAULT_PATTERNS.filter((p) => !exclusions.includes(p));
85
+ const allPatterns = [...base, ...additionalPatterns];
86
+ if (allPatterns.length === 0) {
87
+ return new ProtectedFileList([...DEFAULT_PATTERNS]);
88
+ }
89
+ return new ProtectedFileList(allPatterns);
90
+ }
91
+
72
92
  matches(filePath: string): boolean {
73
93
  if (filePath === '') return false;
74
94
  return this.patterns.some((pattern) => {
@@ -0,0 +1,63 @@
1
+ // @unit agent-integration
2
+ // @layer domain
3
+
4
+ export class StoryReflectionQueryResultInvariantError extends Error {
5
+ constructor(message: string) {
6
+ super(message);
7
+ this.name = 'StoryReflectionQueryResultInvariantError';
8
+ Object.setPrototypeOf(this, new.target.prototype);
9
+ }
10
+ }
11
+
12
+ export class StoryReflectionQueryResult {
13
+ readonly passed: boolean;
14
+ readonly blockers: readonly string[];
15
+ readonly warnings: readonly string[];
16
+ readonly skipped: boolean;
17
+
18
+ private constructor(
19
+ passed: boolean,
20
+ blockers: readonly string[],
21
+ warnings: readonly string[],
22
+ skipped: boolean,
23
+ ) {
24
+ this.passed = passed;
25
+ this.blockers = Object.freeze([...blockers]);
26
+ this.warnings = Object.freeze([...warnings]);
27
+ this.skipped = skipped;
28
+ }
29
+
30
+ static pass(): StoryReflectionQueryResult {
31
+ return new StoryReflectionQueryResult(true, [], [], false);
32
+ }
33
+
34
+ static skipped(warnings: string[] = []): StoryReflectionQueryResult {
35
+ return new StoryReflectionQueryResult(true, [], warnings, true);
36
+ }
37
+
38
+ static block(blockers: string[], warnings: string[]): StoryReflectionQueryResult {
39
+ if (blockers.length === 0) {
40
+ throw new StoryReflectionQueryResultInvariantError(
41
+ 'passed=falseの場合、blockersは1件以上必要です',
42
+ );
43
+ }
44
+
45
+ return new StoryReflectionQueryResult(false, blockers, warnings, false);
46
+ }
47
+
48
+ hasPassed(): boolean {
49
+ return this.passed;
50
+ }
51
+
52
+ getBlockers(): readonly string[] {
53
+ return this.blockers;
54
+ }
55
+
56
+ getWarnings(): readonly string[] {
57
+ return this.warnings;
58
+ }
59
+
60
+ isSkipped(): boolean {
61
+ return this.skipped;
62
+ }
63
+ }
@@ -12,12 +12,12 @@ import { TimeoutError } from '../ports/cli-executor-port.js';
12
12
 
13
13
  /**
14
14
  * CommandName を実行可能なコマンドに変換する
15
- * 例: 'harness:lint' → ['npx', 'tsx', 'scripts/harness/cli/lint.ts']
15
+ * 例: 'phasegate:lint' → ['npx', 'tsx', 'scripts/harness/cli/lint.ts']
16
16
  * テスト時は直接スクリプトパスで execute を呼ぶことも可能
17
17
  */
18
18
  function resolveCommand(commandName: string): { cmd: string; args: string[] } {
19
19
  // コマンド名をファイルパスに変換
20
- const slug = commandName.replace('harness:', '');
20
+ const slug = commandName.replace('phasegate:', '');
21
21
  return {
22
22
  cmd: 'npx',
23
23
  args: ['tsx', `scripts/harness/cli/${slug}.ts`],
@@ -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
+ }
@@ -10,16 +10,16 @@ import type { CliCommandRegistryPort } from '../../domain/ports/cli-command-regi
10
10
 
11
11
  /** integration_contract.md §3.1 に定義された登録済みコマンド一覧 */
12
12
  const REGISTERED_COMMANDS: readonly string[] = [
13
- 'harness:check-ready',
14
- 'harness:check-phase',
15
- 'harness:ci-check',
16
- 'harness:detect-drift',
17
- 'harness:status',
18
- 'harness:lint',
19
- 'harness:complete-check',
20
- 'harness:impact-analysis',
21
- 'harness:enable',
22
- 'harness:disable',
13
+ 'phasegate:check-ready',
14
+ 'phasegate:check-phase',
15
+ 'phasegate:ci-check',
16
+ 'phasegate:detect-drift',
17
+ 'phasegate:status',
18
+ 'phasegate:lint',
19
+ 'phasegate:complete-check',
20
+ 'phasegate:impact-analysis',
21
+ 'phasegate:enable',
22
+ 'phasegate:disable',
23
23
  ];
24
24
 
25
25
  const COMMAND_SET = new Set<string>(REGISTERED_COMMANDS);
@@ -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;