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.
- package/CHANGELOG.md +267 -0
- package/docs/ADR/ADR-014-presentation-domain-dependency.md +82 -0
- package/package.json +1 -1
- package/scripts/harness/adr-foundation/application/dto/application-errors.ts +0 -13
- package/scripts/harness/agent-integration/{infrastructure → application}/ports/cli-executor-port.ts +2 -2
- package/scripts/harness/agent-integration/application/usecases/handle-post-tool-use-usecase.ts +2 -2
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +28 -8
- package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +1 -1
- package/scripts/harness/agent-integration/domain/ports/error-guidance-query-port.ts +0 -8
- package/scripts/harness/agent-integration/domain/ports/phase-gate-query-port.ts +6 -0
- package/scripts/harness/agent-integration/domain/services/fallback-verification-service.ts +2 -17
- package/scripts/harness/agent-integration/index.ts +3 -4
- package/scripts/harness/agent-integration/infrastructure/adapters/child-process-cli-executor-adapter.ts +2 -2
- package/scripts/harness/agent-integration/infrastructure/adapters/env-file-reentry-guard-state-adapter.ts +2 -10
- package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +30 -0
- package/scripts/harness/agent-integration/presentation/session-start-hook.ts +2 -17
- package/scripts/harness/agent-integration/presentation/user-prompt-submit-hook.ts +2 -21
- package/scripts/harness/biome-ast-engine/composition-root.ts +2 -18
- package/scripts/harness/biome-ast-engine/domain/services/lint-runner.ts +19 -3
- package/scripts/harness/biome-ast-engine/domain/services/rule-definition-registry.ts +29 -3
- package/scripts/harness/biome-ast-engine/domain/value-objects/import-graph.ts +22 -6
- package/scripts/harness/biome-ast-engine/domain/value-objects/layer-name.ts +1 -1
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +38 -48
- package/scripts/harness/biome-ast-engine/infrastructure/parsers/layer-comment-parser.ts +2 -8
- package/scripts/harness/biome-ast-engine/infrastructure/parsers/unit-comment-parser.ts +3 -10
- package/scripts/harness/ci-governance/application/usecases/generate-ci-template-usecase.ts +2 -2
- package/scripts/harness/ci-governance/domain/types/lesson-artifact.ts +5 -13
- package/scripts/harness/ci-governance/domain/types/template-type.ts +2 -6
- package/scripts/harness/ci-governance/domain/types/trigger-condition.ts +2 -6
- package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +2 -6
- package/scripts/harness/ci-governance/infrastructure/adapters/validator-id-registry-adapter.ts +2 -6
- package/scripts/harness/ci-governance/infrastructure/schema/error-history-schema.ts +2 -6
- package/scripts/harness/ci-governance/presentation/handlers/migrate-agents-md-handler.ts +1 -1
- package/scripts/harness/config-foundation/domain/errors/config-foundation-domain-error.ts +3 -6
- package/scripts/harness/config-foundation/domain/errors/invalid-preset-error.ts +3 -6
- package/scripts/harness/config-foundation/domain/harness-config.ts +2 -1
- package/scripts/harness/config-foundation/domain/value-objects/phase-dependencies-config.ts +2 -1
- package/scripts/harness/config-foundation/index.ts +2 -11
- package/scripts/harness/harness-error/application/dto/error-definition-summary.ts +3 -6
- package/scripts/harness/harness-error/application/dto/harness-error-contract.ts +3 -6
- package/scripts/harness/harness-error/application/dto/validator-issue-draft.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/adr-reference-not-found-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/duplicate-error-code-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/invalid-adr-ref-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/invalid-error-code-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/invalid-severity-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/missing-adr-ref-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/missing-fix-example-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/severity-downgrade-violation-error.ts +3 -6
- package/scripts/harness/harness-error/domain/errors/unknown-error-definition-error.ts +3 -6
- package/scripts/harness/harness-error/domain/ports/fix-example-validator-port.ts +3 -7
- package/scripts/harness/nyquist-validation/application/dto/analyze-impact-output.ts +2 -6
- package/scripts/harness/nyquist-validation/infrastructure/adapters/ajv-json-schema-validator-adapter.ts +7 -19
- package/scripts/harness/nyquist-validation/infrastructure/adapters/traceability-model-story-registry-adapter.ts +4 -8
- package/scripts/harness/phase-dependency-model/application/usecases/check-story-reflection-usecase.ts +2 -13
- package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +2 -6
- package/scripts/harness/quick-mode/application/dto/change-category-classification-contract.ts +3 -7
- package/scripts/harness/quick-mode/index.ts +2 -13
- package/scripts/harness/setup/skill-deployer.ts +3 -58
- package/scripts/harness/skill-quality/infrastructure/adapters/ajv-lesson-artifact-schema-adapter.ts +3 -3
- package/scripts/harness/traceability-model/domain/services/metadata-validator.ts +1 -1
- package/scripts/harness/traceability-model/domain/value-objects/story-id.ts +3 -3
- package/scripts/harness/traceability-model/infrastructure/parsers/story-catalog-parser.ts +2 -2
- package/scripts/harness/validator-system/application/dto/run-full-validation-input.ts +4 -10
- package/scripts/harness/validator-system/domain/ports/adr-reference-port.ts +2 -6
- package/scripts/harness/validator-system/domain/ports/it-test-file-analyzer-port.ts +2 -10
- package/scripts/harness/validator-system/domain/ports/metadata-policy-port.ts +3 -6
- package/scripts/harness/validator-system/domain/ports/source-analysis-port.ts +2 -7
- package/scripts/harness/validator-system/domain/ports/source-code-analyzer-port.ts +2 -7
- package/scripts/harness/validator-system/domain/ports/source-file-text-scanner-port.ts +2 -9
- package/scripts/harness/validator-system/domain/ports/test-quality-analyzer-port.ts +3 -6
- package/scripts/harness/validator-system/domain/services/cli-e2e-test-existence-service.ts +3 -11
- package/scripts/harness/validator-system/domain/services/stub-comment-detection-service.ts +3 -8
- package/scripts/harness/validator-system/infrastructure/adapters/cli-command-registry-adapter.ts +3 -7
- package/scripts/harness/adr-foundation/application/dto/seed-adr-definition.ts +0 -21
- package/scripts/harness/adr-foundation/application/usecases/seed-initial-adrs-use-case.ts +0 -84
- package/scripts/harness/adr-foundation/infrastructure/seeds/initial-adr-definitions.ts +0 -161
- package/scripts/harness/agent-integration/domain/ports/import-analyzer-port.ts +0 -13
- package/scripts/harness/agent-integration/infrastructure/adapters/regex-import-analyzer-adapter.ts +0 -69
- package/scripts/harness/ci-governance/application/dto/validate-pointers-input.ts +0 -8
- package/scripts/harness/ci-governance/presentation/dto/ci-governance-render-options.ts +0 -9
- package/scripts/harness/ci-governance/presentation/formatters/error-repetition-formatter.ts +0 -26
- package/scripts/harness/config-foundation/infrastructure/registries/composite-feature-registry-adapter.ts +0 -22
- package/scripts/harness/nyquist-validation/domain/errors/story-not-found-error.ts +0 -14
- package/scripts/harness/nyquist-validation/presentation/dto/cli-render-options.ts +0 -12
- package/scripts/harness/quick-mode/application/dto/build-relaxation-profile-input.ts +0 -12
- package/scripts/harness/quick-mode/application/dto/execute-quick-ci-check-input.ts +0 -11
- package/scripts/harness/quick-mode/application/dto/judge-quick-mode-eligibility-input.ts +0 -10
- package/scripts/harness/quick-mode/domain/ports/quick-mode-config-port.ts +0 -12
- package/scripts/harness/quick-mode/domain/ports/validator-id-registry-port.ts +0 -10
- package/scripts/harness/shared-kernel/harness-api.ts +0 -6
- package/scripts/harness/shared-kernel/quick-mode.ts +0 -15
- package/scripts/harness/shared-kernel/validator-system.ts +0 -14
- package/scripts/harness/skill-quality/application/mappers/cascade-update-result-mapper.ts +0 -16
- package/scripts/harness/skill-quality/application/mappers/coverage-report-mapper.ts +0 -37
- package/scripts/harness/skill-quality/application/mappers/lesson-artifact-mapper.ts +0 -11
- package/scripts/harness/skill-quality/domain/types/unit-name.ts +0 -5
- package/scripts/harness/traceability-model/application/dto/metadata-validation-input.ts +0 -12
- package/scripts/harness/traceability-model/domain/constants/traceability-link-types.ts +0 -14
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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 });
|
package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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({
|
|
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([
|
|
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({
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
89
|
+
const pushEdge = (specifier: string, importKind: 'value' | 'type' | 'dynamic'): void => {
|
|
90
|
+
if (!specifier.startsWith('.')) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
94
|
+
const resolved = path.resolve(fromDir, specifier.replace(/\.js$/, '.ts'));
|
|
95
|
+
const relative = path.relative(this.rootDir, resolved);
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
if (relative.startsWith('..')) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
+
ts.forEachChild(node, visit);
|
|
140
|
+
};
|
|
139
141
|
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
26
|
+
templateType: templateType as TemplateType,
|
|
27
27
|
presetRef: presetId,
|
|
28
28
|
triggerCondition: 'pull_request',
|
|
29
29
|
targetValidatorIds: [],
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
/**
|
|
24
|
-
readonly timestamp: string;
|
|
15
|
+
/** ISO 8601形式 */
|
|
16
|
+
readonly timestamp: string;
|
|
25
17
|
}
|
|
@@ -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
|
|
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
|
|