phasegate 0.74.0 → 0.91.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 (99) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/docs/ADR/ADR-014-presentation-domain-dependency.md +82 -0
  3. package/package.json +1 -1
  4. package/scripts/harness/adr-foundation/application/dto/application-errors.ts +0 -13
  5. package/scripts/harness/agent-integration/{infrastructure → application}/ports/cli-executor-port.ts +2 -2
  6. package/scripts/harness/agent-integration/application/usecases/handle-post-tool-use-usecase.ts +2 -2
  7. package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +28 -8
  8. package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +1 -1
  9. package/scripts/harness/agent-integration/domain/ports/error-guidance-query-port.ts +0 -8
  10. package/scripts/harness/agent-integration/domain/ports/phase-gate-query-port.ts +6 -0
  11. package/scripts/harness/agent-integration/domain/services/fallback-verification-service.ts +2 -17
  12. package/scripts/harness/agent-integration/index.ts +3 -4
  13. package/scripts/harness/agent-integration/infrastructure/adapters/child-process-cli-executor-adapter.ts +2 -2
  14. package/scripts/harness/agent-integration/infrastructure/adapters/env-file-reentry-guard-state-adapter.ts +2 -10
  15. package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +30 -0
  16. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +2 -17
  17. package/scripts/harness/agent-integration/presentation/user-prompt-submit-hook.ts +2 -21
  18. package/scripts/harness/biome-ast-engine/composition-root.ts +2 -18
  19. package/scripts/harness/biome-ast-engine/domain/services/lint-runner.ts +19 -3
  20. package/scripts/harness/biome-ast-engine/domain/services/rule-definition-registry.ts +29 -3
  21. package/scripts/harness/biome-ast-engine/domain/value-objects/import-graph.ts +22 -6
  22. package/scripts/harness/biome-ast-engine/domain/value-objects/layer-name.ts +1 -1
  23. package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +38 -48
  24. package/scripts/harness/biome-ast-engine/infrastructure/parsers/layer-comment-parser.ts +2 -8
  25. package/scripts/harness/biome-ast-engine/infrastructure/parsers/unit-comment-parser.ts +3 -10
  26. package/scripts/harness/ci-governance/application/usecases/generate-ci-template-usecase.ts +2 -2
  27. package/scripts/harness/ci-governance/domain/types/lesson-artifact.ts +5 -13
  28. package/scripts/harness/ci-governance/domain/types/template-type.ts +2 -6
  29. package/scripts/harness/ci-governance/domain/types/trigger-condition.ts +2 -6
  30. package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +2 -6
  31. package/scripts/harness/ci-governance/infrastructure/adapters/validator-id-registry-adapter.ts +2 -6
  32. package/scripts/harness/ci-governance/infrastructure/schema/error-history-schema.ts +2 -6
  33. package/scripts/harness/ci-governance/presentation/handlers/migrate-agents-md-handler.ts +1 -1
  34. package/scripts/harness/config-foundation/domain/errors/config-foundation-domain-error.ts +3 -6
  35. package/scripts/harness/config-foundation/domain/errors/invalid-preset-error.ts +3 -6
  36. package/scripts/harness/config-foundation/domain/harness-config.ts +2 -1
  37. package/scripts/harness/config-foundation/domain/value-objects/phase-dependencies-config.ts +2 -1
  38. package/scripts/harness/config-foundation/index.ts +2 -11
  39. package/scripts/harness/harness-error/application/dto/error-definition-summary.ts +3 -6
  40. package/scripts/harness/harness-error/application/dto/harness-error-contract.ts +3 -6
  41. package/scripts/harness/harness-error/application/dto/validator-issue-draft.ts +3 -6
  42. package/scripts/harness/harness-error/domain/errors/adr-reference-not-found-error.ts +3 -6
  43. package/scripts/harness/harness-error/domain/errors/duplicate-error-code-error.ts +3 -6
  44. package/scripts/harness/harness-error/domain/errors/invalid-adr-ref-error.ts +3 -6
  45. package/scripts/harness/harness-error/domain/errors/invalid-error-code-error.ts +3 -6
  46. package/scripts/harness/harness-error/domain/errors/invalid-severity-error.ts +3 -6
  47. package/scripts/harness/harness-error/domain/errors/missing-adr-ref-error.ts +3 -6
  48. package/scripts/harness/harness-error/domain/errors/missing-fix-example-error.ts +3 -6
  49. package/scripts/harness/harness-error/domain/errors/severity-downgrade-violation-error.ts +3 -6
  50. package/scripts/harness/harness-error/domain/errors/unknown-error-definition-error.ts +3 -6
  51. package/scripts/harness/harness-error/domain/ports/fix-example-validator-port.ts +3 -7
  52. package/scripts/harness/nyquist-validation/application/dto/analyze-impact-output.ts +2 -6
  53. package/scripts/harness/nyquist-validation/infrastructure/adapters/ajv-json-schema-validator-adapter.ts +7 -19
  54. package/scripts/harness/nyquist-validation/infrastructure/adapters/traceability-model-story-registry-adapter.ts +4 -8
  55. package/scripts/harness/phase-dependency-model/application/usecases/check-story-reflection-usecase.ts +2 -13
  56. package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +2 -6
  57. package/scripts/harness/quick-mode/application/dto/change-category-classification-contract.ts +3 -7
  58. package/scripts/harness/quick-mode/index.ts +2 -13
  59. package/scripts/harness/setup/skill-deployer.ts +3 -58
  60. package/scripts/harness/skill-quality/infrastructure/adapters/ajv-lesson-artifact-schema-adapter.ts +3 -3
  61. package/scripts/harness/traceability-model/domain/services/metadata-validator.ts +1 -1
  62. package/scripts/harness/traceability-model/domain/value-objects/story-id.ts +3 -3
  63. package/scripts/harness/traceability-model/infrastructure/parsers/story-catalog-parser.ts +2 -2
  64. package/scripts/harness/validator-system/application/dto/run-full-validation-input.ts +4 -10
  65. package/scripts/harness/validator-system/domain/ports/adr-reference-port.ts +2 -6
  66. package/scripts/harness/validator-system/domain/ports/it-test-file-analyzer-port.ts +2 -10
  67. package/scripts/harness/validator-system/domain/ports/metadata-policy-port.ts +3 -6
  68. package/scripts/harness/validator-system/domain/ports/source-analysis-port.ts +2 -7
  69. package/scripts/harness/validator-system/domain/ports/source-code-analyzer-port.ts +2 -7
  70. package/scripts/harness/validator-system/domain/ports/source-file-text-scanner-port.ts +2 -9
  71. package/scripts/harness/validator-system/domain/ports/test-quality-analyzer-port.ts +3 -6
  72. package/scripts/harness/validator-system/domain/services/cli-e2e-test-existence-service.ts +3 -11
  73. package/scripts/harness/validator-system/domain/services/stub-comment-detection-service.ts +3 -8
  74. package/scripts/harness/validator-system/infrastructure/adapters/cli-command-registry-adapter.ts +3 -7
  75. package/scripts/harness/adr-foundation/application/dto/seed-adr-definition.ts +0 -21
  76. package/scripts/harness/adr-foundation/application/usecases/seed-initial-adrs-use-case.ts +0 -84
  77. package/scripts/harness/adr-foundation/infrastructure/seeds/initial-adr-definitions.ts +0 -161
  78. package/scripts/harness/agent-integration/domain/ports/import-analyzer-port.ts +0 -13
  79. package/scripts/harness/agent-integration/infrastructure/adapters/regex-import-analyzer-adapter.ts +0 -69
  80. package/scripts/harness/ci-governance/application/dto/validate-pointers-input.ts +0 -8
  81. package/scripts/harness/ci-governance/presentation/dto/ci-governance-render-options.ts +0 -9
  82. package/scripts/harness/ci-governance/presentation/formatters/error-repetition-formatter.ts +0 -26
  83. package/scripts/harness/config-foundation/infrastructure/registries/composite-feature-registry-adapter.ts +0 -22
  84. package/scripts/harness/nyquist-validation/domain/errors/story-not-found-error.ts +0 -14
  85. package/scripts/harness/nyquist-validation/presentation/dto/cli-render-options.ts +0 -12
  86. package/scripts/harness/quick-mode/application/dto/build-relaxation-profile-input.ts +0 -12
  87. package/scripts/harness/quick-mode/application/dto/execute-quick-ci-check-input.ts +0 -11
  88. package/scripts/harness/quick-mode/application/dto/judge-quick-mode-eligibility-input.ts +0 -10
  89. package/scripts/harness/quick-mode/domain/ports/quick-mode-config-port.ts +0 -12
  90. package/scripts/harness/quick-mode/domain/ports/validator-id-registry-port.ts +0 -10
  91. package/scripts/harness/shared-kernel/harness-api.ts +0 -6
  92. package/scripts/harness/shared-kernel/quick-mode.ts +0 -15
  93. package/scripts/harness/shared-kernel/validator-system.ts +0 -14
  94. package/scripts/harness/skill-quality/application/mappers/cascade-update-result-mapper.ts +0 -16
  95. package/scripts/harness/skill-quality/application/mappers/coverage-report-mapper.ts +0 -37
  96. package/scripts/harness/skill-quality/application/mappers/lesson-artifact-mapper.ts +0 -11
  97. package/scripts/harness/skill-quality/domain/types/unit-name.ts +0 -5
  98. package/scripts/harness/traceability-model/application/dto/metadata-validation-input.ts +0 -12
  99. package/scripts/harness/traceability-model/domain/constants/traceability-link-types.ts +0 -14
