phasegate 0.191.0 → 0.222.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 (77) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.ja.md +52 -15
  3. package/README.md +39 -11
  4. package/docs/ADR/030-injection-threat-model-and-trust-root.md +145 -0
  5. package/docs/guide/hooks-integration.md +101 -1
  6. package/docs/guide/installation.md +1 -1
  7. package/docs/guide/quick-vs-full-mode.md +1 -1
  8. package/docs/guide/skills-overview.md +7 -8
  9. package/package.json +1 -1
  10. package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +131 -63
  11. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +31 -4
  12. package/scripts/harness/agent-integration/presentation/spotlight.ts +65 -0
  13. package/scripts/harness/biome-ast-engine/application/mappers/build-harness-error-payload-output-mapper.ts +3 -3
  14. package/scripts/harness/biome-ast-engine/infrastructure/mappers/rule-violation-code-mapper.ts +3 -3
  15. package/scripts/harness/biome-ast-engine/presentation/cli/harness-lint-command-handler.ts +6 -4
  16. package/scripts/harness/ci-governance/application/dto/pin-integrity-input.ts +8 -0
  17. package/scripts/harness/ci-governance/application/dto/pin-integrity-output.ts +9 -0
  18. package/scripts/harness/ci-governance/application/dto/verify-integrity-input.ts +7 -0
  19. package/scripts/harness/ci-governance/application/dto/verify-integrity-output.ts +10 -0
  20. package/scripts/harness/ci-governance/application/usecases/pin-integrity-usecase.ts +60 -0
  21. package/scripts/harness/ci-governance/application/usecases/verify-integrity-usecase.ts +46 -0
  22. package/scripts/harness/ci-governance/composition-root.ts +67 -64
  23. package/scripts/harness/ci-governance/domain/ports/integrity-manifest-repository-port.ts +14 -0
  24. package/scripts/harness/ci-governance/domain/ports/sha256-hasher-port.ts +10 -0
  25. package/scripts/harness/ci-governance/domain/services/integrity-checker.ts +42 -0
  26. package/scripts/harness/ci-governance/domain/value-objects/integrity-drift.ts +16 -0
  27. package/scripts/harness/ci-governance/domain/value-objects/integrity-manifest.ts +49 -0
  28. package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +40 -0
  29. package/scripts/harness/ci-governance/infrastructure/adapters/file-system-sha256-hasher-adapter.ts +17 -0
  30. package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +7 -73
  31. package/scripts/harness/ci-governance/infrastructure/adapters/integrity-manifest-json-repository-adapter.ts +83 -0
  32. package/scripts/harness/ci-governance/presentation/handlers/integrity-handler.ts +76 -0
  33. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +60 -28
  34. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +33 -8
  35. package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +90 -0
  36. package/scripts/harness/harness-error/infrastructure/registry/l1-error-definitions.ts +9 -9
  37. package/scripts/harness/installation/application/bundled-skill-selection.ts +2 -5
  38. package/scripts/harness/installation/application/usecases/run-reconcile.ts +69 -1
  39. package/scripts/harness/main.ts +257 -105
  40. package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +65 -3
  41. package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +44 -40
  42. package/scripts/harness/setup/skill-deployer.ts +2 -4
  43. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +82 -49
  44. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +133 -53
  45. package/scripts/harness/validator-system/composition-root.ts +153 -99
  46. package/scripts/harness/validator-system/domain/ports/coverage-attestation-gating-policy-port.ts +14 -0
  47. package/scripts/harness/validator-system/domain/ports/coverage-attestation-verification-policy-port.ts +30 -0
  48. package/scripts/harness/validator-system/domain/ports/injection-scan-policy-port.ts +14 -0
  49. package/scripts/harness/validator-system/domain/services/coverage-attestation-gating-service.ts +56 -0
  50. package/scripts/harness/validator-system/domain/services/coverage-attestation-verification-service.ts +45 -0
  51. package/scripts/harness/validator-system/domain/services/injection-pattern-scan-service.ts +118 -0
  52. package/scripts/harness/validator-system/domain/value-objects/attestation-verification-report.ts +59 -0
  53. package/scripts/harness/validator-system/domain/value-objects/coverage-gating-report.ts +67 -0
  54. package/scripts/harness/validator-system/domain/value-objects/injection-scan-report.ts +55 -0
  55. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +37 -31
  56. package/scripts/harness/validator-system/infrastructure/adapters/adr-foundation-reference-adapter.ts +13 -7
  57. package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-gating-adapter.ts +87 -0
  58. package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-verification-adapter.ts +165 -0
  59. package/scripts/harness/validator-system/infrastructure/adapters/file-system-injection-scan-adapter.ts +82 -0
  60. package/skills/README.md +1 -1
  61. package/skills/codebase-mapper/SKILL.md +1 -1
  62. 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 +1 -1
  63. package/skills/doc-health-checker/SKILL.md +148 -0
  64. package/skills/release-publisher/SKILL.md +101 -0
  65. package/skills/skill-creator/SKILL.md +74 -332
  66. package/skills/story-implementor/SKILL.md +52 -0
  67. package/skills/story-mapper/SKILL.md +4 -0
  68. package/skills/story-writer/SKILL.md +9 -0
  69. package/skills/uiux-designer/references/uiux-design-template.md +4 -4
  70. package/skills/unit-designer/SKILL.md +3 -1
  71. package/templates/.claude/scripts/deny-check.sh +260 -0
  72. package/skills/doc-freshness-checker/SKILL.md +0 -140
  73. package/skills/implementation-planner/SKILL.md +0 -169
  74. package/skills/implementation-planner/references/document-structure.md +0 -116
  75. package/skills/implementation-planner/references/plan-template.md +0 -177
  76. package/skills/implementation-planner/references/workflow.md +0 -164
  77. package/skills/pointer-validator/SKILL.md +0 -105
