phasegate 0.283.0 → 0.335.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 +16 -0
- package/docs/ADR/017-warning-severity-aggregation.md +17 -0
- package/docs/ADR/038-config-state-operation-permission-policy.md +78 -0
- package/docs/ADR/039-hook-observable-state-as-authorization-unit.md +74 -0
- package/docs/ADR/040-quick-mode-config-via-preset-resolution.md +66 -0
- package/docs/guide/cli-reference.md +89 -7
- package/docs/guide/configuration.md +43 -2
- package/docs/guide/layer-model.md +2 -0
- package/docs/guide/quick-vs-full-mode.md +63 -3
- package/docs/guide/troubleshooting.md +37 -0
- package/docs/templates/agent-context/CLAUDE.md.template.md +6 -6
- package/package.json +2 -2
- package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +5 -1
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +108 -20
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +215 -5
- package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +5 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/file-system-full-mode-session-query-adapter.ts +75 -23
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +18 -3
- package/scripts/harness/agent-integration/infrastructure/adapters/quick-mode-full-mode-requirement-adapter.ts +15 -1
- package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +57 -9
- package/scripts/harness/ci-governance/application/dto/scaffold-inception-input.ts +9 -0
- package/scripts/harness/ci-governance/application/dto/scaffold-inception-output.ts +13 -0
- package/scripts/harness/ci-governance/application/usecases/list-templates-usecase.ts +30 -0
- package/scripts/harness/ci-governance/application/usecases/scaffold-inception-usecase.ts +63 -0
- package/scripts/harness/ci-governance/application/usecases/show-template-usecase.ts +39 -0
- package/scripts/harness/ci-governance/composition-root.ts +57 -5
- package/scripts/harness/ci-governance/domain/ports/inception-doc-writer-port.ts +19 -0
- package/scripts/harness/ci-governance/domain/ports/inception-template-repository-port.ts +16 -0
- package/scripts/harness/ci-governance/domain/ports/template-catalog-port.ts +23 -0
- package/scripts/harness/ci-governance/domain/services/claude-md-composer.ts +20 -11
- package/scripts/harness/ci-governance/domain/value-objects/inception-doc-kind.ts +109 -0
- package/scripts/harness/ci-governance/domain/value-objects/template-catalog-entry.ts +53 -0
- package/scripts/harness/ci-governance/domain/value-objects/template-name.ts +50 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-inception-doc-writer-adapter.ts +42 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-inception-template-repository-adapter.ts +32 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-template-catalog-adapter.ts +60 -0
- package/scripts/harness/ci-governance/presentation/handlers/check-repetition-handler.ts +10 -2
- package/scripts/harness/ci-governance/presentation/handlers/scaffold-inception-handler.ts +101 -0
- package/scripts/harness/ci-governance/presentation/handlers/templates-handler.ts +103 -0
- package/scripts/harness/config-foundation/domain/harness-config.ts +12 -0
- package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +3 -3
- package/scripts/harness/config-foundation/infrastructure/presets/standard.json +3 -3
- package/scripts/harness/config-foundation/infrastructure/presets/strict.json +3 -3
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +44 -1
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +42 -1
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +12 -3
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +2 -22
- package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +3 -1
- package/scripts/harness/harness-error/application/dto/create-harness-error-input.ts +3 -1
- package/scripts/harness/harness-error/application/dto/harness-error-contract.ts +3 -1
- package/scripts/harness/harness-error/application/mappers/harness-error-contract-mapper.ts +9 -17
- package/scripts/harness/harness-error/application/usecases/create-harness-error-use-case.ts +7 -7
- package/scripts/harness/harness-error/domain/services/harness-error-factory.ts +30 -33
- package/scripts/harness/harness-error/domain/value-objects/error-definition.ts +28 -17
- package/scripts/harness/harness-error/domain/value-objects/harness-error.ts +30 -9
- package/scripts/harness/harness-error/domain/value-objects/remediation-type.ts +30 -0
- package/scripts/harness/harness-error/infrastructure/registry/l2-error-definitions.ts +44 -29
- package/scripts/harness/harness-error/infrastructure/registry/l3-error-definitions.ts +44 -27
- package/scripts/harness/harness-error/infrastructure/registry/l4-error-definitions.ts +47 -32
- package/scripts/harness/installation/application/checks/claude-context-missing-check.ts +13 -7
- package/scripts/harness/installation/application/checks/config-status-check.ts +52 -0
- package/scripts/harness/installation/application/checks/husky-pre-commit-missing-check.ts +6 -0
- package/scripts/harness/installation/application/ports/config-status-probe-port.ts +9 -0
- package/scripts/harness/installation/application/usecases/run-doctor-diagnostics.ts +30 -8
- package/scripts/harness/installation/application/usecases/run-install.ts +44 -11
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +44 -11
- package/scripts/harness/installation/composition-root.ts +13 -3
- package/scripts/harness/installation/domain/check-id.ts +2 -0
- package/scripts/harness/installation/domain/config-status.ts +17 -0
- package/scripts/harness/installation/domain/ports/heuristic-check.ts +10 -1
- package/scripts/harness/installation/infrastructure/adapters/config-status-probe-adapter.ts +79 -0
- package/scripts/harness/installation/presentation/cli/doctor-handler.ts +6 -1
- package/scripts/harness/installation/presentation/formatters/diagnostic-report-formatter.ts +19 -5
- package/scripts/harness/integrations/pre-commit.ts +17 -3
- package/scripts/harness/main.ts +159 -21
- package/scripts/harness/phase-dependency-model/application/services/evidence-bundle-assembler.ts +2 -1
- package/scripts/harness/phase-dependency-model/domain/ports/plan-document-reader-port.ts +12 -0
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/markdown-plan-document-reader.ts +64 -33
- package/scripts/harness/phase2-extensions/presentation/handlers/check-freshness-handler.ts +17 -9
- package/scripts/harness/quick-mode/application/ports/file-existence-port.ts +15 -0
- package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +60 -22
- package/scripts/harness/quick-mode/composition-root.ts +25 -15
- package/scripts/harness/quick-mode/domain/errors/quick-mode-config-error.ts +19 -0
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +116 -18
- package/scripts/harness/quick-mode/domain/value-objects/category-override-rules.ts +147 -0
- package/scripts/harness/quick-mode/domain/value-objects/change-category.ts +33 -0
- package/scripts/harness/quick-mode/domain/value-objects/quick-mode-config.ts +31 -10
- package/scripts/harness/quick-mode/index.ts +7 -1
- package/scripts/harness/quick-mode/infrastructure/adapters/fs-file-existence-adapter.ts +38 -0
- package/scripts/harness/quick-mode/infrastructure/adapters/harness-config-quick-mode-config-adapter.ts +83 -32
- package/scripts/harness/skill-quality/infrastructure/adapters/file-system-requirement-test-matrix-adapter.ts +51 -8
- package/scripts/harness/skill-quality/presentation/handlers/check-coverage-handler.ts +13 -6
- package/scripts/harness/traceability-model/domain/value-objects/work-item-frontmatter.ts +5 -1
- package/scripts/harness/traceability-model/infrastructure/gateways/file-system-work-item-identity-gateway.ts +6 -1
- package/scripts/harness/traceability-model/infrastructure/gateways/file-system-work-item-status-gateway.ts +15 -2
- package/scripts/harness/validator-system/application/use-cases/aggregate-validation-results-usecase.ts +11 -14
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +8 -0
- package/scripts/harness/validator-system/domain/services/effective-severity-policy.ts +39 -0
- package/scripts/harness/validator-system/domain/value-objects/consistency-report.ts +6 -4
- package/scripts/harness/validator-system/domain/value-objects/drift-report.ts +12 -7
- package/scripts/harness/validator-system/domain/value-objects/validation-result.ts +11 -3
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-security-pattern-scanner-adapter.ts +20 -17
- package/scripts/harness/validator-system/infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.ts +35 -14
- package/scripts/harness/validator-system/infrastructure/adapters/traceability-metadata-policy-adapter.ts +13 -8
- package/scripts/harness/validator-system/presentation/formatters/agent-validation-result-formatter.ts +19 -10
- package/skills/product-architect/SKILL.md +19 -0
- package/skills/quick-implementor/SKILL.md +19 -0
- package/skills/story-mapper/SKILL.md +11 -0
- package/skills/story-writer/SKILL.md +11 -0
- package/skills/unit-designer/SKILL.md +11 -0
- package/templates/product_overview.template.md +85 -0
- package/templates/product_overview_plan.template.md +55 -0
- package/templates/story_mapping_plan.template.md +61 -0
- package/templates/story_writer_plan.template.md +61 -0
- package/templates/unit_design_plan.template.md +63 -0
|
@@ -1,17 +1,59 @@
|
|
|
1
1
|
// @unit agent-integration
|
|
2
2
|
// @layer infrastructure
|
|
3
3
|
// @work-item-id WI-206
|
|
4
|
+
// @work-item-id WI-348
|
|
5
|
+
// @work-item-id WI-350
|
|
4
6
|
|
|
5
|
-
import * as fs from
|
|
6
|
-
import * as path from
|
|
7
|
-
|
|
7
|
+
import * as fs from "node:fs/promises";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import type { ConfigQueryPort } from "../../domain/ports/config-query-port.js";
|
|
8
10
|
import type {
|
|
9
11
|
FullModeSessionQueryInput,
|
|
10
12
|
FullModeSessionQueryPort,
|
|
11
13
|
FullModeSessionQueryResult,
|
|
12
|
-
} from
|
|
13
|
-
import
|
|
14
|
-
|
|
14
|
+
} from "../../domain/ports/full-mode-session-query-port.js";
|
|
15
|
+
import { WriteTargetScope } from "../../domain/value-objects/write-target-scope.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* quick-mode の `ChangeCategoryValue` と同一の語彙。
|
|
19
|
+
* 照合相手は quick-mode 分類結果(`dominantCategory`)なので、この語彙以外は
|
|
20
|
+
* session.json 側の書式誤りとして扱う。
|
|
21
|
+
* `session begin` 側との同期は
|
|
22
|
+
* `__tests__/integration/harness-api/session-begin-allowed-categories.integration.test.ts`
|
|
23
|
+
* が検証する(WI-348)。
|
|
24
|
+
*/
|
|
25
|
+
const KNOWN_CHANGE_CATEGORIES: ReadonlySet<string> = new Set([
|
|
26
|
+
"bugfix",
|
|
27
|
+
"docs",
|
|
28
|
+
"test",
|
|
29
|
+
"config",
|
|
30
|
+
"feature",
|
|
31
|
+
"domain",
|
|
32
|
+
"api",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* WI-348: v0.301.0 以前の `phasegate session begin` は allowedCategories に
|
|
37
|
+
* レイヤー名(domain / application / infrastructure / presentation / config)を
|
|
38
|
+
* 書き出していた。この語彙は ChangeCategory と domain / config でしか交差せず、
|
|
39
|
+
* 旧形式 session.json をそのまま照合すると feature / api / bugfix などが
|
|
40
|
+
* 恒常的に拒否され session が事実上無力化する。
|
|
41
|
+
*
|
|
42
|
+
* `session begin` はカテゴリ指定フラグを持たず常に定数を書き出すため、
|
|
43
|
+
* 「ChangeCategory 語彙に無い値が混ざっている」= 旧形式生成物と判断できる。
|
|
44
|
+
* その場合のみ全カテゴリ許可へ正規化する。
|
|
45
|
+
* 全要素が既知カテゴリなら手編集による意図的な絞り込みとみなし原文を尊重する。
|
|
46
|
+
* unit / 期限 / target path のスコープ判定は正規化後も従来どおり効く。
|
|
47
|
+
*/
|
|
48
|
+
function normalizeAllowedCategories(rawCategories: readonly string[]): readonly string[] {
|
|
49
|
+
if (rawCategories.length === 0) {
|
|
50
|
+
return rawCategories;
|
|
51
|
+
}
|
|
52
|
+
if (rawCategories.every((value) => KNOWN_CHANGE_CATEGORIES.has(value))) {
|
|
53
|
+
return rawCategories;
|
|
54
|
+
}
|
|
55
|
+
return [...KNOWN_CHANGE_CATEGORIES];
|
|
56
|
+
}
|
|
15
57
|
|
|
16
58
|
interface FullModeSessionDocument {
|
|
17
59
|
readonly mode?: unknown;
|
|
@@ -35,52 +77,62 @@ export class FileSystemFullModeSessionQueryAdapter implements FullModeSessionQue
|
|
|
35
77
|
async check(input: FullModeSessionQueryInput): Promise<FullModeSessionQueryResult> {
|
|
36
78
|
let document: FullModeSessionDocument;
|
|
37
79
|
try {
|
|
38
|
-
const raw = await fs.readFile(path.join(this.options.rootDir,
|
|
80
|
+
const raw = await fs.readFile(path.join(this.options.rootDir, ".phasegate", "session.json"), "utf8");
|
|
39
81
|
document = JSON.parse(raw) as FullModeSessionDocument;
|
|
40
82
|
} catch {
|
|
41
|
-
return { active: false, allowed: false, reason:
|
|
83
|
+
return { active: false, allowed: false, reason: "session marker not found or unreadable" };
|
|
42
84
|
}
|
|
43
85
|
|
|
44
|
-
if (document.mode !==
|
|
45
|
-
return { active: false, allowed: false, reason:
|
|
86
|
+
if (document.mode !== "full") {
|
|
87
|
+
return { active: false, allowed: false, reason: "session mode is not full" };
|
|
46
88
|
}
|
|
47
|
-
if (typeof document.unit !==
|
|
48
|
-
return { active: true, allowed: false, reason:
|
|
89
|
+
if (typeof document.unit !== "string" || document.unit === "") {
|
|
90
|
+
return { active: true, allowed: false, reason: "session unit is missing" };
|
|
49
91
|
}
|
|
50
|
-
if (typeof document.workItemId !==
|
|
51
|
-
return { active: true, allowed: false, reason:
|
|
92
|
+
if (typeof document.workItemId !== "string" || !/^WI-\d+$/.test(document.workItemId)) {
|
|
93
|
+
return { active: true, allowed: false, reason: "session work item is invalid" };
|
|
52
94
|
}
|
|
53
|
-
if (typeof document.expiresAt !==
|
|
54
|
-
return { active: true, allowed: false, reason:
|
|
95
|
+
if (typeof document.expiresAt !== "string" || Number.isNaN(Date.parse(document.expiresAt))) {
|
|
96
|
+
return { active: true, allowed: false, reason: "session expiry is invalid" };
|
|
55
97
|
}
|
|
56
98
|
if ((this.options.now?.() ?? new Date()).getTime() >= Date.parse(document.expiresAt)) {
|
|
57
99
|
return {
|
|
58
100
|
active: true,
|
|
59
101
|
allowed: false,
|
|
60
|
-
reason:
|
|
102
|
+
reason: "session expired",
|
|
61
103
|
workItemId: document.workItemId,
|
|
62
104
|
unit: document.unit,
|
|
63
105
|
expiresAt: document.expiresAt,
|
|
64
106
|
};
|
|
65
107
|
}
|
|
66
|
-
if (
|
|
67
|
-
|
|
108
|
+
if (
|
|
109
|
+
!Array.isArray(document.allowedCategories) ||
|
|
110
|
+
document.allowedCategories.some((value) => typeof value !== "string")
|
|
111
|
+
) {
|
|
112
|
+
return { active: true, allowed: false, reason: "session allowedCategories is invalid" };
|
|
68
113
|
}
|
|
69
|
-
|
|
114
|
+
const allowedCategories = normalizeAllowedCategories(document.allowedCategories as readonly string[]);
|
|
115
|
+
if (input.dominantCategory === undefined || !allowedCategories.includes(input.dominantCategory)) {
|
|
70
116
|
return {
|
|
71
117
|
active: true,
|
|
72
118
|
allowed: false,
|
|
73
|
-
reason: `category ${input.dominantCategory ??
|
|
119
|
+
reason: `category ${input.dominantCategory ?? "<unknown>"} is not allowed by session`,
|
|
74
120
|
workItemId: document.workItemId,
|
|
75
121
|
unit: document.unit,
|
|
76
122
|
expiresAt: document.expiresAt,
|
|
77
123
|
};
|
|
78
124
|
}
|
|
79
|
-
|
|
125
|
+
// WI-350: unit を持たないパス(プロジェクト直下のファイル・__tests__ 配下など)は
|
|
126
|
+
// 集約 unitId が undefined になる。旧実装はこれを即拒否していたが、
|
|
127
|
+
// 直下の allTargetPathsBelongToUnit は unitless パスを許容しており内部矛盾していた。
|
|
128
|
+
// 集約チェックは「unitId が定義済みかつ session unit と不一致」の場合のみ拒否し、
|
|
129
|
+
// unitless は per-path チェックへ委ねる。unit 付きパスが 1 つでも混ざれば
|
|
130
|
+
// per-path 側が従来どおり拒否するため、unit 境界は緩まない。
|
|
131
|
+
if (input.unitId !== undefined && input.unitId !== document.unit) {
|
|
80
132
|
return {
|
|
81
133
|
active: true,
|
|
82
134
|
allowed: false,
|
|
83
|
-
reason: `target unit ${input.unitId
|
|
135
|
+
reason: `target unit ${input.unitId} does not match session unit ${document.unit}`,
|
|
84
136
|
workItemId: document.workItemId,
|
|
85
137
|
unit: document.unit,
|
|
86
138
|
expiresAt: document.expiresAt,
|
|
@@ -83,9 +83,24 @@ export class HarnessConfigConfigQueryAdapter implements ConfigQueryPort {
|
|
|
83
83
|
// GitHub #40: config が JSON として壊れている場合に hook プロセス全体を throw で
|
|
84
84
|
// 落とすと、エージェントの全ツール呼び出しが遮断され config の修復自体が不能になる。
|
|
85
85
|
// main.ts の ConfigPersistenceError と同じ意味論(警告 + 既定値で続行)に揃える。
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
86
|
+
// ADR-038 §4 G1 / WI-333: config **不在**(ENOENT)も同じデッドロックを起こすため、
|
|
87
|
+
// invalid-json と同じ「警告 + 既定値で続行」の fail-open とする。config を作成する
|
|
88
|
+
// Write 自体が遮断されない(自己修復経路が残る)ことがこの adapter の契約。
|
|
89
|
+
// それ以外の fs エラー(EACCES 等の真の異常)は従来どおり throw する。
|
|
90
|
+
// gated スコープへの書き込みは phase-gate 側が既定設定で fail-closed を維持する。
|
|
91
|
+
let raw: string;
|
|
92
|
+
try {
|
|
93
|
+
raw = fs.readFileSync(this.configPath, "utf8");
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
96
|
+
process.stderr.write(
|
|
97
|
+
`Warning: phasegate.config.json not found at ${this.configPath}; continuing with default hook settings so self-repair stays possible (run \`phasegate init\` to generate one).\n`,
|
|
98
|
+
);
|
|
99
|
+
this.cachedConfig = {};
|
|
100
|
+
return this.cachedConfig;
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
89
104
|
let doc: HarnessConfigDocument;
|
|
90
105
|
try {
|
|
91
106
|
doc = JSON.parse(raw) as HarnessConfigDocument;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @unit agent-integration
|
|
2
2
|
// @layer infrastructure
|
|
3
|
+
// @work-item-id WI-373
|
|
3
4
|
|
|
4
5
|
import type {
|
|
5
6
|
FullModeRequirementQueryPort,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
FullModeTargetChange,
|
|
8
9
|
} from '../../domain/ports/full-mode-requirement-query-port.js';
|
|
9
10
|
import type { ClassifyChangeCategoryUseCase } from '../../../quick-mode/application/usecases/classify-change-category-usecase.js';
|
|
11
|
+
import { QuickModeConfigError } from '../../../quick-mode/domain/errors/quick-mode-config-error.js';
|
|
10
12
|
|
|
11
13
|
export interface QuickModeFullModeRequirementAdapterDeps {
|
|
12
14
|
classifyUseCaseFactory: () => ClassifyChangeCategoryUseCase;
|
|
@@ -45,7 +47,19 @@ export class QuickModeFullModeRequirementAdapter implements FullModeRequirementQ
|
|
|
45
47
|
rejectionReason: contract.rejectionReason,
|
|
46
48
|
dominantCategory: contract.dominantCategory ?? undefined,
|
|
47
49
|
};
|
|
48
|
-
} catch {
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// WI-373: quickMode 設定そのものが不正な場合は fail-closed。
|
|
52
|
+
// allowedCategories / categoryOverrides の enum 検証が入ったことで
|
|
53
|
+
// config の typo が例外になるため、ここで一律 fail-open のままだと
|
|
54
|
+
// 「設定を壊すと全書き込みが素通りする」という最悪の穴になる。
|
|
55
|
+
// config 不在・IO エラー等その他の例外は WI-333 の fail-open を維持する。
|
|
56
|
+
if (error instanceof QuickModeConfigError) {
|
|
57
|
+
return {
|
|
58
|
+
requiresFullMode: true,
|
|
59
|
+
rejectionRule: 'MIXED_CHANGES',
|
|
60
|
+
rejectionReason: `quickMode 設定が不正なため Quick Mode 判定を実行できません: ${error.message}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
49
63
|
return { requiresFullMode: false };
|
|
50
64
|
}
|
|
51
65
|
}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* @work-item-id WI-202 / WI-204
|
|
5
5
|
* @work-item-id WI-206
|
|
6
6
|
* @work-item-id WI-208
|
|
7
|
+
* @work-item-id WI-345
|
|
8
|
+
* @work-item-id WI-347
|
|
9
|
+
* @work-item-id WI-376
|
|
7
10
|
*
|
|
8
11
|
* PreToolUse Hook Adapter
|
|
9
12
|
* Claude Code の PreToolUse Hook エントリポイント
|
|
@@ -23,10 +26,14 @@ import { createQuickModeCompositionRoot } from '../../quick-mode/composition-roo
|
|
|
23
26
|
import * as path from 'node:path';
|
|
24
27
|
import * as fs from 'node:fs/promises';
|
|
25
28
|
|
|
29
|
+
/**
|
|
30
|
+
* WI-376 (ADR-039): 呼び出し元 skill 名を受け取るフィールド(caller_skill)は持たない。
|
|
31
|
+
* Claude Code の PreToolUse payload に skill 情報は無く、エージェントの自己申告値は
|
|
32
|
+
* authorization / guidance の入力にしない。未知キーが来ても単に無視される。
|
|
33
|
+
*/
|
|
26
34
|
interface PreToolUseHookInput {
|
|
27
35
|
cwd?: string;
|
|
28
36
|
tool_name?: string;
|
|
29
|
-
caller_skill?: string;
|
|
30
37
|
tool_input?: {
|
|
31
38
|
path?: string;
|
|
32
39
|
file_path?: string;
|
|
@@ -80,8 +87,10 @@ function projectRootForConfig(configPath: string): string {
|
|
|
80
87
|
return path.basename(configDir) === '.phasegate-local' ? path.dirname(configDir) : configDir;
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
function
|
|
84
|
-
|
|
90
|
+
function isProjectExternalPath(filePath: string, cwd: string, projectRoot: string): boolean {
|
|
91
|
+
const resolvedPath = path.resolve(cwd, filePath);
|
|
92
|
+
const relativePath = path.relative(projectRoot, resolvedPath);
|
|
93
|
+
return relativePath === '..' || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath);
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
async function main(): Promise<void> {
|
|
@@ -141,15 +150,29 @@ async function main(): Promise<void> {
|
|
|
141
150
|
const bashTargets = extractor.extract(input.tool_input.command);
|
|
142
151
|
if (bashTargets.length > 0) {
|
|
143
152
|
targetFilePaths.push(...bashTargets.map(toRelative));
|
|
153
|
+
const recordedTargetPaths = new Set(targetChanges.map((change) => change.filePath));
|
|
154
|
+
const bashTargetChanges = await Promise.all(
|
|
155
|
+
bashTargets.map((bashTarget) => buildBashTargetChange(cwd, bashTarget, toRelative)),
|
|
156
|
+
);
|
|
157
|
+
for (const change of bashTargetChanges) {
|
|
158
|
+
if (!recordedTargetPaths.has(change.filePath)) {
|
|
159
|
+
targetChanges.push(change);
|
|
160
|
+
recordedTargetPaths.add(change.filePath);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
144
163
|
effectiveToolName = 'Write';
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
|
|
148
167
|
try {
|
|
149
168
|
const configPath = await findConfigPath(cwd);
|
|
150
|
-
const projectTargetFilePaths = targetFilePaths.filter((filePath) => !isProjectExternalAbsolutePath(filePath));
|
|
151
|
-
const projectTargetChanges = targetChanges.filter((change) => !isProjectExternalAbsolutePath(change.filePath));
|
|
152
169
|
const projectRoot = projectRootForConfig(configPath);
|
|
170
|
+
const projectTargetFilePaths = targetFilePaths.filter(
|
|
171
|
+
(filePath) => !isProjectExternalPath(filePath, cwd, projectRoot),
|
|
172
|
+
);
|
|
173
|
+
const projectTargetChanges = targetChanges.filter(
|
|
174
|
+
(change) => !isProjectExternalPath(change.filePath, cwd, projectRoot),
|
|
175
|
+
);
|
|
153
176
|
const configQueryPort = new HarnessConfigConfigQueryAdapter(configPath);
|
|
154
177
|
const phaseGateQueryPort = new PhaseGateQueryAdapter();
|
|
155
178
|
const storyReflectionQueryPort = new FileSystemStoryReflectionQueryAdapter({
|
|
@@ -157,7 +180,8 @@ async function main(): Promise<void> {
|
|
|
157
180
|
configPath,
|
|
158
181
|
});
|
|
159
182
|
const fullModeRequirementQueryPort = new QuickModeFullModeRequirementAdapter({
|
|
160
|
-
classifyUseCaseFactory: () =>
|
|
183
|
+
classifyUseCaseFactory: () =>
|
|
184
|
+
createQuickModeCompositionRoot({ configPath, rootDir: projectRoot }).classifyUseCase,
|
|
161
185
|
});
|
|
162
186
|
const baselineGrandfatherQueryPort = new CiGovernanceBaselineGrandfatherAdapter({
|
|
163
187
|
baseDir: projectRoot,
|
|
@@ -180,11 +204,9 @@ async function main(): Promise<void> {
|
|
|
180
204
|
fullModeSessionQueryPort,
|
|
181
205
|
});
|
|
182
206
|
|
|
183
|
-
const callerSkill = input.caller_skill ?? process.env.PHASEGATE_CALLER_SKILL;
|
|
184
207
|
const output = await useCase.execute({
|
|
185
208
|
toolName: effectiveToolName,
|
|
186
209
|
targetFilePaths: projectTargetFilePaths,
|
|
187
|
-
callerSkill,
|
|
188
210
|
targetChanges: projectTargetChanges,
|
|
189
211
|
});
|
|
190
212
|
|
|
@@ -222,7 +244,7 @@ async function buildTargetChanges(
|
|
|
222
244
|
toRelative: (p: string) => string,
|
|
223
245
|
): Promise<TargetChange[]> {
|
|
224
246
|
const toolInput = input.tool_input;
|
|
225
|
-
if (toolInput
|
|
247
|
+
if (toolInput == null || typeof toolInput !== 'object') {
|
|
226
248
|
return [];
|
|
227
249
|
}
|
|
228
250
|
|
|
@@ -258,6 +280,32 @@ async function readExistingContent(cwd: string, filePath: string): Promise<strin
|
|
|
258
280
|
}
|
|
259
281
|
}
|
|
260
282
|
|
|
283
|
+
async function buildBashTargetChange(
|
|
284
|
+
cwd: string,
|
|
285
|
+
rawPath: string,
|
|
286
|
+
toRelative: (p: string) => string,
|
|
287
|
+
): Promise<TargetChange> {
|
|
288
|
+
const filePath = toRelative(rawPath);
|
|
289
|
+
if (rawPath.includes('$') || rawPath.includes('`') || rawPath.startsWith('~')) {
|
|
290
|
+
// hook ではシェル展開後の実パスを解決できないため、存在チェックを避けて MODIFY 既定にする。
|
|
291
|
+
return { filePath };
|
|
292
|
+
}
|
|
293
|
+
const absolutePath = path.isAbsolute(rawPath) ? rawPath : path.join(cwd, rawPath);
|
|
294
|
+
try {
|
|
295
|
+
await fs.stat(absolutePath);
|
|
296
|
+
return { filePath };
|
|
297
|
+
} catch (error) {
|
|
298
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
299
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
300
|
+
// Bash では afterContent を取得できないため、空文字を CREATE 判定用の
|
|
301
|
+
// 「変更後内容あり」sentinel として渡す。実ファイルへの書き込みは行わない。
|
|
302
|
+
return { filePath, beforeContent: null, afterContent: '' };
|
|
303
|
+
}
|
|
304
|
+
// 権限エラー等で存在を確認できない場合は従来どおり MODIFY 既定(安全側)。
|
|
305
|
+
return { filePath };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
261
309
|
main().catch((error) => {
|
|
262
310
|
process.stderr.write(`予期しないエラー: ${String(error)}\n`);
|
|
263
311
|
process.exit(2);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @unit ci-governance
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-368
|
|
4
|
+
|
|
5
|
+
export interface ScaffoldInceptionOutput {
|
|
6
|
+
readonly targetPath: string;
|
|
7
|
+
readonly templatePath: string;
|
|
8
|
+
readonly kind: string;
|
|
9
|
+
readonly dryRun: boolean;
|
|
10
|
+
readonly written: boolean;
|
|
11
|
+
readonly alreadyExists: boolean;
|
|
12
|
+
readonly overwritten: boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// @unit ci-governance
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-367
|
|
4
|
+
|
|
5
|
+
import type { TemplateCatalogPort } from "../../domain/ports/template-catalog-port.js";
|
|
6
|
+
|
|
7
|
+
export interface ListTemplatesOutputEntry {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly fileName: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ListTemplatesOutput {
|
|
13
|
+
readonly directoryPath: string;
|
|
14
|
+
readonly templates: readonly ListTemplatesOutputEntry[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ListTemplatesUseCase {
|
|
18
|
+
constructor(private readonly catalog: TemplateCatalogPort) {}
|
|
19
|
+
|
|
20
|
+
async execute(): Promise<ListTemplatesOutput> {
|
|
21
|
+
const entries = await this.catalog.list();
|
|
22
|
+
return {
|
|
23
|
+
directoryPath: this.catalog.directoryPath(),
|
|
24
|
+
templates: entries.map((entry) => ({
|
|
25
|
+
name: entry.name.value,
|
|
26
|
+
fileName: entry.fileName,
|
|
27
|
+
})),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @unit ci-governance
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-368
|
|
4
|
+
|
|
5
|
+
import type { InceptionDocWriterPort } from "../../domain/ports/inception-doc-writer-port.js";
|
|
6
|
+
import type { InceptionTemplateRepositoryPort } from "../../domain/ports/inception-template-repository-port.js";
|
|
7
|
+
import { InceptionDocKind } from "../../domain/value-objects/inception-doc-kind.js";
|
|
8
|
+
import type { ScaffoldInceptionInput } from "../dto/scaffold-inception-input.js";
|
|
9
|
+
import type { ScaffoldInceptionOutput } from "../dto/scaffold-inception-output.js";
|
|
10
|
+
|
|
11
|
+
export class ScaffoldInceptionUseCase {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly templates: InceptionTemplateRepositoryPort,
|
|
14
|
+
private readonly writer: InceptionDocWriterPort,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
async execute(input: ScaffoldInceptionInput): Promise<ScaffoldInceptionOutput> {
|
|
18
|
+
const kind = InceptionDocKind.create(input.kind);
|
|
19
|
+
const dryRun = input.dryRun === true;
|
|
20
|
+
const force = input.force === true;
|
|
21
|
+
|
|
22
|
+
const templatePath = this.templates.resolvePath(kind);
|
|
23
|
+
const targetPath = this.writer.resolvePath(kind);
|
|
24
|
+
const alreadyExists = await this.writer.exists(kind);
|
|
25
|
+
|
|
26
|
+
if (dryRun) {
|
|
27
|
+
return {
|
|
28
|
+
targetPath,
|
|
29
|
+
templatePath,
|
|
30
|
+
kind: kind.value,
|
|
31
|
+
dryRun: true,
|
|
32
|
+
written: false,
|
|
33
|
+
alreadyExists,
|
|
34
|
+
overwritten: false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (alreadyExists && !force) {
|
|
39
|
+
return {
|
|
40
|
+
targetPath,
|
|
41
|
+
templatePath,
|
|
42
|
+
kind: kind.value,
|
|
43
|
+
dryRun: false,
|
|
44
|
+
written: false,
|
|
45
|
+
alreadyExists: true,
|
|
46
|
+
overwritten: false,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const raw = await this.templates.read(kind);
|
|
51
|
+
await this.writer.write(kind, raw);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
targetPath,
|
|
55
|
+
templatePath,
|
|
56
|
+
kind: kind.value,
|
|
57
|
+
dryRun: false,
|
|
58
|
+
written: true,
|
|
59
|
+
alreadyExists,
|
|
60
|
+
overwritten: alreadyExists && force,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @unit ci-governance
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-367
|
|
4
|
+
|
|
5
|
+
import type { TemplateCatalogPort } from "../../domain/ports/template-catalog-port.js";
|
|
6
|
+
import { TemplateName } from "../../domain/value-objects/template-name.js";
|
|
7
|
+
|
|
8
|
+
export type ShowTemplateOutput =
|
|
9
|
+
| { readonly found: true; readonly name: string; readonly content: string }
|
|
10
|
+
| {
|
|
11
|
+
readonly found: false;
|
|
12
|
+
readonly reason: "invalid-name" | "not-found";
|
|
13
|
+
readonly availableNames: readonly string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class ShowTemplateUseCase {
|
|
17
|
+
constructor(private readonly catalog: TemplateCatalogPort) {}
|
|
18
|
+
|
|
19
|
+
async execute(rawName: string): Promise<ShowTemplateOutput> {
|
|
20
|
+
if (!TemplateName.isValid(rawName)) {
|
|
21
|
+
// 不正名では catalog を引かない。パス由来の情報を一切露出させない。
|
|
22
|
+
return { found: false, reason: "invalid-name", availableNames: [] };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const name = TemplateName.create(rawName);
|
|
26
|
+
const content = await this.catalog.read(name);
|
|
27
|
+
|
|
28
|
+
if (content === null) {
|
|
29
|
+
const entries = await this.catalog.list();
|
|
30
|
+
return {
|
|
31
|
+
found: false,
|
|
32
|
+
reason: "not-found",
|
|
33
|
+
availableNames: entries.map((entry) => entry.name.value),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { found: true, name: name.value, content };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @layer infrastructure
|
|
3
3
|
* @unit ci-governance
|
|
4
|
+
* @work-item-id WI-367, WI-368, WI-369
|
|
4
5
|
*
|
|
5
6
|
* Composition Root - 依存性の組み立て
|
|
6
7
|
*/
|
|
@@ -10,6 +11,7 @@ import { CheckAgentContextUseCase } from "./application/usecases/check-agent-con
|
|
|
10
11
|
import { CheckEscalationUseCase } from "./application/usecases/check-escalation-usecase.js";
|
|
11
12
|
import { CreateBaselineUseCase } from "./application/usecases/create-baseline-usecase.js";
|
|
12
13
|
import { GenerateCiTemplateUseCase } from "./application/usecases/generate-ci-template-usecase.js";
|
|
14
|
+
import { ListTemplatesUseCase } from "./application/usecases/list-templates-usecase.js";
|
|
13
15
|
import { MigrateAgentsMdUseCase } from "./application/usecases/migrate-agents-md-usecase.js";
|
|
14
16
|
import { PinIntegrityUseCase } from "./application/usecases/pin-integrity-usecase.js";
|
|
15
17
|
import { RecordErrorOccurrenceUseCase } from "./application/usecases/record-error-occurrence-usecase.js";
|
|
@@ -18,6 +20,8 @@ import { RefreshClaudeMdUseCase } from "./application/usecases/refresh-claude-md
|
|
|
18
20
|
import { RenderCiTemplateUseCase } from "./application/usecases/render-ci-template-usecase.js";
|
|
19
21
|
import { ResetRepetitionUseCase } from "./application/usecases/reset-repetition-usecase.js";
|
|
20
22
|
import { ScaffoldDesignUseCase } from "./application/usecases/scaffold-design-usecase.js";
|
|
23
|
+
import { ScaffoldInceptionUseCase } from "./application/usecases/scaffold-inception-usecase.js";
|
|
24
|
+
import { ShowTemplateUseCase } from "./application/usecases/show-template-usecase.js";
|
|
21
25
|
import { ValidatePointersUseCase } from "./application/usecases/validate-pointers-usecase.js";
|
|
22
26
|
import { VerifyIntegrityUseCase } from "./application/usecases/verify-integrity-usecase.js";
|
|
23
27
|
import { ClaudeMdComposer } from "./domain/services/claude-md-composer.js";
|
|
@@ -34,8 +38,11 @@ import { ErrorRepetitionJsonRepository } from "./infrastructure/adapters/error-r
|
|
|
34
38
|
import { EscalationLogExecutorAdapter } from "./infrastructure/adapters/escalation-log-executor-adapter.js";
|
|
35
39
|
import { FileSystemDesignDocWriterAdapter } from "./infrastructure/adapters/file-system-design-doc-writer-adapter.js";
|
|
36
40
|
import { FileSystemExistenceAdapter } from "./infrastructure/adapters/file-system-existence-adapter.js";
|
|
41
|
+
import { FileSystemInceptionDocWriterAdapter } from "./infrastructure/adapters/file-system-inception-doc-writer-adapter.js";
|
|
42
|
+
import { FileSystemInceptionTemplateRepositoryAdapter } from "./infrastructure/adapters/file-system-inception-template-repository-adapter.js";
|
|
37
43
|
import { FileSystemSha1HasherAdapter } from "./infrastructure/adapters/file-system-sha1-hasher-adapter.js";
|
|
38
44
|
import { FileSystemSha256HasherAdapter } from "./infrastructure/adapters/file-system-sha256-hasher-adapter.js";
|
|
45
|
+
import { FileSystemTemplateCatalogAdapter } from "./infrastructure/adapters/file-system-template-catalog-adapter.js";
|
|
39
46
|
import { FileSystemTemplateRepositoryAdapter } from "./infrastructure/adapters/file-system-template-repository-adapter.js";
|
|
40
47
|
import { GlobFileScannerAdapter } from "./infrastructure/adapters/glob-file-scanner-adapter.js";
|
|
41
48
|
import { HarnessApiCommandExistenceAdapter } from "./infrastructure/adapters/harness-api-command-existence-adapter.js";
|
|
@@ -53,6 +60,8 @@ import { MigrateAgentsMdHandler } from "./presentation/handlers/migrate-agents-m
|
|
|
53
60
|
import { RefreshAgentContextHandler } from "./presentation/handlers/refresh-agent-context-handler.js";
|
|
54
61
|
import { RefreshClaudeMdHandler } from "./presentation/handlers/refresh-claude-md-handler.js";
|
|
55
62
|
import { ScaffoldDesignHandler } from "./presentation/handlers/scaffold-design-handler.js";
|
|
63
|
+
import { ScaffoldInceptionHandler } from "./presentation/handlers/scaffold-inception-handler.js";
|
|
64
|
+
import { TemplatesHandler } from "./presentation/handlers/templates-handler.js";
|
|
56
65
|
|
|
57
66
|
export interface CiGovernanceCompositionRoot {
|
|
58
67
|
generateCiTemplateHandler: GenerateCiTemplateHandler;
|
|
@@ -63,6 +72,8 @@ export interface CiGovernanceCompositionRoot {
|
|
|
63
72
|
checkRepetitionHandler: CheckRepetitionHandler;
|
|
64
73
|
createBaselineHandler: CreateBaselineHandler;
|
|
65
74
|
scaffoldDesignHandler: ScaffoldDesignHandler;
|
|
75
|
+
scaffoldInceptionHandler: ScaffoldInceptionHandler;
|
|
76
|
+
templatesHandler: TemplatesHandler;
|
|
66
77
|
// Use cases exposed for direct access
|
|
67
78
|
recordErrorOccurrenceUseCase: RecordErrorOccurrenceUseCase;
|
|
68
79
|
checkEscalationUseCase: CheckEscalationUseCase;
|
|
@@ -71,10 +82,33 @@ export interface CiGovernanceCompositionRoot {
|
|
|
71
82
|
validatePointersUseCase: ValidatePointersUseCase;
|
|
72
83
|
createBaselineUseCase: CreateBaselineUseCase;
|
|
73
84
|
scaffoldDesignUseCase: ScaffoldDesignUseCase;
|
|
85
|
+
scaffoldInceptionUseCase: ScaffoldInceptionUseCase;
|
|
74
86
|
integrityHandler: IntegrityHandler;
|
|
75
87
|
}
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
/**
|
|
90
|
+
* 解決済みの設計文書ルート(`phasegate.config.json` の `paths`)。
|
|
91
|
+
* 省略時は従来どおり `docs/product/construction` / `docs/inception` を使う。
|
|
92
|
+
*
|
|
93
|
+
* WI-369: これを渡さないと `scaffold-design` の書き込み先が
|
|
94
|
+
* `docs/product/construction` に固定され、`paths.designDocs` を移設した PJ で
|
|
95
|
+
* scaffold 先とフェーズゲートの検査先がズレる。
|
|
96
|
+
*/
|
|
97
|
+
export interface CiGovernanceDocPaths {
|
|
98
|
+
readonly designDocs?: string;
|
|
99
|
+
readonly inceptionDocs?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const DEFAULT_DESIGN_DOCS_ROOT = "docs/product/construction";
|
|
103
|
+
const DEFAULT_INCEPTION_DOCS_ROOT = "docs/inception";
|
|
104
|
+
|
|
105
|
+
export function buildCiGovernance(
|
|
106
|
+
baseDir: string,
|
|
107
|
+
harnessRoot: string = baseDir,
|
|
108
|
+
docPaths: CiGovernanceDocPaths = {},
|
|
109
|
+
): CiGovernanceCompositionRoot {
|
|
110
|
+
const designDocsRoot = docPaths.designDocs ?? DEFAULT_DESIGN_DOCS_ROOT;
|
|
111
|
+
const inceptionDocsRoot = docPaths.inceptionDocs ?? DEFAULT_INCEPTION_DOCS_ROOT;
|
|
78
112
|
// Infrastructure adapters
|
|
79
113
|
const validatorIdRegistryAdapter = new ValidatorIdRegistryAdapter();
|
|
80
114
|
const presetConfigAdapter = new PresetConfigAdapter();
|
|
@@ -119,20 +153,35 @@ export function buildCiGovernance(baseDir: string, harnessRoot: string = baseDir
|
|
|
119
153
|
const baselineRepository = new BaselineJsonRepositoryAdapter(baseDir);
|
|
120
154
|
const createBaselineUseCase = new CreateBaselineUseCase(fileScanner, fileHasher, baselineRepository);
|
|
121
155
|
|
|
122
|
-
// Scaffold design adapters & use case (ISSUE-007 Wave 4)
|
|
156
|
+
// Scaffold design adapters & use case (ISSUE-007 Wave 4 / WI-369 paths 追従)
|
|
123
157
|
const templateRepository = new FileSystemTemplateRepositoryAdapter(harnessRoot);
|
|
124
|
-
const designDocWriter = new FileSystemDesignDocWriterAdapter(baseDir);
|
|
158
|
+
const designDocWriter = new FileSystemDesignDocWriterAdapter(baseDir, designDocsRoot);
|
|
125
159
|
const scaffoldDesignUseCase = new ScaffoldDesignUseCase(templateRepository, designDocWriter);
|
|
126
160
|
|
|
161
|
+
// Scaffold inception adapters & use case (WI-368)
|
|
162
|
+
const inceptionTemplateRepository = new FileSystemInceptionTemplateRepositoryAdapter(harnessRoot);
|
|
163
|
+
const inceptionDocWriter = new FileSystemInceptionDocWriterAdapter(baseDir, {
|
|
164
|
+
designDocsRoot,
|
|
165
|
+
inceptionDocsRoot,
|
|
166
|
+
});
|
|
167
|
+
const scaffoldInceptionUseCase = new ScaffoldInceptionUseCase(inceptionTemplateRepository, inceptionDocWriter);
|
|
168
|
+
|
|
169
|
+
// Template catalog adapters & use cases (WI-367)
|
|
170
|
+
const templateCatalog = new FileSystemTemplateCatalogAdapter(harnessRoot);
|
|
171
|
+
const listTemplatesUseCase = new ListTemplatesUseCase(templateCatalog);
|
|
172
|
+
const showTemplateUseCase = new ShowTemplateUseCase(templateCatalog);
|
|
173
|
+
|
|
127
174
|
// Handlers
|
|
128
175
|
const generateCiTemplateHandler = new GenerateCiTemplateHandler(generateCiTemplateUseCase, renderCiTemplateUseCase);
|
|
129
|
-
const migrateAgentsMdHandler = new MigrateAgentsMdHandler(migrateAgentsMdUseCase);
|
|
176
|
+
const migrateAgentsMdHandler = new MigrateAgentsMdHandler(migrateAgentsMdUseCase, validatePointersUseCase);
|
|
130
177
|
const refreshAgentContextHandler = new RefreshAgentContextHandler(refreshAgentContextUseCase);
|
|
131
178
|
const refreshClaudeMdHandler = new RefreshClaudeMdHandler(refreshClaudeMdUseCase);
|
|
132
179
|
const checkAgentContextHandler = new CheckAgentContextHandler(checkAgentContextUseCase);
|
|
133
|
-
const checkRepetitionHandler = new CheckRepetitionHandler(checkEscalationUseCase);
|
|
180
|
+
const checkRepetitionHandler = new CheckRepetitionHandler(checkEscalationUseCase, resetRepetitionUseCase);
|
|
134
181
|
const createBaselineHandler = new CreateBaselineHandler(createBaselineUseCase);
|
|
135
182
|
const scaffoldDesignHandler = new ScaffoldDesignHandler(scaffoldDesignUseCase);
|
|
183
|
+
const scaffoldInceptionHandler = new ScaffoldInceptionHandler(scaffoldInceptionUseCase);
|
|
184
|
+
const templatesHandler = new TemplatesHandler(listTemplatesUseCase, showTemplateUseCase);
|
|
136
185
|
|
|
137
186
|
// Integrity pin adapters & handler (WI-254 / ADR-030 §Decision.3.①)
|
|
138
187
|
const integrityChecker = new IntegrityChecker();
|
|
@@ -156,6 +205,8 @@ export function buildCiGovernance(baseDir: string, harnessRoot: string = baseDir
|
|
|
156
205
|
checkRepetitionHandler,
|
|
157
206
|
createBaselineHandler,
|
|
158
207
|
scaffoldDesignHandler,
|
|
208
|
+
scaffoldInceptionHandler,
|
|
209
|
+
templatesHandler,
|
|
159
210
|
recordErrorOccurrenceUseCase,
|
|
160
211
|
checkEscalationUseCase,
|
|
161
212
|
resetRepetitionUseCase,
|
|
@@ -163,6 +214,7 @@ export function buildCiGovernance(baseDir: string, harnessRoot: string = baseDir
|
|
|
163
214
|
validatePointersUseCase,
|
|
164
215
|
createBaselineUseCase,
|
|
165
216
|
scaffoldDesignUseCase,
|
|
217
|
+
scaffoldInceptionUseCase,
|
|
166
218
|
integrityHandler,
|
|
167
219
|
};
|
|
168
220
|
}
|