@@ -1,10 +1,5 @@
1
- /**
2
- * @layer infrastructure
3
- * @unit agent-integration
4
- *
5
- * EnvFileReentryGuardStateAdapter
6
- * ReentryGuardStatePort の実装。環境変数またはtmpファイルで状態を管理する
7
- */
1
+ // @unit agent-integration
2
+ // @layer infrastructure
8
3
 
9
4
  import * as fs from 'node:fs/promises';
10
5
  import * as path from 'node:path';
@@ -30,7 +25,6 @@ export class EnvFileReentryGuardStateAdapter implements ReentryGuardStatePort {
30
25
  return process.env[ENV_VAR_NAME] === '1';
31
26
  }
32
27
 
33
- // file strategy
34
28
  try {
35
29
  await fs.access(getTmpFilePath());
36
30
  return true;
@@ -45,7 +39,6 @@ export class EnvFileReentryGuardStateAdapter implements ReentryGuardStatePort {
45
39
  return;
46
40
  }
47
41
 
48
- // file strategy
49
42
  const tmpPath = getTmpFilePath();
50
43
  await fs.writeFile(tmpPath, '', 'utf8');
51
44
  }
@@ -56,7 +49,6 @@ export class EnvFileReentryGuardStateAdapter implements ReentryGuardStatePort {
56
49
  return;
57
50
  }