@@ -0,0 +1,60 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ import type { FileScannerPort } from "../../domain/ports/file-scanner-port.js";
5
+ import type { IntegrityManifestRepositoryPort } from "../../domain/ports/integrity-manifest-repository-port.js";
6
+ import type { Sha256HasherPort } from "../../domain/ports/sha256-hasher-port.js";
7
+ import { IntegrityManifest } from "../../domain/value-objects/integrity-manifest.js";
8
+ import { IntegrityTarget } from "../../domain/value-objects/integrity-target.js";
9
+ import type { PinIntegrityInput } from "../dto/pin-integrity-input.js";
10
+ import type { PinIntegrityOutput } from "../dto/pin-integrity-output.js";
11
+
12
+ /**
13
+ * 指示搭載ファイル群の SHA-256 を再計算して manifest を書き出す。
14
+ * pin は常に「意図的な更新」なので overwrite 制御は行わない(dry-run のみ温存)。
15
+ * ADR-030 §Decision.3.①。
16
+ */
17
+ export class PinIntegrityUseCase {
18
+ constructor(
19
+ private readonly scanner: FileScannerPort,
20
+ private readonly hasher: Sha256HasherPort,
21
+ private readonly repository: IntegrityManifestRepositoryPort,
22
+ ) {}
23
+
24
+ async execute(input: PinIntegrityInput = {}): Promise<PinIntegrityOutput> {
25
+ const target = IntegrityTarget.defaultTargets();
26
+ const include = input.include ?? target.include;
27
+ const exclude = input.exclude ?? target.exclude;
28
+ const dryRun = input.dryRun === true;
29
+
30
+ const savedPath = this.repository.getPath();
31
+
32
+ const scanned = await this.scanner.scan({ include, exclude });
33
+ const sortedPaths = [...scanned].sort();
34
+
35
+ const files = new Map<string, string>();
36
+ for (const path of sortedPaths) {
37
+ files.set(path, await this.hasher.hashFile(path));
38
+ }
39
+
40
+ const manifest = IntegrityManifest.create({ files });
41
+ const outputFiles = manifest.sortedEntries().map(([path, digest]) => ({ path, digest }));
42
+
43
+ if (dryRun) {
44
+ return {
45
+ savedPath,
46
+ entryCount: outputFiles.length,
47
+ dryRun: true,
48
+ files: outputFiles,
49
+ };
50
+ }
51
+
52
+ const writtenPath = await this.repository.save(manifest);
53
+ return {
54
+ savedPath: writtenPath,
55
+ entryCount: outputFiles.length,
56
+ dryRun: false,
57
+ files: outputFiles,
58
+ };
59
+ }
60
+ }
@@ -0,0 +1,46 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ import type { FileScannerPort } from "../../domain/ports/file-scanner-port.js";
5
+ import type { IntegrityManifestRepositoryPort } from "../../domain/ports/integrity-manifest-repository-port.js";
6
+ import type { Sha256HasherPort } from "../../domain/ports/sha256-hasher-port.js";
7
+ import type { IntegrityChecker } from "../../domain/services/integrity-checker.js";
8
+ import { IntegrityTarget } from "../../domain/value-objects/integrity-target.js";
9
+ import type { VerifyIntegrityInput } from "../dto/verify-integrity-input.js";
10
+ import type { VerifyIntegrityOutput } from "../dto/verify-integrity-output.js";
11
+
12
+ /**
13
+ * manifest を読み込み、指示搭載ファイルを再計算して照合する。
14
+ * drift(不一致・追加・欠落・manifest 欠落)を列挙する。ADR-030 §Decision.3.①。
15
+ */
16
+ export class VerifyIntegrityUseCase {
17
+ constructor(
18
+ private readonly scanner: FileScannerPort,
19
+ private readonly hasher: Sha256HasherPort,
20
+ private readonly repository: IntegrityManifestRepositoryPort,
21
+ private readonly checker: IntegrityChecker,
22
+ ) {}
23
+
24
+ async execute(input: VerifyIntegrityInput = {}): Promise<VerifyIntegrityOutput> {
25
+ const target = IntegrityTarget.defaultTargets();
26
+ const include = input.include ?? target.include;
27
+ const exclude = input.exclude ?? target.exclude;
28
+
29
+ const manifestPath = this.repository.getPath();
30
+ const manifest = await this.repository.load();
31
+
32
+ const scanned = await this.scanner.scan({ include, exclude });
33
+ const actual = new Map<string, string>();
34
+ for (const path of [...scanned].sort()) {
35
+ actual.set(path, await this.hasher.hashFile(path));
36
+ }
37
+
38
+ const drifts = this.checker.computeDrifts(manifest, actual);
39
+
40
+ return {
41
+ manifestPath,
42
+ ok: drifts.length === 0,
43
+ drifts,
44
+ };
45
+ }
46
+ }
@@ -5,53 +5,54 @@
5
5
  * Composition Root - 依存性の組み立て
