phasegate 0.183.0 → 0.212.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 (100) hide show
  1. package/CHANGELOG.md +20 -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 +50 -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 +17 -17
  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/infrastructure/parsers/comment-density-parser.ts +51 -10
  14. package/scripts/harness/ci-governance/application/dto/pin-integrity-input.ts +8 -0
  15. package/scripts/harness/ci-governance/application/dto/pin-integrity-output.ts +9 -0
  16. package/scripts/harness/ci-governance/application/dto/verify-integrity-input.ts +7 -0
  17. package/scripts/harness/ci-governance/application/dto/verify-integrity-output.ts +10 -0
  18. package/scripts/harness/ci-governance/application/usecases/pin-integrity-usecase.ts +60 -0
  19. package/scripts/harness/ci-governance/application/usecases/verify-integrity-usecase.ts +46 -0
  20. package/scripts/harness/ci-governance/composition-root.ts +68 -65
  21. package/scripts/harness/ci-governance/domain/ports/integrity-manifest-repository-port.ts +14 -0
  22. package/scripts/harness/ci-governance/domain/ports/sha256-hasher-port.ts +10 -0
  23. package/scripts/harness/ci-governance/domain/services/integrity-checker.ts +42 -0
  24. package/scripts/harness/ci-governance/domain/value-objects/integrity-drift.ts +16 -0
  25. package/scripts/harness/ci-governance/domain/value-objects/integrity-manifest.ts +49 -0
  26. package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +40 -0
  27. package/scripts/harness/ci-governance/infrastructure/adapters/adr-foundation-existence-adapter.ts +26 -2
  28. package/scripts/harness/ci-governance/infrastructure/adapters/file-system-sha256-hasher-adapter.ts +17 -0
  29. package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +8 -1
  30. package/scripts/harness/ci-governance/infrastructure/adapters/integrity-manifest-json-repository-adapter.ts +83 -0
  31. package/scripts/harness/ci-governance/presentation/handlers/integrity-handler.ts +76 -0
  32. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +53 -28
  33. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +33 -8
  34. package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +90 -0
  35. package/scripts/harness/installation/application/bundled-skill-selection.ts +2 -5
  36. package/scripts/harness/main.ts +257 -105
  37. package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +2 -0
  38. package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +8 -0
  39. package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +137 -2
  40. package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +44 -40
  41. package/scripts/harness/setup/skill-deployer.ts +8 -10
  42. package/scripts/harness/skill-quality/domain/services/skill-structure-validator.ts +13 -2
  43. package/scripts/harness/skill-quality/domain/types/skill-kind.ts +6 -0
  44. package/scripts/harness/skill-quality/domain/value-objects/skill-structure.ts +24 -8
  45. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +82 -49
  46. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +87 -58
  47. package/scripts/harness/validator-system/composition-root.ts +141 -99
  48. package/scripts/harness/validator-system/domain/ports/coverage-attestation-gating-policy-port.ts +14 -0
  49. package/scripts/harness/validator-system/domain/ports/injection-scan-policy-port.ts +14 -0
  50. package/scripts/harness/validator-system/domain/services/coverage-attestation-gating-service.ts +56 -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/coverage-gating-report.ts +67 -0
  53. package/scripts/harness/validator-system/domain/value-objects/injection-scan-report.ts +55 -0
  54. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +35 -31
  55. package/scripts/harness/validator-system/infrastructure/adapters/adr-foundation-reference-adapter.ts +13 -7
  56. package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-gating-adapter.ts +87 -0
  57. package/scripts/harness/validator-system/infrastructure/adapters/file-system-injection-scan-adapter.ts +82 -0
  58. package/skills/README.md +1 -1
  59. package/skills/cascade-updater/SKILL.md +3 -3
  60. package/skills/codebase-mapper/SKILL.md +6 -6
  61. package/skills/codex-delegator/SKILL.md +4 -3
  62. package/skills/codex-delegator/references/prompt-patterns.md +3 -3
  63. package/skills/codex-delegator/references/review-dimensions.md +1 -1
  64. package/skills/consistency-checker/SKILL.md +1 -1
  65. 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
  66. package/skills/doc-health-checker/SKILL.md +148 -0
  67. package/skills/domain-designer/SKILL.md +4 -2
  68. package/skills/engineering-perspective/SKILL.md +1 -0
  69. package/skills/environment-designer/SKILL.md +8 -6
  70. package/skills/implementation-readiness-checker/SKILL.md +2 -1
  71. package/skills/it-test-designer/SKILL.md +10 -8
  72. package/skills/it-test-logic-designer/SKILL.md +11 -9
  73. package/skills/it-test-logic-designer/references/repository-test-patterns.md +8 -1
  74. package/skills/logical-designer/SKILL.md +5 -3
  75. package/skills/mock-designer/SKILL.md +10 -6
  76. package/skills/phasegate-config-doctor/SKILL.md +3 -2
  77. package/skills/phasegate-toolkit-guide/SKILL.md +1 -0
  78. package/skills/quick-implementor/SKILL.md +1 -1
  79. package/skills/release-publisher/SKILL.md +101 -0
  80. package/skills/scenario-test-designer/SKILL.md +27 -14
  81. package/skills/scenario-test-logic-designer/SKILL.md +10 -8
  82. package/skills/scenario-test-logic-designer/references/msw-patterns.md +3 -1
  83. package/skills/scenario-test-logic-designer/references/playwright-patterns.md +3 -1
  84. package/skills/skill-creator/SKILL.md +75 -332
  85. package/skills/story-implementor/SKILL.md +54 -0
  86. package/skills/story-mapper/SKILL.md +8 -4
  87. package/skills/story-writer/SKILL.md +14 -5
  88. package/skills/test-coverage-checker/SKILL.md +4 -6
  89. package/skills/uiux-designer/SKILL.md +4 -2
  90. package/skills/uiux-designer/references/uiux-design-template.md +4 -4
  91. package/skills/unit-designer/SKILL.md +11 -7
  92. package/skills/unit-test-designer/SKILL.md +25 -11
  93. package/skills/unit-test-logic-designer/SKILL.md +10 -8
  94. package/skills/unit-test-logic-designer/references/test-patterns.md +7 -1
  95. package/skills/doc-freshness-checker/SKILL.md +0 -140
  96. package/skills/implementation-planner/SKILL.md +0 -167
  97. package/skills/implementation-planner/references/document-structure.md +0 -116
  98. package/skills/implementation-planner/references/plan-template.md +0 -177
  99. package/skills/implementation-planner/references/workflow.md +0 -164
  100. package/skills/pointer-validator/SKILL.md +0 -104