58
51
 
59
- // file strategy
60
52
  const tmpPath = getTmpFilePath();
61
53
  try {
62
54
  await fs.rm(tmpPath, { force: true });
@@ -1,6 +1,9 @@
1
1
  // @unit agent-integration
2
2
  // @layer infrastructure
3
3
 
4
+ import * as path from 'node:path';
5
+ import * as fs from 'node:fs/promises';
6
+
4
7
  import type { PhaseGateQueryPort } from '../../domain/ports/phase-gate-query-port.js';
5
8
  import type { WriteTargetScope } from '../../domain/value-objects/write-target-scope.js';
6
9
  import { PhaseGateQueryResult } from '../../domain/value-objects/phase-gate-query-result.js';
@@ -8,6 +11,8 @@ import { toPhaseConfigSection } from '../../../config-foundation/application/map
8
11
  import { createConfigFoundationModule } from '../../../config-foundation/composition-root.js';
9
12
  import { ConfigValidationError } from '../../../config-foundation/domain/errors/config-validation-error.js';
10
13
 
14
+ const REQUIRED_DESIGN_DOCS: readonly string[] = Object.freeze(['logical_design.md', 'domain_model.md']);
15
+
11
16
  export class PhaseGateQueryAdapter implements PhaseGateQueryPort {
12
17
  async checkGate(scope: WriteTargetScope, targetFilePath?: string): Promise<PhaseGateQueryResult> {
13
18
  try {
@@ -43,4 +48,29 @@ export class PhaseGateQueryAdapter implements PhaseGateQueryPort {
43
48
  return PhaseGateQueryResult.create(true, [], ['phase-dependency-model not available']);
44
49
  }
45
50
  }
51
+
52
+ async checkDesignDocsExist(unitId: string): Promise<boolean> {
53
+ if (unitId === '') {
54
+ return false;
55
+ }
56
+
57
+ try {
58
+ const configModule = createConfigFoundationModule();
59
+ const resolvedConfig = await configModule.usecases.loadResolvedConfigUseCase.execute();
60
+ const designDocsRoot = resolvedConfig.config.paths.designDocs;
61
+ const unitDir = path.resolve(process.cwd(), designDocsRoot, unitId);
62
+
63
+ for (const docName of REQUIRED_DESIGN_DOCS) {
64
+ try {
65
+ await fs.access(path.join(unitDir, docName));
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ return true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
46
76
  }
@@ -1,20 +1,5 @@
1
- /**
2
- * @layer presentation
3
- * @unit agent-integration
4
- *
5
- * SessionStart Hook Adapter (ISSUE-013 Wave 3 / C-4)
6
- *
7
- * Codex CLI のセッション開始時に phase-gate 状態 (運用ルール + 保護ファイル一覧 +
8
- * ブロック中 Unit) を `hookSpecificOutput.additionalContext` として注入する。
9
- *
10
- * 出力スキーマ (Codex 公式):
11
- * {
12
- * "hookSpecificOutput": {
13
- * "hookEventName": "SessionStart",
14
- * "additionalContext": "<developer context を string で>"
15
- * }
16
- * }
17
- */
1
+ // @unit agent-integration
2
+ // @layer presentation
18
3
 
19
4
  import { buildSessionStartContext, collectPhasegateStatus } from './phasegate-status-context.js';
20
5
 
@@ -1,24 +1,5 @@
1
- /**
2
- * @layer presentation
3
- * @unit agent-integration
4
- *
5
- * UserPromptSubmit Hook Adapter (ISSUE-013 Wave 3 / C-5)
6
- *
7
- * Codex CLI の UserPromptSubmit 時 (ユーザーが prompt を送信する毎) に、
8
- * 最新の phase-gate 状態を簡潔に `hookSpecificOutput.additionalContext` として注入する。
9
- *
10
- * SessionStart と異なり毎ターン発火するため、運用ルールの再掲は省いて
11
- * 「現在の保護ファイル数」「ブロック中 Unit 数と内訳」のみを通知する。
12
- * ルール本体は SessionStart hook で既に injection 済みの前提。
13
- *
14
- * 出力スキーマ (Codex 公式):
15
- * {
16
- * "hookSpecificOutput": {
17
- * "hookEventName": "UserPromptSubmit",
18
- * "additionalContext": "<developer context を string で>"
19
- * }
20
- * }
21
- */
1
+ // @unit agent-integration
2
+ // @layer presentation
22
3
 
23
4
  import * as path from 'node:path';
24
5
  import {
@@ -1,12 +1,6 @@
1
- /**
2
- * @layer composition
3
- * @unit biome-ast-engine
4
- *
5
- * biome-ast-engine ユニットの Composition Root。
6
- * 全コンポーネントを生成・配線し、外部に公開するハンドラーを返す。
7
- */
1
+ // @unit biome-ast-engine
2
+ // @layer composition
8
3
 
9
- // Infrastructure adapters
10
4
  import { SystemClockAdapter } from './infrastructure/adapters/system-clock-adapter.js';
11
5
  import { HarnessConfigProviderAdapter, type L1ConfigInput } from './infrastructure/adapters/harness-config-provider-adapter.js';
12
6
  import { HarnessErrorFormatterAdapter } from './infrastructure/adapters/harness-error-formatter-adapter.js';
@@ -14,20 +8,14 @@ import { NodeWorkspaceFileAdapter } from './infrastructure/adapters/node-workspa
14
8
  import { TypeScriptSourceModuleAnalyzerAdapter } from './infrastructure/adapters/typescript-source-module-analyzer-adapter.js';
15
9
  import { BiomeCliExecutorAdapter } from './infrastructure/adapters/biome-cli-executor-adapter.js';
16
10
  import { WorkspaceInventoryAdapter } from './infrastructure/adapters/workspace-inventory-adapter.js';
17
-
18
- // Domain services
19
11
  import { RuleDefinitionRegistry } from './domain/services/rule-definition-registry.js';
20
12
  import { ImportGraphBuilder } from './domain/services/import-graph-builder.js';
21
13
  import { LintRunner } from './domain/services/lint-runner.js';
22
-
23
- // Usecases
24
14
  import { ResolveEnabledRulesUseCase } from './application/usecases/resolve-enabled-rules-usecase.js';
25
15
  import { AnalyzeImportGraphUseCase } from './application/usecases/analyze-import-graph-usecase.js';
26
16
  import { ExecuteLintUseCase } from './application/usecases/execute-lint-usecase.js';
27
17
  import { VerifyEslintRemovalUseCase } from './application/usecases/verify-eslint-removal-usecase.js';
28
18
  import { BuildHarnessErrorPayloadUseCase } from './application/usecases/build-harness-error-payload-usecase.js';
29
-
30
- // Presentation
31
19
  import { HarnessLintCommandHandler } from './presentation/cli/harness-lint-command-handler.js';
32
20
 
33
21
  export interface BiomeAstEngineModuleOptions {
@@ -38,7 +26,6 @@ export function createBiomeAstEngineModule(
38
26
  rootDir: string,
39
27
  options?: BiomeAstEngineModuleOptions,
40
28
  ) {
41
- // Infrastructure
42
29
  const clockPort = new SystemClockAdapter();
43
30
  const ruleConfigProviderPort = new HarnessConfigProviderAdapter(options?.l1Config);
44
31
  const violationFormatterPort = new HarnessErrorFormatterAdapter();
@@ -47,12 +34,10 @@ export function createBiomeAstEngineModule(
47
34
  const biomeExecutorPort = new BiomeCliExecutorAdapter({ cwd: rootDir });
48
35
  const workspaceInventoryPort = new WorkspaceInventoryAdapter({ rootDir });
49
36
 
50
- // Domain services
51
37
  const ruleDefinitionRegistry = new RuleDefinitionRegistry();
52
38
  const importGraphBuilder = new ImportGraphBuilder();
53
39
  const lintRunner = new LintRunner(ruleDefinitionRegistry);
54
40
 
55
- // Usecases
56
41
  const resolveEnabledRulesUseCase = new ResolveEnabledRulesUseCase({
57
42
  ruleConfigProviderPort,
58
43
  ruleDefinitionRegistry,
@@ -80,7 +65,6 @@ export function createBiomeAstEngineModule(
80
65
  violationFormatterPort,
81
66
  });
82
67
 
83
- // Presentation handler
84
68
  const harnessLintCommandHandler = new HarnessLintCommandHandler({
85
69
  executeLintUseCase,
86
70
  verifyEslintRemovalUseCase,
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { RuleDefinitionRegistry } from './rule-definition-registry.js';
7
- import { ImportGraph } from '../value-objects/import-graph.js';
7
+ import { ImportGraph, matchesPattern } from '../value-objects/import-graph.js';
8
8
  import { LayerBoundary } from '../value-objects/layer-boundary.js';
9
9
  import { LintReport } from '../value-objects/lint-report.js';
10
10
  import { RuleDefinition } from '../value-objects/rule-definition.js';
@@ -97,9 +97,12 @@ export class LintRunner {
97
97
  break;
98
98
  }
99
99
  case 'no-layer-violation': {
100
+ const ignorePatterns = toStringArray(rule.config.ignorePatterns, Object.freeze([]));
101
+
100
102
  for (const edge of params.importGraph.findLayerViolations(
101
103
  LayerBoundary.standardMatrix(),
102
- layerByFile
104
+ layerByFile,
105
+ ignorePatterns
103
106
  )) {
104
107
  const fromSnapshot = snapshotByPath.get(edge.from.toString());
105
108
  if (fromSnapshot && isTestFile(fromSnapshot)) {
@@ -137,10 +140,16 @@ export class LintRunner {
137
140
  break;
138
141
  }
139
142
  case 'enforce-folder-structure': {
143
+ const ignorePatterns = toStringArray(rule.config.ignorePatterns, Object.freeze([]));
144
+
140
145
  for (const snapshot of params.snapshots) {
141
146
  if (isTestFile(snapshot)) {
142
147
  continue;
143
148
  }
149
+ const filePathStr = snapshot.filePath.toString();
150
+ if (ignorePatterns.some((pattern) => matchesPattern(filePathStr, pattern))) {
151
+ continue;
152
+ }
144
153
  if (snapshot.declaredLayer !== null && !snapshot.belongsToLayerDirectory()) {
145
154
  ruleViolations.push(
146
155
  RuleViolation.create({
@@ -222,8 +231,15 @@ export class LintRunner {
222
231
  }
223
232
  case 'no-ghost-file': {
224
233
  const ignorePatterns = toStringArray(rule.config.ignorePatterns, Object.freeze([]));
234
+ const entryPointPatterns = toStringArray(
235
+ rule.config.entryPointPatterns,
236
+ Object.freeze([])
237
+ );
225
238
 
226
- for (const filePath of params.importGraph.findGhostFiles(ignorePatterns)) {
239
+ for (const filePath of params.importGraph.findGhostFiles(
240
+ ignorePatterns,
241
+ entryPointPatterns
242
+ )) {
227
243
  ruleViolations.push(
228
244
  RuleViolation.create({
229
245
  filePath,
@@ -68,7 +68,18 @@ export class RuleDefinitionRegistry {
68
68
  'enforce-folder-structure',
69
69
  'L1-004',
70
70
  Object.freeze([REQUIRED_INPUT.sourceModuleSnapshots]),
71
- Object.freeze({ rootDir: 'scripts/harness', allowTestFixtures: true }),
71
+ Object.freeze({
72
+ rootDir: 'scripts/harness',
73
+ allowTestFixtures: true,
74
+ ignorePatterns: Object.freeze([
75
+ '**/composition-root.ts',
76
+ '**/index.ts',
77
+ '**/main.ts',
78
+ '**/shared-kernel/**',
79
+ '**/integrations/**',
80
+ '**/setup/**',
81
+ ]),
82
+ }),
72
83
  'フォルダ構造がレイヤーと一致していることを検証する',
73
84
  '宣言レイヤーと配置ディレクトリを一致させる'
74
85
  ),
@@ -101,7 +112,14 @@ export class RuleDefinitionRegistry {
101
112
  'L1-007',
102
113
  Object.freeze([REQUIRED_INPUT.importGraph]),
103
114
  Object.freeze({
104
- entryPointPatterns: Object.freeze(['**/index.ts', '**/cli/**/*.ts']),
115
+ entryPointPatterns: Object.freeze([
116
+ '**/index.ts',
117
+ '**/cli/**/*.ts',
118
+ '**/*.config*.ts',
119
+ '**/main.ts',
120
+ '**/composition-root.ts',
121
+ '**/presentation/*-hook.ts',
122
+ ]),
105
123
  ignorePatterns: Object.freeze(['**/*.test.ts', '**/*.spec.ts']),
106
124
  }),
107
125
  '未参照ファイルを検出する',
@@ -111,7 +129,15 @@ export class RuleDefinitionRegistry {
111
129
  'no-layer-violation',
112
130
  'L1-003',
113
131
  Object.freeze([REQUIRED_INPUT.sourceModuleSnapshots, REQUIRED_INPUT.importGraph]),
114
- Object.freeze({ ignorePatterns: Object.freeze(['**/shared-kernel/**']) }),
132
+ Object.freeze({
133
+ ignorePatterns: Object.freeze([
134
+ '**/shared-kernel/**',
135
+ '**/composition-root.ts',
136
+ '**/main.ts',
137
+ '**/presentation/*-hook.ts',
138
+ '**/index.ts',
139
+ ]),
140
+ }),
115
141
  'レイヤー違反を検出する',
116
142
  '依存方向をアーキテクチャ方針に合わせる'
117
143
  ),
@@ -62,7 +62,7 @@ const globToRegex = (pattern: string): RegExp => {
62
62
  return new RegExp(`^${output}$`);
63
63
  };
64
64
 
65
- const matchesPattern = (value: string, pattern: string): boolean => {
65
+ export const matchesPattern = (value: string, pattern: string): boolean => {
66
66
  return globToRegex(pattern).test(value) || value.includes(pattern.replace(/\*/g, '').replace(/\//g, ''));
67
67
  };
68
68
 
@@ -169,11 +169,18 @@ export class ImportGraph {
169
169
 
170
170
  findLayerViolations(
171
171
  boundaries: readonly LayerBoundary[],
172
- layerByFile: ReadonlyMap<string, LayerName>
172
+ layerByFile: ReadonlyMap<string, LayerName>,
173
+ ignorePatterns: readonly string[] = []
173
174
  ): readonly ImportEdge[] {
174
175
  return Object.freeze(
175
176
  this.edges.filter((edge) => {
176
- const sourceLayer = layerByFile.get(edge.from.toString());
177
+ const fromStr = edge.from.toString();
178
+
179
+ if (ignorePatterns.some((pattern) => matchesPattern(fromStr, pattern))) {
180
+ return false;
181
+ }
182
+
183
+ const sourceLayer = layerByFile.get(fromStr);
177
184
  const targetLayer = layerByFile.get(edge.to.toString());
178
185
 
179
186
  if (sourceLayer === undefined || targetLayer === undefined) {
@@ -189,12 +196,21 @@ export class ImportGraph {
189
196
  );
190
197
  }
191
198
 
192
- findGhostFiles(ignorePatterns: readonly string[]): readonly FilePath[] {
199
+ findGhostFiles(
200
+ ignorePatterns: readonly string[],
201
+ entryPointPatterns: readonly string[] = []
202
+ ): readonly FilePath[] {
193
203
  const rootNodeSet = new Set(this.rootNodes.map((node) => node.toString()));
194
204
 
195
205
  return Object.freeze(
196
206
  this.nodes.filter((node) => {
197
- if (rootNodeSet.has(node.toString())) {
207
+ const nodeStr = node.toString();
208
+
209
+ if (rootNodeSet.has(nodeStr)) {
210
+ return false;
211
+ }
212
+
213
+ if (entryPointPatterns.some((pattern) => matchesPattern(nodeStr, pattern))) {
198
214
  return false;
199
215
  }
200
216
 
@@ -202,7 +218,7 @@ export class ImportGraph {
202
218
  return false;
203
219
  }
204
220
 
205
- return !ignorePatterns.some((pattern) => matchesPattern(node.toString(), pattern));
221
+ return !ignorePatterns.some((pattern) => matchesPattern(nodeStr, pattern));
206
222
  })
207
223
  );
208
224
  }
@@ -16,7 +16,7 @@ const ALLOWED_DEPENDENCIES: Readonly<Record<LayerNameValue, readonly LayerNameVa
16
16
  domain: Object.freeze(['domain']),
17
17
  application: Object.freeze(['application', 'domain']),
18
18
  infrastructure: Object.freeze(['infrastructure', 'application', 'domain']),
19
- presentation: Object.freeze(['presentation', 'application']),
19
+ presentation: Object.freeze(['presentation', 'application', 'domain']),
20
20
  };
21
21
 
22
22
  export class InvalidLayerNameError extends Error {
@@ -84,37 +84,47 @@ export class TypeScriptSourceModuleAnalyzerAdapter implements SourceModuleAnalyz
84
84
 
85
85
  private extractImports(sourceFile: ts.SourceFile, fromFile: FilePath): ImportEdge[] {
86
86
  const edges: ImportEdge[] = [];
87
+ const fromDir = path.dirname(path.resolve(this.rootDir, fromFile.toString()));
87
88
 
88
- ts.forEachChild(sourceFile, (node) => {
89
- if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
90
- const specifier = node.moduleSpecifier.text;
89
+ const pushEdge = (specifier: string, importKind: 'value' | 'type' | 'dynamic'): void => {
90
+ if (!specifier.startsWith('.')) {
91
+ return;
92
+ }
91
93
 
92
- if (!specifier.startsWith('.')) {
93
- return;
94
- }
94
+ const resolved = path.resolve(fromDir, specifier.replace(/\.js$/, '.ts'));
95
+ const relative = path.relative(this.rootDir, resolved);
95
96
 
96
- const fromDir = path.dirname(path.resolve(this.rootDir, fromFile.toString()));
97
- const resolved = path.resolve(fromDir, specifier.replace(/\.js$/, '.ts'));
98
- const relative = path.relative(this.rootDir, resolved);
97
+ if (relative.startsWith('..')) {
98
+ return;
99
+ }
99
100
 
100
- if (relative.startsWith('..')) {
101
- return;
102
- }
101
+ try {
102
+ edges.push(
103
+ ImportEdge.create({
104
+ from: fromFile,
105
+ to: FilePathVO.fromWorkspaceRelative(relative),
106
+ importKind,
107
+ })
108
+ );
109
+ } catch {
110
+ // invalid path — skip
111
+ }
112
+ };
103
113
 
114
+ const visit = (node: ts.Node): void => {
115
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
104
116
  const importKind =
105
117
  node.importClause?.isTypeOnly === true ? 'type' as const : 'value' as const;
118
+ pushEdge(node.moduleSpecifier.text, importKind);
119
+ }
106
120
 
107
- try {
108
- edges.push(
109
- ImportEdge.create({
110
- from: fromFile,
111
- to: FilePathVO.fromWorkspaceRelative(relative),
112
- importKind,
113
- })
114
- );
115
- } catch {
116
- // invalid path — skip
117
- }
121
+ if (
122
+ ts.isExportDeclaration(node) &&
123
+ node.moduleSpecifier !== undefined &&
124
+ ts.isStringLiteral(node.moduleSpecifier)
125
+ ) {
126
+ const importKind = node.isTypeOnly === true ? 'type' as const : 'value' as const;
127
+ pushEdge(node.moduleSpecifier.text, importKind);
118
128
  }
119
129
 
120
130
  if (
@@ -123,33 +133,13 @@ export class TypeScriptSourceModuleAnalyzerAdapter implements SourceModuleAnalyz
123
133
  node.arguments.length > 0 &&
124
134
  ts.isStringLiteral(node.arguments[0])
125
135
  ) {
126
- const specifier = node.arguments[0].text;
127
-
128
- if (!specifier.startsWith('.')) {
129
- return;
130
- }
131
-
132
- const fromDir = path.dirname(path.resolve(this.rootDir, fromFile.toString()));
133
- const resolved = path.resolve(fromDir, specifier.replace(/\.js$/, '.ts'));
134
- const relative = path.relative(this.rootDir, resolved);
136
+ pushEdge(node.arguments[0].text, 'dynamic');
137
+ }
135
138
 
136
- if (relative.startsWith('..')) {
137
- return;
138
- }
139
+ ts.forEachChild(node, visit);
140
+ };
139
141
 
140
- try {
141
- edges.push(
142
- ImportEdge.create({
143
- from: fromFile,
144
- to: FilePathVO.fromWorkspaceRelative(relative),
145
- importKind: 'dynamic',
146
- })
147
- );
148
- } catch {
149
- // invalid path — skip
150
- }
151
- }
152
- });
142
+ ts.forEachChild(sourceFile, visit);
153
143
 
154
144
  return edges;
155
145
  }
@@ -1,7 +1,5 @@
1
- /**
2
- * @layer infrastructure
3
- * @unit biome-ast-engine
4
- */
1
+ // @unit biome-ast-engine
2
+ // @layer infrastructure
5
3
 
6
4
  const LAYER_COMMENT_PATTERN = /^\s*(?:\/\/|\/\*\*?\s*|\*)\s*@layer\s+(\S+)/m;
7
5
 
@@ -9,10 +7,6 @@ export type LayerCommentResult = {
9
7
  readonly layerName: string | null;
10
8
  };
11
9
 
12
- /**
13
- * ソースコードから `// @layer {layer}` コメントを抽出する。
14
- * JSDoc (`/** @layer ... * /`) 形式にも対応する。
15
- */
16
10
  export const parseLayerComment = (sourceCode: string): LayerCommentResult => {
17
11
  const match = sourceCode.match(LAYER_COMMENT_PATTERN);
18
12
 
@@ -1,20 +1,13 @@
1
- /**
2
- * @layer infrastructure
3
- * @unit biome-ast-engine
4
- */
1
+ // @unit biome-ast-engine
2
+ // @layer infrastructure
5
3
 
4
+ // カンマ区切り複数ユニット対応 + 複数行の @unit を収集、重複除去。
6
5
  const UNIT_COMMENT_PATTERN = /^\s*(?:\/\/|\/\*\*?\s*|\*)\s*@unit\s+(.+)/gm;
7
6
 
8
7
  export type UnitCommentResult = {
9
8
  readonly unitNames: readonly string[];
10
9
  };
11
10
 
12
- /**
13
- * ソースコードから `// @unit {unit}` コメントを抽出する。
14
- * JSDoc (`/** @unit ... * /`) 形式にも対応する。
15
- * カンマ区切りで複数ユニットを指定でき、複数行の @unit も収集する。
16
- * 結果は重複除去済み。
17
- */
18
11
  export const parseUnitComment = (sourceCode: string): UnitCommentResult => {
19
12
  const matches = sourceCode.matchAll(UNIT_COMMENT_PATTERN);
20
13
  const names: string[] = [];
@@ -9,7 +9,7 @@ import type { TemplateGenerator } from '../../domain/services/template-generator
9
9
  import type { GenerateCiTemplateInput } from '../dto/generate-ci-template-input.js';
10
10
  import type { GenerateCiTemplateOutput } from '../dto/generate-ci-template-output.js';
11
11
  import { CiTemplate } from '../../domain/aggregates/ci-template.js';
12
- import { isTemplateType, TEMPLATE_TYPES } from '../../domain/types/template-type.js';
12
+ import { isTemplateType, TEMPLATE_TYPES, type TemplateType } from '../../domain/types/template-type.js';
13
13
 
14
14
  export class GenerateCiTemplateUseCase {
15
15
  constructor(private readonly templateGenerator: TemplateGenerator) {}
@@ -23,7 +23,7 @@ export class GenerateCiTemplateUseCase {
23
23
  ? ` (Hint: --type specifies the template purpose, not the CI platform. Use one of: ${validValues})`
24
24
  : ` (valid: ${validValues})`;
25
25
  return {
26
- templateType: templateType as any,
26
+ templateType: templateType as TemplateType,
27
27
  presetRef: presetId,
28
28
  triggerCondition: 'pull_request',
29
29
  targetValidatorIds: [],
@@ -1,11 +1,6 @@
1
- /**
2
- * @layer domain
3
- * @unit ci-governance
4
- *
5
- * LessonArtifact型定義(Cross-Unit Contract所有)
6
- * Schema Owner: ci-governance Unit
7
- * Cross-Unit Contract: docs/contracts/lesson-artifact.schema.json
8
- */
1
+ // @unit ci-governance
2
+ // @layer domain
3
+ // Cross-Unit Contract: docs/contracts/lesson-artifact.schema.json
9
4
 
10
5
  import type { LessonCategory } from './lesson-category.js';
11
6
 
@@ -14,12 +9,9 @@ export type { LessonCategory };
14
9
  export interface LessonArtifact {
15
10
  /** UUID形式の一意識別子(INV-12: UUID形式必須) */
16
11
  readonly lessonId: string;
17
- /** lesson artifactの出力元スキル名(例: 'story-implementor', 'domain-designer') */
18
12
  readonly source: string;
19
- /** AGENTS.mdに集約するlesson内容テキスト */
20
13
  readonly content: string;
21
- /** lessonのカテゴリタグ(重複集約時はマージされる) */
22
14
  readonly tags: LessonCategory[];
23
- /** artifact作成日時(ISO 8601形式) */
24
- readonly timestamp: string; // ISO8601DateString
15
+ /** ISO 8601形式 */
16
+ readonly timestamp: string;
25
17
  }
@@ -1,9 +1,5 @@
1
- /**
2
- * @layer domain
3
- * @unit ci-governance
4
- *
5
- * TemplateType補助型
6
- */
1
+ // @unit ci-governance
2
+ // @layer domain
7
3
 
8
4
  export type TemplateType = 'aidlc-gate' | 'consistency-check' | 'pre-commit';
9
5
 
@@ -1,9 +1,5 @@
1
- /**
2
- * @layer domain
3
- * @unit ci-governance
4
- *
5
- * TriggerCondition補助型
6
- */
1
+ // @unit ci-governance
2
+ // @layer domain
7
3
 
8
4
  export type TriggerCondition = 'pull_request' | 'schedule' | 'pre-commit';
9
5
 
@@ -1,9 +1,5 @@
1
- /**
2
- * @layer infrastructure
3
- * @unit ci-governance
4
- *
5
- * CommandExistencePort実装
6
- */
1
+ // @unit ci-governance
2
+ // @layer infrastructure
7
3
 
8
4
  import type { CommandExistencePort } from '../../domain/ports/command-existence-port.js';
9
5
 
@@ -1,9 +1,5 @@
1
- /**
2
- * @layer infrastructure
3
- * @unit ci-governance
4
- *
5
- * ValidatorIdRegistryPort実装
6
- */
1
+ // @unit ci-governance
2
+ // @layer infrastructure
7
3
 
8
4
  import type { ValidatorIdRegistryPort } from '../../domain/ports/validator-id-registry-port.js';
9
5
 
@@ -1,9 +1,5 @@
1
- /**
2
- * @layer infrastructure
3
- * @unit ci-governance
4
- *
5
- * .harness/error-history.json スキーマ定義
6
- */
1
+ // @unit ci-governance
2
+ // @layer infrastructure
7
3
 
8
4
  export interface ErrorHistoryEntry {
9
5
  readonly code: string;
@@ -36,7 +36,7 @@ export class MigrateAgentsMdHandler {
36
36
  }
37
37
  const lines = vResult.passed
38
38
  ? ['✓ All pointers are valid']
39
- : ['❌ Dead pointers detected', ...vResult.errors.map((e: any) => ` [${e.code}] ${e.message}`)];
39
+ : ['❌ Dead pointers detected', ...vResult.errors.map((e) => ` [${e.code}] ${e.message}`)];
40
40
  return { exitCode: vResult.passed ? 0 : 1, output: lines.join('\n'), errors: vResult.errors };
41
41
  }
42
42