6
6
  */
7
7
 
8
- import { TemplateGenerator } from './domain/services/template-generator.js';
9
- import { RepetitionDetector } from './domain/services/repetition-detector.js';
10
- import { PointerValidator } from './domain/services/pointer-validator.js';
11
- import { LessonAggregator } from './domain/services/lesson-aggregator.js';
12
- import { ClaudeMdComposer } from './domain/services/claude-md-composer.js';
13
-
14
- import { GenerateCiTemplateUseCase } from './application/usecases/generate-ci-template-usecase.js';
15
- import { RenderCiTemplateUseCase } from './application/usecases/render-ci-template-usecase.js';
16
- import { RecordErrorOccurrenceUseCase } from './application/usecases/record-error-occurrence-usecase.js';
17
- import { CheckEscalationUseCase } from './application/usecases/check-escalation-usecase.js';
18
- import { ResetRepetitionUseCase } from './application/usecases/reset-repetition-usecase.js';
19
- import { MigrateAgentsMdUseCase } from './application/usecases/migrate-agents-md-usecase.js';
20
- import { RefreshAgentContextUseCase } from './application/usecases/refresh-agent-context-usecase.js';
21
- import { RefreshClaudeMdUseCase } from './application/usecases/refresh-claude-md-usecase.js';
22
- import { CheckAgentContextUseCase } from './application/usecases/check-agent-context-usecase.js';
23
- import { AggregateLessonsUseCase } from './application/usecases/aggregate-lessons-usecase.js';
24
- import { ValidatePointersUseCase } from './application/usecases/validate-pointers-usecase.js';
25
-
26
- import { ValidatorIdRegistryAdapter } from './infrastructure/adapters/validator-id-registry-adapter.js';
27
- import { PresetConfigAdapter } from './infrastructure/adapters/preset-config-adapter.js';
28
- import { ErrorRepetitionJsonRepository } from './infrastructure/adapters/error-repetition-json-repository.js';
29
- import { EscalationLogExecutorAdapter } from './infrastructure/adapters/escalation-log-executor-adapter.js';
30
- import { YamlTemplateRendererAdapter } from './infrastructure/adapters/yaml-template-renderer-adapter.js';
31
- import { FileSystemExistenceAdapter } from './infrastructure/adapters/file-system-existence-adapter.js';
32
- import { AgentsMdFileAdapter } from './infrastructure/adapters/agents-md-file-adapter.js';
33
- import { AgentContextFileAdapter } from './infrastructure/adapters/agent-context-file-adapter.js';
34
- import { LessonArtifactFileReaderAdapter } from './infrastructure/adapters/lesson-artifact-file-reader-adapter.js';
35
- import { HarnessApiCommandExistenceAdapter } from './infrastructure/adapters/harness-api-command-existence-adapter.js';
36
- import { AdrFoundationExistenceAdapter } from './infrastructure/adapters/adr-foundation-existence-adapter.js';
37
-
38
- import { GenerateCiTemplateHandler } from './presentation/handlers/generate-ci-template-handler.js';
39
- import { MigrateAgentsMdHandler } from './presentation/handlers/migrate-agents-md-handler.js';
40
- import { RefreshAgentContextHandler } from './presentation/handlers/refresh-agent-context-handler.js';
41
- import { RefreshClaudeMdHandler } from './presentation/handlers/refresh-claude-md-handler.js';
42
- import { CheckAgentContextHandler } from './presentation/handlers/check-agent-context-handler.js';
43
- import { CheckRepetitionHandler } from './presentation/handlers/check-repetition-handler.js';
44
- import { CreateBaselineHandler } from './presentation/handlers/create-baseline-handler.js';
45
-
46
- import { CreateBaselineUseCase } from './application/usecases/create-baseline-usecase.js';
47
- import { GlobFileScannerAdapter } from './infrastructure/adapters/glob-file-scanner-adapter.js';
48
- import { FileSystemSha1HasherAdapter } from './infrastructure/adapters/file-system-sha1-hasher-adapter.js';
49
- import { BaselineJsonRepositoryAdapter } from './infrastructure/adapters/baseline-json-repository-adapter.js';
50
-
51
- import { ScaffoldDesignUseCase } from './application/usecases/scaffold-design-usecase.js';
52
- import { FileSystemTemplateRepositoryAdapter } from './infrastructure/adapters/file-system-template-repository-adapter.js';
53
- import { FileSystemDesignDocWriterAdapter } from './infrastructure/adapters/file-system-design-doc-writer-adapter.js';
54
- import { ScaffoldDesignHandler } from './presentation/handlers/scaffold-design-handler.js';
8
+ import { AggregateLessonsUseCase } from "./application/usecases/aggregate-lessons-usecase.js";
9
+ import { CheckAgentContextUseCase } from "./application/usecases/check-agent-context-usecase.js";
10
+ import { CheckEscalationUseCase } from "./application/usecases/check-escalation-usecase.js";
11
+ import { CreateBaselineUseCase } from "./application/usecases/create-baseline-usecase.js";
12
+ import { GenerateCiTemplateUseCase } from "./application/usecases/generate-ci-template-usecase.js";
13
+ import { MigrateAgentsMdUseCase } from "./application/usecases/migrate-agents-md-usecase.js";
14
+ import { PinIntegrityUseCase } from "./application/usecases/pin-integrity-usecase.js";
15
+ import { RecordErrorOccurrenceUseCase } from "./application/usecases/record-error-occurrence-usecase.js";
16
+ import { RefreshAgentContextUseCase } from "./application/usecases/refresh-agent-context-usecase.js";
17
+ import { RefreshClaudeMdUseCase } from "./application/usecases/refresh-claude-md-usecase.js";
18
+ import { RenderCiTemplateUseCase } from "./application/usecases/render-ci-template-usecase.js";
19
+ import { ResetRepetitionUseCase } from "./application/usecases/reset-repetition-usecase.js";
20
+ import { ScaffoldDesignUseCase } from "./application/usecases/scaffold-design-usecase.js";
21
+ import { ValidatePointersUseCase } from "./application/usecases/validate-pointers-usecase.js";
22
+ import { VerifyIntegrityUseCase } from "./application/usecases/verify-integrity-usecase.js";
23
+ import { ClaudeMdComposer } from "./domain/services/claude-md-composer.js";
24
+ import { IntegrityChecker } from "./domain/services/integrity-checker.js";
25
+ import { LessonAggregator } from "./domain/services/lesson-aggregator.js";
26
+ import { PointerValidator } from "./domain/services/pointer-validator.js";
27
+ import { RepetitionDetector } from "./domain/services/repetition-detector.js";
28
+ import { TemplateGenerator } from "./domain/services/template-generator.js";
29
+ import { AdrFoundationExistenceAdapter } from "./infrastructure/adapters/adr-foundation-existence-adapter.js";
30
+ import { AgentContextFileAdapter } from "./infrastructure/adapters/agent-context-file-adapter.js";
31
+ import { AgentsMdFileAdapter } from "./infrastructure/adapters/agents-md-file-adapter.js";
32
+ import { BaselineJsonRepositoryAdapter } from "./infrastructure/adapters/baseline-json-repository-adapter.js";
33
+ import { ErrorRepetitionJsonRepository } from "./infrastructure/adapters/error-repetition-json-repository.js";
34
+ import { EscalationLogExecutorAdapter } from "./infrastructure/adapters/escalation-log-executor-adapter.js";
35
+ import { FileSystemDesignDocWriterAdapter } from "./infrastructure/adapters/file-system-design-doc-writer-adapter.js";
36
+ import { FileSystemExistenceAdapter } from "./infrastructure/adapters/file-system-existence-adapter.js";
37
+ import { FileSystemSha1HasherAdapter } from "./infrastructure/adapters/file-system-sha1-hasher-adapter.js";
38
+ import { FileSystemSha256HasherAdapter } from "./infrastructure/adapters/file-system-sha256-hasher-adapter.js";
39
+ import { FileSystemTemplateRepositoryAdapter } from "./infrastructure/adapters/file-system-template-repository-adapter.js";
40
+ import { GlobFileScannerAdapter } from "./infrastructure/adapters/glob-file-scanner-adapter.js";
41
+ import { HarnessApiCommandExistenceAdapter } from "./infrastructure/adapters/harness-api-command-existence-adapter.js";
42
+ import { IntegrityManifestJsonRepositoryAdapter } from "./infrastructure/adapters/integrity-manifest-json-repository-adapter.js";
43
+ import { LessonArtifactFileReaderAdapter } from "./infrastructure/adapters/lesson-artifact-file-reader-adapter.js";
44
+ import { PresetConfigAdapter } from "./infrastructure/adapters/preset-config-adapter.js";
45
+ import { ValidatorIdRegistryAdapter } from "./infrastructure/adapters/validator-id-registry-adapter.js";
46
+ import { YamlTemplateRendererAdapter } from "./infrastructure/adapters/yaml-template-renderer-adapter.js";
47
+ import { CheckAgentContextHandler } from "./presentation/handlers/check-agent-context-handler.js";
48
+ import { CheckRepetitionHandler } from "./presentation/handlers/check-repetition-handler.js";
49
+ import { CreateBaselineHandler } from "./presentation/handlers/create-baseline-handler.js";
50
+ import { GenerateCiTemplateHandler } from "./presentation/handlers/generate-ci-template-handler.js";
51
+ import { IntegrityHandler } from "./presentation/handlers/integrity-handler.js";
52
+ import { MigrateAgentsMdHandler } from "./presentation/handlers/migrate-agents-md-handler.js";
53
+ import { RefreshAgentContextHandler } from "./presentation/handlers/refresh-agent-context-handler.js";
54
+ import { RefreshClaudeMdHandler } from "./presentation/handlers/refresh-claude-md-handler.js";
55
+ import { ScaffoldDesignHandler } from "./presentation/handlers/scaffold-design-handler.js";
55
56
 