@@ -11,22 +11,37 @@ export type CommentDensityResult = {
11
11
 
12
12
  const SINGLE_LINE_COMMENT = /^\s*\/\//;
13
13
  const MULTI_LINE_COMMENT_START = /^\s*\/\*/;
14
+ const DOC_COMMENT_START = /^\s*\/\*\*/;
14
15
  const MULTI_LINE_COMMENT_END = /\*\//;
15
16
  const BLANK_LINE = /^\s*$/;
17
+ // L1 ルール (require-unit-comment / require-layer-comment) が必須化する
18
+ // 先頭メタデータヘッダのタグ行。ファイル冒頭の連続コメント領域でのみ効力を持つ。
19
+ const METADATA_HEADER_TAG = /^\s*\/\/\s*@(unit|layer|work-item-id|story|story-id|issue-id|module|feature|epic)\b/;
16
20
 
17
21
  /**
18
22
  * ソースコードのコメント密度と繰り返しブロック数を算出する。
19
23
  *
20
- * - commentLineCount: コメント行数(単一行・複数行両方)
21
- * - logicalLineCount: 空行を除いた行数
22
- * - repeatedCommentBlocks: 連続する同一コメントブロックの検出回数
24
+ * WI-239: commentLineCount(密度分子)は「密度に寄与する narrative コメント行数」
25
+ * を表すよう意味を再定義した。次は分子から除外する:
26
+ * - 先頭の必須メタデータヘッダ行(`// @unit` / `// @layer` / `// @work-item-id` /
27
+ * `// @story` 等。ファイル冒頭の連続コメント領域に限る)
28
+ * - `/** … *\/` doc-comment ブロック(ブロック全体)
29
+ * 分子に残すのは narrative な `//` 行コメントと非 doc の `/* … *\/` ブロックのみ。
30
+ *
31
+ * - commentLineCount: 密度分子となる narrative コメント行数
32
+ * - logicalLineCount: 空行を除いた行数(分母。定義不変)
33
+ * - repeatedCommentBlocks: 連続する同一 narrative コメントブロックの検出回数
23
34
  */
24
35
  export const parseCommentDensity = (sourceCode: string): CommentDensityResult => {
25
- const lines = sourceCode.split('\n');
36
+ const lines = sourceCode.split("\n");
26
37
 
27
38
  let commentLineCount = 0;
28
39
  let logicalLineCount = 0;
29
40
  let insideBlockComment = false;
41
+ let insideDocComment = false;
42
+ // ファイル冒頭の連続したメタデータヘッダ領域内かどうか。
43
+ // 最初のコード行または非メタデータコメントが現れた時点で false になる。
44
+ let insideHeaderZone = true;
30
45
 
31
46
  const commentBlocks: string[] = [];
32
47
  let currentBlock: string[] = [];
@@ -41,11 +56,14 @@ export const parseCommentDensity = (sourceCode: string): CommentDensityResult =>
41
56
  logicalLineCount += 1;
42
57
 
43
58
  if (insideBlockComment) {
44
- commentLineCount += 1;
45
- currentBlock.push(line.trim());
59
+ if (!insideDocComment) {
60
+ commentLineCount += 1;
61
+ currentBlock.push(line.trim());
62
+ }
46
63
 
47
64
  if (MULTI_LINE_COMMENT_END.test(line)) {
48
65
  insideBlockComment = false;
66
+ insideDocComment = false;
49
67
  flushBlock(currentBlock, commentBlocks);
50
68
  currentBlock = [];
51
69
  }
@@ -54,25 +72,48 @@ export const parseCommentDensity = (sourceCode: string): CommentDensityResult =>
54
72
  }
55
73
 
56
74
  if (SINGLE_LINE_COMMENT.test(line)) {
75
+ const isMetadataHeader = insideHeaderZone && METADATA_HEADER_TAG.test(line);
76
+
77
+ if (isMetadataHeader) {
78
+ // メタデータヘッダは分子・反復検出のいずれにも寄与しない。
79
+ continue;
80
+ }
81
+
82
+ // narrative な行コメントに到達 → ヘッダ領域は終了。
83
+ insideHeaderZone = false;
57
84
  commentLineCount += 1;
58
85
  currentBlock.push(line.trim());
59
86
  continue;
60
87
  }
61
88
 
62
89
  if (MULTI_LINE_COMMENT_START.test(line)) {
90
+ insideHeaderZone = false;
91
+ const isDocComment = DOC_COMMENT_START.test(line);
92
+ const isClosed = MULTI_LINE_COMMENT_END.test(line);
93
+
94
+ if (isDocComment) {
95
+ // doc-comment ブロックは分子・反復検出のいずれにも寄与しない。
96
+ if (!isClosed) {
97
+ insideBlockComment = true;
98
+ insideDocComment = true;
99
+ }
100
+ continue;
101
+ }
102
+
63
103
  commentLineCount += 1;
64
- insideBlockComment = true;
65
104
  currentBlock.push(line.trim());
66
105
 
67
- if (MULTI_LINE_COMMENT_END.test(line)) {
68
- insideBlockComment = false;
106
+ if (isClosed) {
69
107
  flushBlock(currentBlock, commentBlocks);
70
108
  currentBlock = [];
109
+ } else {
110
+ insideBlockComment = true;
71
111
  }
72
112
 
73
113
  continue;
74
114
  }
75
115
 
116
+ insideHeaderZone = false;
76
117
  flushBlock(currentBlock, commentBlocks);
77
118
  currentBlock = [];
78
119
  }
@@ -86,7 +127,7 @@ export const parseCommentDensity = (sourceCode: string): CommentDensityResult =>
86
127
 
87
128
  const flushBlock = (current: string[], blocks: string[]): void => {
88
129
  if (current.length > 0) {
89
- blocks.push(current.join('\n'));
130
+ blocks.push(current.join("\n"));
90
131
  }
91
132
  };
92
133
 
@@ -0,0 +1,8 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ export interface PinIntegrityInput {
5
+ readonly include?: readonly string[];
6
+ readonly exclude?: readonly string[];
7
+ readonly dryRun?: boolean;
8
+ }
@@ -0,0 +1,9 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ export interface PinIntegrityOutput {
5
+ readonly savedPath: string;
6
+ readonly entryCount: number;
7
+ readonly dryRun: boolean;
8
+ readonly files: ReadonlyArray<{ readonly path: string; readonly digest: string }>;
9
+ }
@@ -0,0 +1,7 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ export interface VerifyIntegrityInput {
5
+ readonly include?: readonly string[];
6
+ readonly exclude?: readonly string[];
7
+ }
@@ -0,0 +1,10 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ import type { IntegrityDrift } from "../../domain/value-objects/integrity-drift.js";
5
+
6
+ export interface VerifyIntegrityOutput {
7
+ readonly manifestPath: string;
8
+ readonly ok: boolean;
9
+ readonly drifts: readonly IntegrityDrift[];
10
+ }
@@ -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();
@@ -84,7 +83,7 @@ export function buildCiGovernance(
84
83
  const templateRendererPort = new YamlTemplateRendererAdapter(harnessRoot);
85
84
  const fileExistencePort = new FileSystemExistenceAdapter(baseDir);
86
85
  const commandExistencePort = new HarnessApiCommandExistenceAdapter();
87
- const adrExistencePort = new AdrFoundationExistenceAdapter();
86
+ const adrExistencePort = new AdrFoundationExistenceAdapter(baseDir);
88
87
  const agentsMdPort = new AgentsMdFileAdapter(baseDir);
89
88
  const agentContextDocumentPort = new AgentContextFileAdapter(baseDir, harnessRoot);
90
89
  const lessonArtifactReaderPort = new LessonArtifactFileReaderAdapter(baseDir);
@@ -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
+ }