56
57
  export interface CiGovernanceCompositionRoot {
57
58
  generateCiTemplateHandler: GenerateCiTemplateHandler;
@@ -70,12 +71,10 @@ export interface CiGovernanceCompositionRoot {
70
71
  validatePointersUseCase: ValidatePointersUseCase;
71
72
  createBaselineUseCase: CreateBaselineUseCase;
72
73
  scaffoldDesignUseCase: ScaffoldDesignUseCase;
74
+ integrityHandler: IntegrityHandler;
73
75
  }
74
76
 
75
- export function buildCiGovernance(
76
- baseDir: string,
77
- harnessRoot: string = baseDir,
78
- ): CiGovernanceCompositionRoot {
77
+ export function buildCiGovernance(baseDir: string, harnessRoot: string = baseDir): CiGovernanceCompositionRoot {
79
78
  // Infrastructure adapters
80
79
  const validatorIdRegistryAdapter = new ValidatorIdRegistryAdapter();
81
80
  const presetConfigAdapter = new PresetConfigAdapter();
@@ -118,25 +117,15 @@ export function buildCiGovernance(
118
117
  const fileScanner = new GlobFileScannerAdapter(baseDir);
119
118
  const fileHasher = new FileSystemSha1HasherAdapter(baseDir);
120
119
  const baselineRepository = new BaselineJsonRepositoryAdapter(baseDir);
121
- const createBaselineUseCase = new CreateBaselineUseCase(
122
- fileScanner,
123
- fileHasher,
124
- baselineRepository,
125
- );
120
+ const createBaselineUseCase = new CreateBaselineUseCase(fileScanner, fileHasher, baselineRepository);
126
121
 
127
122
  // Scaffold design adapters & use case (ISSUE-007 Wave 4)
128
123
  const templateRepository = new FileSystemTemplateRepositoryAdapter(harnessRoot);
129
124
  const designDocWriter = new FileSystemDesignDocWriterAdapter(baseDir);
130
- const scaffoldDesignUseCase = new ScaffoldDesignUseCase(
131
- templateRepository,
132
- designDocWriter,
133
- );
125
+ const scaffoldDesignUseCase = new ScaffoldDesignUseCase(templateRepository, designDocWriter);
134
126
 
135
127
  // Handlers
136
- const generateCiTemplateHandler = new GenerateCiTemplateHandler(
137
- generateCiTemplateUseCase,
138
- renderCiTemplateUseCase,
139
- );
128
+ const generateCiTemplateHandler = new GenerateCiTemplateHandler(generateCiTemplateUseCase, renderCiTemplateUseCase);
140
129
  const migrateAgentsMdHandler = new MigrateAgentsMdHandler(migrateAgentsMdUseCase);
141
130
  const refreshAgentContextHandler = new RefreshAgentContextHandler(refreshAgentContextUseCase);
142
131
  const refreshClaudeMdHandler = new RefreshClaudeMdHandler(refreshClaudeMdUseCase);
@@ -145,6 +134,19 @@ export function buildCiGovernance(
145
134
  const createBaselineHandler = new CreateBaselineHandler(createBaselineUseCase);
146
135
  const scaffoldDesignHandler = new ScaffoldDesignHandler(scaffoldDesignUseCase);
147
136
 
137
+ // Integrity pin adapters & handler (WI-254 / ADR-030 §Decision.3.①)
138
+ const integrityChecker = new IntegrityChecker();
139
+ const sha256Hasher = new FileSystemSha256HasherAdapter(baseDir);
140
+ const integrityManifestRepository = new IntegrityManifestJsonRepositoryAdapter(baseDir);
141
+ const pinIntegrityUseCase = new PinIntegrityUseCase(fileScanner, sha256Hasher, integrityManifestRepository);
142
+ const verifyIntegrityUseCase = new VerifyIntegrityUseCase(
143
+ fileScanner,
144
+ sha256Hasher,
145
+ integrityManifestRepository,
146
+ integrityChecker,
147
+ );
148
+ const integrityHandler = new IntegrityHandler(pinIntegrityUseCase, verifyIntegrityUseCase);
149
+
148
150
  return {
149
151
  generateCiTemplateHandler,
150
152
  migrateAgentsMdHandler,
@@ -161,5 +163,6 @@ export function buildCiGovernance(
161
163
  validatePointersUseCase,
162
164
  createBaselineUseCase,
163
165
  scaffoldDesignUseCase,
166
+ integrityHandler,
164
167
  };
165
168
  }
@@ -0,0 +1,14 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ import type { IntegrityManifest } from "../value-objects/integrity-manifest.js";
5
+
6
+ /**
7
+ * phasegate.integrity.json の読み書きポート。
8
+ */
9
+ export interface IntegrityManifestRepositoryPort {
10
+ getPath(): string;
11
+ exists(): Promise<boolean>;
12
+ load(): Promise<IntegrityManifest | null>;
13
+ save(manifest: IntegrityManifest): Promise<string>;
14
+ }
@@ -0,0 +1,10 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ /**
5
+ * ファイルの SHA-256 digest(64 桁小文字 hex)を返すポート。
6
+ * 既存の sha1 用 FileHasherPort とは別ポート(アルゴリズムが異なるため混同回避)。
7
+ */
8
+ export interface Sha256HasherPort {
9
+ hashFile(relativePath: string): Promise<string>;
10
+ }
@@ -0,0 +1,42 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ import type { IntegrityDrift } from "../value-objects/integrity-drift.js";
5
+ import type { IntegrityManifest } from "../value-objects/integrity-manifest.js";
6
+
7
+ /** manifest 欠落時に返す drift の path(manifest ファイルの相対パス) */
8
+ export const INTEGRITY_MANIFEST_PATH = "phasegate.integrity.json";
9
+
10
+ /**
11
+ * pin された manifest と、現在の再計算結果 (actual) を突き合わせて drift を列挙する
12
+ * 純ドメインサービス(状態なし・I/O なし)。ADR-030 §Decision.3.①。
13
+ */
14
+ export class IntegrityChecker {
15
+ computeDrifts(manifest: IntegrityManifest | null, actual: ReadonlyMap<string, string>): IntegrityDrift[] {
16
+ if (manifest === null) {
17
+ return [{ path: INTEGRITY_MANIFEST_PATH, kind: "manifest-absent" }];
18
+ }
19
+
20
+ const drifts: IntegrityDrift[] = [];
21
+
22
+ // manifest の各 path: actual に無ければ missing、不一致なら mismatch
23
+ for (const [path, expected] of manifest.sortedEntries()) {
24
+ const actualDigest = actual.get(path);
25
+ if (actualDigest === undefined) {
26
+ drifts.push({ path, kind: "missing" });
27
+ } else if (actualDigest !== expected) {
28
+ drifts.push({ path, kind: "mismatch" });
29
+ }
30
+ }
31
+
32
+ // actual の各 path: manifest に無ければ added
33
+ for (const path of actual.keys()) {
34
+ if (manifest.digestOf(path) === undefined) {
35
+ drifts.push({ path, kind: "added" });
36
+ }
37
+ }
38
+
39
+ // path 昇順で決定的に返す
40
+ return drifts.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
41
+ }
42
+ }
@@ -0,0 +1,16 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ /**
5
+ * verify で検出した整合性差分の種別。
6
+ * - mismatch: digest 不一致
7
+ * - added: 実在するが manifest に無い
8
+ * - missing: manifest にあるが実在しない
9
+ * - manifest-absent: manifest ファイル自体が欠落
10
+ */
11
+ export type IntegrityDriftKind = "mismatch" | "added" | "missing" | "manifest-absent";
12
+
13
+ export interface IntegrityDrift {
14
+ readonly path: string;
15
+ readonly kind: IntegrityDriftKind;
16
+ }
@@ -0,0 +1,49 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
5
+
6
+ export interface IntegrityManifestProps {
7
+ readonly files: ReadonlyMap<string, string>;
8
+ }
9
+
10
+ /**
11
+ * 指示搭載ファイル群の SHA-256 pin を表す不変 VO(pin 集約ルート)。
12
+ * version は 1 固定、algorithm は 'sha256' 固定(ADR-030 §Decision.3.①)。
13
+ */
14
+ export class IntegrityManifest {
15
+ readonly version = 1 as const;
16
+ readonly algorithm = "sha256" as const;
17
+ private readonly files: ReadonlyMap<string, string>;
18
+
19
+ private constructor(files: ReadonlyMap<string, string>) {
20
+ this.files = new Map(files);
21
+ Object.freeze(this);
22
+ }
23
+
24
+ static create(props: IntegrityManifestProps): IntegrityManifest {
25
+ for (const [path, digest] of props.files) {
26
+ if (path.length === 0) {
27
+ throw new Error("IntegrityManifest: path must not be empty");
28
+ }
29
+ if (!SHA256_HEX_RE.test(digest)) {
30
+ throw new Error(`IntegrityManifest: digest must be 64 lowercase hex chars, got: ${digest} (path: ${path})`);
31
+ }
32
+ }
33
+ return new IntegrityManifest(props.files);
34
+ }
35
+
36
+ /** path 昇順の [path, digest] 一覧(決定的) */
37
+ sortedEntries(): Array<[string, string]> {
38
+ return [...this.files.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
39
+ }
40
+
41
+ /** path 昇順の path 一覧 */
42
+ paths(): readonly string[] {
43
+ return this.sortedEntries().map(([p]) => p);
44
+ }
45
+
46
+ digestOf(path: string): string | undefined {
47
+ return this.files.get(path);
48
+ }
49
+ }
@@ -0,0 +1,40 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ export interface IntegrityTargetProps {
5
+ readonly include: readonly string[];
6
+ readonly exclude: readonly string[];
7
+ }
8
+
9
+ /**
10
+ * 整合性 pin の対象ファイル集合を表す不変 VO(include / exclude glob)。
11
+ * v1 固定の既定対象は ADR-030 §Decision.3.① の指示搭載ファイル群。
12
+ */
13
+ export class IntegrityTarget {
14
+ readonly include: readonly string[];
15
+ readonly exclude: readonly string[];
16
+
17
+ private constructor(props: IntegrityTargetProps) {
18
+ this.include = Object.freeze([...props.include]);
19
+ this.exclude = Object.freeze([...props.exclude]);
20
+ Object.freeze(this);
21
+ }
22
+
23
+ static create(props: IntegrityTargetProps): IntegrityTarget {
24
+ return new IntegrityTarget(props);
25
+ }
26
+
27
+ /** v1 固定の既定 pin 対象(指示搭載ファイル群) */
28
+ static defaultTargets(): IntegrityTarget {
29
+ return new IntegrityTarget({
30
+ include: [
31
+ "skills/*/SKILL.md",
32
+ ".claude/settings.json",
33
+ ".claude/scripts/*.sh",
34
+ ".husky/*",
35
+ "docs/templates/agent-context/**",
36
+ ],
37
+ exclude: [],
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,17 @@
1
+ // @unit ci-governance
2
+ // @layer infrastructure
3
+
4
+ import * as crypto from "node:crypto";
5
+ import * as fs from "node:fs/promises";
6
+ import * as path from "node:path";
7
+ import type { Sha256HasherPort } from "../../domain/ports/sha256-hasher-port.js";
8
+
9
+ export class FileSystemSha256HasherAdapter implements Sha256HasherPort {
10
+ constructor(private readonly baseDir: string) {}
11
+
12
+ async hashFile(relativePath: string): Promise<string> {
13
+ const absPath = path.isAbsolute(relativePath) ? relativePath : path.join(this.baseDir, relativePath);
14
+ const content = await fs.readFile(absPath);
15
+ return crypto.createHash("sha256").update(content).digest("hex");
16
+ }
17
+ }
@@ -1,81 +1,15 @@
1
1
  // @unit ci-governance
2
2
  // @layer infrastructure
3
+ // @work-item-id WI-250
3
4
 
5
+ import { KNOWN_HARNESS_COMMANDS } from '../../../harness-api/domain/value-objects/known-harness-commands.js';
4
6
  import type { CommandExistencePort } from '../../domain/ports/command-existence-port.js';
5
7
 
6
- const KNOWN_HARNESS_COMMANDS: readonly string[] = [
7
- 'baseline',
8
- 'bypass:audit',
9
- 'check-change-category',
10
- 'check-phase-gate',
11
- 'ci-check',
12
- 'ci:auto-refresh-agent-context',
13
- 'ci:check-repetition',
14
- 'ci:generate-template',
15
- 'ci:migrate-agents-md',
16
- 'commit-msg',
17
- 'complete-check',
18
- 'config:plan',
19
- 'delegate-sonnet',
20
- 'disable-feature',
21
- 'doctor',
22
- 'emit-agent-rules',
23
- 'enable-feature',
24
- 'hook',
25
- 'init',
26
- 'install',
27
- 'lint',
28
- 'list-adrs',
29
- 'list-errors',
30
- 'list-features',
31
- 'migrate',
32
- 'p2:check-agent-context',
33
- 'p2:check-freshness',
34
- 'p2:check-initial-creation',
35
- 'p2:generate-e2e-template',
36
- 'p2:validate-pointers',
37
- 'phasegate:attest',
38
- 'phasegate:check-phase',
39
- 'phasegate:check-ready',
40
- 'phasegate:ci-check',
41
- 'phasegate:complete-check',
42
- 'phasegate:detect-drift',
43
- 'phasegate:generate-matrix',
44
- 'phasegate:impact-analysis',
45
- 'phasegate:lint',
46
- 'phasegate:status',
47
- 'phasegate:verify-attestation',
48
- 'pre-commit',
49
- 'reconcile',
50
- 'refresh-claude-md',
51
- 'regression:analyze-migration',
52
- 'regression:configure-ci-gate',
53
- 'regression:migrate-v0-tests',
54
- 'regression:run-agent-guard',
55
- 'regression:run-gng-gate',
56
- 'regression:run-k-requirements',
57
- 'regression:run-k14-k15',
58
- 'render-errors',
59
- 'scaffold-design',
60
- 'scaffold-wi',
61
- 'session',
62
- 'setup:agent',
63
- 'skill:apply-cascade-update',
64
- 'skill:check-coverage',
65
- 'skill:collect-lessons',
66
- 'skill:execute-tdd-cycle',
67
- 'skill:validate-structure',
68
- 'skills',
69
- 'status',
70
- 'uninstall',
71
- 'update-skills',
72
- 'validate',
73
- 'validate-adr',
74
- 'validate-fix',
75
- 'validate-metadata',
76
- 'work-items:status',
77
- ];
78
-
8
+ /**
9
+ * WI-250: 既知コマンドのデフォルトは harness-api domain の canonical 定数
10
+ * `KNOWN_HARNESS_COMMANDS` を配線する(infra ローカルの重複定義は持たない)。
11
+ * main.ts dispatch との乖離検出は harness-api 側の conformance テストが担う。
12
+ */
79
13
  export class HarnessApiCommandExistenceAdapter implements CommandExistencePort {
80
14
  private readonly knownCommands: Set<string>;
81
15