phasegate 0.191.0 → 0.212.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.ja.md +52 -15
- package/README.md +39 -11
- package/docs/ADR/030-injection-threat-model-and-trust-root.md +145 -0
- package/docs/guide/hooks-integration.md +50 -1
- package/docs/guide/installation.md +1 -1
- package/docs/guide/quick-vs-full-mode.md +1 -1
- package/docs/guide/skills-overview.md +7 -8
- package/package.json +1 -1
- package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +131 -63
- package/scripts/harness/agent-integration/presentation/session-start-hook.ts +31 -4
- package/scripts/harness/agent-integration/presentation/spotlight.ts +65 -0
- package/scripts/harness/ci-governance/application/dto/pin-integrity-input.ts +8 -0
- package/scripts/harness/ci-governance/application/dto/pin-integrity-output.ts +9 -0
- package/scripts/harness/ci-governance/application/dto/verify-integrity-input.ts +7 -0
- package/scripts/harness/ci-governance/application/dto/verify-integrity-output.ts +10 -0
- package/scripts/harness/ci-governance/application/usecases/pin-integrity-usecase.ts +60 -0
- package/scripts/harness/ci-governance/application/usecases/verify-integrity-usecase.ts +46 -0
- package/scripts/harness/ci-governance/composition-root.ts +67 -64
- package/scripts/harness/ci-governance/domain/ports/integrity-manifest-repository-port.ts +14 -0
- package/scripts/harness/ci-governance/domain/ports/sha256-hasher-port.ts +10 -0
- package/scripts/harness/ci-governance/domain/services/integrity-checker.ts +42 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-drift.ts +16 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-manifest.ts +49 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +40 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-sha256-hasher-adapter.ts +17 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +7 -73
- package/scripts/harness/ci-governance/infrastructure/adapters/integrity-manifest-json-repository-adapter.ts +83 -0
- package/scripts/harness/ci-governance/presentation/handlers/integrity-handler.ts +76 -0
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +53 -28
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +33 -8
- package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +90 -0
- package/scripts/harness/installation/application/bundled-skill-selection.ts +2 -5
- package/scripts/harness/main.ts +257 -105
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +65 -3
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +44 -40
- package/scripts/harness/setup/skill-deployer.ts +2 -4
- package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +82 -49
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +87 -58
- package/scripts/harness/validator-system/composition-root.ts +141 -99
- package/scripts/harness/validator-system/domain/ports/coverage-attestation-gating-policy-port.ts +14 -0
- package/scripts/harness/validator-system/domain/ports/injection-scan-policy-port.ts +14 -0
- package/scripts/harness/validator-system/domain/services/coverage-attestation-gating-service.ts +56 -0
- package/scripts/harness/validator-system/domain/services/injection-pattern-scan-service.ts +118 -0
- package/scripts/harness/validator-system/domain/value-objects/coverage-gating-report.ts +67 -0
- package/scripts/harness/validator-system/domain/value-objects/injection-scan-report.ts +55 -0
- package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +35 -31
- package/scripts/harness/validator-system/infrastructure/adapters/adr-foundation-reference-adapter.ts +13 -7
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-gating-adapter.ts +87 -0
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-injection-scan-adapter.ts +82 -0
- package/skills/README.md +1 -1
- package/skills/codebase-mapper/SKILL.md +1 -1
- package/skills/consistency-checker/references//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +1 -1
- package/skills/doc-health-checker/SKILL.md +148 -0
- package/skills/release-publisher/SKILL.md +101 -0
- package/skills/skill-creator/SKILL.md +74 -332
- package/skills/story-implementor/SKILL.md +52 -0
- package/skills/story-mapper/SKILL.md +4 -0
- package/skills/story-writer/SKILL.md +9 -0
- package/skills/uiux-designer/references/uiux-design-template.md +4 -4
- package/skills/unit-designer/SKILL.md +3 -1
- package/skills/doc-freshness-checker/SKILL.md +0 -140
- package/skills/implementation-planner/SKILL.md +0 -169
- package/skills/implementation-planner/references/document-structure.md +0 -116
- package/skills/implementation-planner/references/plan-template.md +0 -177
- package/skills/implementation-planner/references/workflow.md +0 -164
- package/skills/pointer-validator/SKILL.md +0 -105
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @unit validator-system
|
|
2
|
+
// @layer domain
|
|
3
|
+
// @work-item-id WI-259
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* WI-259 / ADR-030 §Decision.3.④ — advisory インジェクションスキャナ(L3-006)用ドメインモデル。
|
|
7
|
+
*
|
|
8
|
+
* 指示搭載ファイルに対する既知インジェクションパターンの検出結果を表す value-object 群。
|
|
9
|
+
* 全 finding は severity='warning'(advisory)。error / violation は構造上生成しない(§4.(b))。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** 検出種別。 */
|
|
13
|
+
export type InjectionFindingKind =
|
|
14
|
+
| "instruction-override"
|
|
15
|
+
| "invisible-unicode"
|
|
16
|
+
| "base64-blob"
|
|
17
|
+
| "html-comment-instruction";
|
|
18
|
+
|
|
19
|
+
/** 1 走査対象ファイル(infra が解決)。 */
|
|
20
|
+
export interface InjectionScanTarget {
|
|
21
|
+
/** project-relative パス(報告用)。 */
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly content: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 1 件の検出。severity は常に 'warning'。 */
|
|
27
|
+
export interface InjectionFinding {
|
|
28
|
+
readonly kind: InjectionFindingKind;
|
|
29
|
+
readonly severity: "warning";
|
|
30
|
+
readonly sourcePath: string;
|
|
31
|
+
/** 1 起点の行番号。 */
|
|
32
|
+
readonly lineNumber: number;
|
|
33
|
+
readonly message: string;
|
|
34
|
+
readonly suggestion: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* スキャン判定レポート。findings はすべて warning(advisory)。
|
|
39
|
+
*/
|
|
40
|
+
export class InjectionScanReport {
|
|
41
|
+
readonly findings: readonly InjectionFinding[];
|
|
42
|
+
|
|
43
|
+
private constructor(findings: readonly InjectionFinding[]) {
|
|
44
|
+
this.findings = Object.freeze([...findings]);
|
|
45
|
+
Object.freeze(this);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static create(findings: readonly InjectionFinding[]): InjectionScanReport {
|
|
49
|
+
return new InjectionScanReport(findings);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
hasFindings(): boolean {
|
|
53
|
+
return this.findings.length > 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
* WI-156 で L4-006 を追加
|
|
12
12
|
* WI-222 (HF2-05) で L4-007(ac-level-traceability, default-OFF advisory)を追加
|
|
13
13
|
* WI-227 (H16-03) で L3-005(ac-bound-coverage, default-OFF fail-closed)を追加
|
|
14
|
+
* WI-258 (ADR-030 §Decision.3.②) で L2-016(coverage-attestation-gating, fail-closed)を追加
|
|
15
|
+
* WI-259 (ADR-030 §Decision.3.④) で L3-006(injection-scan, advisory warning-only)を追加
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
export class InvalidValidatorIdError extends Error {
|
|
17
19
|
readonly invalidValue: string;
|
|
18
20
|
constructor(raw: string) {
|
|
19
21
|
super(`Invalid validator ID: "${raw}". Must be one of the registered valid IDs.`);
|
|
20
|
-
this.name =
|
|
22
|
+
this.name = "InvalidValidatorIdError";
|
|
21
23
|
this.invalidValue = raw;
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -26,38 +28,40 @@ const VALIDATOR_ID_PATTERN = /^L[0-4]-\d{3}$/;
|
|
|
26
28
|
|
|
27
29
|
/** バリデータID -> バリデータ名のマップ */
|
|
28
30
|
const VALIDATOR_NAME_MAP: Record<string, string> = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
"L1-017": "it-test-mock-detection",
|
|
32
|
+
"L1-018": "stub-comment-detection",
|
|
33
|
+
"L2-001": "phase-gate",
|
|
34
|
+
"L2-002": "metadata",
|
|
35
|
+
"L2-003": "test-quality",
|
|
36
|
+
"L2-013": "cli-e2e-test-existence",
|
|
37
|
+
"L2-014": "work-item-status-staleness",
|
|
38
|
+
"L2-015": "contract-traceability-coverage",
|
|
39
|
+
"L2-016": "coverage-attestation-gating",
|
|
40
|
+
"L3-001": "security",
|
|
41
|
+
"L3-002": "performance",
|
|
42
|
+
"L3-003": "coverage",
|
|
43
|
+
"L3-004": "nyquist",
|
|
44
|
+
"L3-005": "ac-bound-coverage",
|
|
45
|
+
"L3-006": "injection-scan",
|
|
46
|
+
"L4-001": "drift-detect",
|
|
47
|
+
"L4-002": "consistency-check",
|
|
48
|
+
"L4-003": "dead-code",
|
|
49
|
+
"L4-004": "doc-freshness",
|
|
50
|
+
"L4-005": "pointer-validation",
|
|
51
|
+
"L4-006": "skill-catalog-drift",
|
|
52
|
+
"L4-007": "ac-level-traceability",
|
|
49
53
|
};
|
|
50
54
|
|
|
51
55
|
/** バリデータ名 -> バリデータID の逆引きマップ */
|
|
52
56
|
const NAME_TO_ID_MAP: Record<string, string> = {
|
|
53
57
|
...Object.fromEntries(Object.entries(VALIDATOR_NAME_MAP).map(([id, name]) => [name, id])),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
"drift-detector": "L4-001",
|
|
59
|
+
"consistency-checker": "L4-002",
|
|
60
|
+
"dead-code-detector": "L4-003",
|
|
61
|
+
"doc-freshness-checker": "L4-004",
|
|
62
|
+
"pointer-validator": "L4-005",
|
|
63
|
+
"skill-catalog-drift": "L4-006",
|
|
64
|
+
"ac-level-traceability": "L4-007",
|
|
61
65
|
};
|
|
62
66
|
|
|
63
67
|
/** 有効なValidatorID集合 */
|
|
@@ -65,12 +69,12 @@ const VALID_IDS = new Set(Object.keys(VALIDATOR_NAME_MAP));
|
|
|
65
69
|
|
|
66
70
|
export class ValidatorId {
|
|
67
71
|
readonly value: string;
|
|
68
|
-
readonly layer:
|
|
72
|
+
readonly layer: "L0" | "L1" | "L2" | "L3" | "L4";
|
|
69
73
|
readonly sequence: string;
|
|
70
74
|
|
|
71
75
|
private constructor(value: string) {
|
|
72
76
|
this.value = value;
|
|
73
|
-
this.layer = value.substring(0, 2) as
|
|
77
|
+
this.layer = value.substring(0, 2) as "L0" | "L1" | "L2" | "L3" | "L4";
|
|
74
78
|
this.sequence = value.substring(3);
|
|
75
79
|
Object.freeze(this);
|
|
76
80
|
}
|
|
@@ -93,7 +97,7 @@ export class ValidatorId {
|
|
|
93
97
|
return new ValidatorId(id);
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
getLayer():
|
|
100
|
+
getLayer(): "L0" | "L1" | "L2" | "L3" | "L4" {
|
|
97
101
|
return this.layer;
|
|
98
102
|
}
|
|
99
103
|
|
package/scripts/harness/validator-system/infrastructure/adapters/adr-foundation-reference-adapter.ts
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* AdrFoundationReferenceAdapter — AdrReferencePort実装
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import type { AdrMetadata, AdrReferencePort } from "../../domain/ports/adr-reference-port.js";
|
|
8
9
|
|
|
9
10
|
export class AdrFoundationReferenceAdapter implements AdrReferencePort {
|
|
11
|
+
constructor(private readonly rootDir: string = process.cwd()) {}
|
|
12
|
+
|
|
10
13
|
async exists(adrRef: string): Promise<boolean> {
|
|
11
14
|
try {
|
|
12
15
|
const adr = await this.findAdr(adrRef);
|
|
@@ -35,19 +38,22 @@ export class AdrFoundationReferenceAdapter implements AdrReferencePort {
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
private async findAdr(adrRef: string): Promise<{
|
|
38
|
-
readonly getFrontmatter: () => {
|
|
41
|
+
readonly getFrontmatter: () => {
|
|
42
|
+
readonly title: string;
|
|
43
|
+
readonly status: { readonly value: AdrMetadata["status"] };
|
|
44
|
+
};
|
|
39
45
|
readonly toAdrRef: () => string;
|
|
40
46
|
} | null> {
|
|
41
|
-
const { createAdrFoundationModule } = await import(
|
|
42
|
-
const { AdrId } = await import(
|
|
43
|
-
const mod = createAdrFoundationModule(
|
|
47
|
+
const { createAdrFoundationModule } = await import("../../../adr-foundation/composition-root.js");
|
|
48
|
+
const { AdrId } = await import("../../../adr-foundation/domain/value-objects/adr-id.js");
|
|
49
|
+
const mod = createAdrFoundationModule(path.join(this.rootDir, "docs", "ADR"));
|
|
44
50
|
const repository = mod.adrRepository;
|
|
45
51
|
|
|
46
|
-
if (
|
|
52
|
+
if ("findById" in repository && typeof repository.findById === "function") {
|
|
47
53
|
return repository.findById(AdrId.create(adrRef));
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
if (
|
|
56
|
+
if ("findAll" in repository && typeof repository.findAll === "function") {
|
|
51
57
|
const adrs = await repository.findAll();
|
|
52
58
|
return adrs.find((adr) => adr.toAdrRef() === adrRef) ?? null;
|
|
53
59
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// @unit validator-system
|
|
2
|
+
// @layer infrastructure
|
|
3
|
+
// @work-item-id WI-258
|
|
4
|
+
|
|
5
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import type { CoverageAttestationGatingPolicyPort } from "../../domain/ports/coverage-attestation-gating-policy-port.js";
|
|
8
|
+
import type { CoverageClaim, CoverageReportGatingModel } from "../../domain/value-objects/coverage-gating-report.js";
|
|
9
|
+
|
|
10
|
+
const COVERAGE_REPORT_FILE = "coverage_report.md";
|
|
11
|
+
const CONSTRUCTION_REL = join("docs", "product", "construction");
|
|
12
|
+
const LEGACY_MARKER = /<!--\s*@coverage-gating:\s*ungated-legacy\s*-->/;
|
|
13
|
+
const ATTESTATION_REF = /<!--\s*@attestation\b/;
|
|
14
|
+
const CHECKMARK = "✅"; // ✅
|
|
15
|
+
const COMMENT_LINE = /^\s*<!--/;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* WI-258 / ADR-030 §Decision.3.② — coverage-attestation-gating (L2-016) の走査アダプタ。
|
|
19
|
+
*
|
|
20
|
+
* `docs/product/construction/*/coverage_report.md` を cwd 起点で走査し(targetPaths 非依存の
|
|
21
|
+
* corpus 走査。L2-014 と同様に自前でファイル探索する)、各ファイルを CoverageReportGatingModel 化する。
|
|
22
|
+
*
|
|
23
|
+
* - legacy マーカー: `<!-- @coverage-gating: ungated-legacy -->` の有無。
|
|
24
|
+
* - ✅ claim: `✅` を含む行。同一行に `<!-- @attestation ... -->` があるか、
|
|
25
|
+
* 直前の連続コメント行に `@attestation` があれば hasAttestationRef=true。
|
|
26
|
+
*/
|
|
27
|
+
export class FileSystemCoverageAttestationGatingAdapter implements CoverageAttestationGatingPolicyPort {
|
|
28
|
+
constructor(private readonly projectRoot: string) {}
|
|
29
|
+
|
|
30
|
+
async collect(): Promise<readonly CoverageReportGatingModel[]> {
|
|
31
|
+
const constructionRoot = join(this.projectRoot, CONSTRUCTION_REL);
|
|
32
|
+
let unitDirs: string[];
|
|
33
|
+
try {
|
|
34
|
+
const entries = await readdir(constructionRoot, { withFileTypes: true });
|
|
35
|
+
unitDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
36
|
+
} catch {
|
|
37
|
+
return Object.freeze([]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const models: CoverageReportGatingModel[] = [];
|
|
41
|
+
for (const unitDir of unitDirs) {
|
|
42
|
+
const filePath = join(constructionRoot, unitDir, COVERAGE_REPORT_FILE);
|
|
43
|
+
let content: string;
|
|
44
|
+
try {
|
|
45
|
+
content = await readFile(filePath, "utf-8");
|
|
46
|
+
} catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// path は project-relative で報告する(メッセージの可搬性のため)。
|
|
50
|
+
const relPath = `${CONSTRUCTION_REL.replace(/\\/g, "/")}/${unitDir}/${COVERAGE_REPORT_FILE}`;
|
|
51
|
+
models.push(this.parse(relPath, content));
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze(models);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private parse(path: string, content: string): CoverageReportGatingModel {
|
|
57
|
+
const lines = content.split(/\r?\n/);
|
|
58
|
+
const hasLegacyMarker = lines.some((line) => LEGACY_MARKER.test(line));
|
|
59
|
+
|
|
60
|
+
const claims: CoverageClaim[] = [];
|
|
61
|
+
for (let i = 0; i < lines.length; i++) {
|
|
62
|
+
const line = lines[i];
|
|
63
|
+
if (!line.includes(CHECKMARK)) continue;
|
|
64
|
+
claims.push({
|
|
65
|
+
lineNumber: i + 1,
|
|
66
|
+
hasAttestationRef: this.hasAttestationRef(lines, i),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { path, hasLegacyMarker, claims };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* ✅ 行 index について attestation 参照があるか判定する。
|
|
75
|
+
* (1) 同一行にコメント参照、または
|
|
76
|
+
* (2) 直前の連続するコメント行のいずれかに `@attestation` 参照がある。
|
|
77
|
+
*/
|
|
78
|
+
private hasAttestationRef(lines: readonly string[], index: number): boolean {
|
|
79
|
+
if (ATTESTATION_REF.test(lines[index])) return true;
|
|
80
|
+
for (let j = index - 1; j >= 0; j--) {
|
|
81
|
+
const prev = lines[j];
|
|
82
|
+
if (!COMMENT_LINE.test(prev)) break;
|
|
83
|
+
if (ATTESTATION_REF.test(prev)) return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// @unit validator-system
|
|
2
|
+
// @layer infrastructure
|
|
3
|
+
// @work-item-id WI-259
|
|
4
|
+
|
|
5
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import type { InjectionScanPolicyPort } from "../../domain/ports/injection-scan-policy-port.js";
|
|
8
|
+
import type { InjectionScanTarget } from "../../domain/value-objects/injection-scan-report.js";
|
|
9
|
+
|
|
10
|
+
const SKILLS_DIR = "skills";
|
|
11
|
+
const SKILL_FILE = "SKILL.md";
|
|
12
|
+
const AGENT_CONTEXT_REL = join("docs", "templates", "agent-context");
|
|
13
|
+
|
|
14
|
+
/** 固定パスの指示搭載ファイル(cwd 相対)。存在しなければ skip。 */
|
|
15
|
+
const FIXED_FILES: readonly string[] = ["CLAUDE.md", "AGENTS.md", join(".claude", "settings.json")];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* WI-259 / ADR-030 §Decision.3.④ — advisory インジェクションスキャナ(L3-006)の走査アダプタ。
|
|
19
|
+
*
|
|
20
|
+
* 指示搭載ファイル群を cwd 起点で列挙・読み込みして InjectionScanTarget[] を返す
|
|
21
|
+
* (targetPaths 非依存の corpus 走査。L2-016 と同様に自前でファイル探索する):
|
|
22
|
+
* - `skills/*/SKILL.md`(readdir)
|
|
23
|
+
* - `CLAUDE.md` / `AGENTS.md` / `.claude/settings.json`(固定パス)
|
|
24
|
+
* - `docs/templates/agent-context/*.md`(readdir)
|
|
25
|
+
*
|
|
26
|
+
* 不在ファイルは黙って skip する。path は project-relative(posix 区切り)で報告する。
|
|
27
|
+
*/
|
|
28
|
+
export class FileSystemInjectionScanAdapter implements InjectionScanPolicyPort {
|
|
29
|
+
constructor(private readonly projectRoot: string) {}
|
|
30
|
+
|
|
31
|
+
async collect(): Promise<readonly InjectionScanTarget[]> {
|
|
32
|
+
const targets: InjectionScanTarget[] = [];
|
|
33
|
+
|
|
34
|
+
// skills/*/SKILL.md
|
|
35
|
+
const skillsRoot = join(this.projectRoot, SKILLS_DIR);
|
|
36
|
+
let skillDirs: string[] = [];
|
|
37
|
+
try {
|
|
38
|
+
const entries = await readdir(skillsRoot, { withFileTypes: true });
|
|
39
|
+
skillDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
40
|
+
} catch {
|
|
41
|
+
skillDirs = [];
|
|
42
|
+
}
|
|
43
|
+
for (const dir of skillDirs) {
|
|
44
|
+
const relPath = `${SKILLS_DIR}/${dir}/${SKILL_FILE}`;
|
|
45
|
+
const target = await this.readTarget(join(skillsRoot, dir, SKILL_FILE), relPath);
|
|
46
|
+
if (target) targets.push(target);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// docs/templates/agent-context/*.md
|
|
50
|
+
const agentContextRoot = join(this.projectRoot, AGENT_CONTEXT_REL);
|
|
51
|
+
let agentContextFiles: string[] = [];
|
|
52
|
+
try {
|
|
53
|
+
const entries = await readdir(agentContextRoot, { withFileTypes: true });
|
|
54
|
+
agentContextFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
|
|
55
|
+
} catch {
|
|
56
|
+
agentContextFiles = [];
|
|
57
|
+
}
|
|
58
|
+
for (const name of agentContextFiles) {
|
|
59
|
+
const relPath = `${AGENT_CONTEXT_REL.replace(/\\/g, "/")}/${name}`;
|
|
60
|
+
const target = await this.readTarget(join(agentContextRoot, name), relPath);
|
|
61
|
+
if (target) targets.push(target);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 固定パス
|
|
65
|
+
for (const rel of FIXED_FILES) {
|
|
66
|
+
const posixRel = rel.replace(/\\/g, "/");
|
|
67
|
+
const target = await this.readTarget(join(this.projectRoot, rel), posixRel);
|
|
68
|
+
if (target) targets.push(target);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return Object.freeze(targets);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private async readTarget(absPath: string, relPath: string): Promise<InjectionScanTarget | null> {
|
|
75
|
+
try {
|
|
76
|
+
const content = await readFile(absPath, "utf-8");
|
|
77
|
+
return { path: relPath, content };
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
package/skills/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Skills ディレクトリ
|
|
2
2
|
|
|
3
|
-
このディレクトリには、AIエージェントの共有スキル定義が含まれています。現在の配布対象は
|
|
3
|
+
このディレクトリには、AIエージェントの共有スキル定義が含まれています。現在の配布対象は 29 skills です。公開一覧は `docs/guide/skills-overview.md`、setup lifecycle の管理対象は `docs/guide/setup-artifacts.md` を正とします。<!-- @work-item-id WI-154 -->
|
|
4
4
|
|
|
5
5
|
## ディレクトリ構成と同期
|
|
6
6
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: doc-health-checker
|
|
3
|
+
kind: advisory
|
|
4
|
+
description: 設計文書の健全性(鮮度 + ポインタ有効性)を L4 バリデータ CLI で診断・対処するスキル。`npx phasegate p2:check-freshness`(鮮度・code-design drift, L4-004)と `npx phasegate p2:validate-pointers`(壊れたファイルパスポインタ, L4-005)をラップし、結果を解釈して修正アクションを提案する。使用タイミング:「設計文書が古くなっていないか確認して」「ドキュメントの鮮度チェック」「設計とコードの乖離を調べて」「ドキュメントのリンク切れを確認して」「broken pointer を探して」「設計文書の参照が正しいか確認して」「L4 doc health チェック」など。
|
|
5
|
+
model: sonnet
|
|
6
|
+
review: opus
|
|
7
|
+
languages: [typescript]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Doc Health Checker
|
|
11
|
+
|
|
12
|
+
## 目的
|
|
13
|
+
|
|
14
|
+
設計文書の健全性を 2 つの観点で機械的に検証し、結果を解釈して修正アクションを提案する advisory スキル。旧 `doc-freshness-checker`(鮮度)と旧 `pointer-validator`(ポインタ有効性)を統合したもの。
|
|
15
|
+
|
|
16
|
+
1. **鮮度(freshness)— L4-004**: 設計文書の最終更新が古すぎないか、コード変更と設計文書の乖離(code-design drift)がないかを検出する。`npx phasegate p2:check-freshness` をラップする。
|
|
17
|
+
2. **ポインタ有効性(pointer validity)— L4-005**: 設計文書内で参照されているファイルパスポインタが実在するか(broken pointer)を検出する。`npx phasegate p2:validate-pointers` をラップする。
|
|
18
|
+
|
|
19
|
+
いずれも L4 バリデータの CLI 拡張であり、本スキルは CLI を実行して結果を解釈し、ユーザーに対処案を提示する。
|
|
20
|
+
|
|
21
|
+
> **重要(CLI 名の正)**: コマンドは必ず `p2:` 接頭辞付きで呼ぶ。無接頭辞の `phasegate check-freshness` / `phasegate validate-pointers` は **存在しない誤った表記**(旧スキルの記載は誤りだった)。正しくは `npx phasegate p2:check-freshness` / `npx phasegate p2:validate-pointers`。
|
|
22
|
+
|
|
23
|
+
## 対象読者・使いどころ
|
|
24
|
+
|
|
25
|
+
- 設計文書(`docs/product/construction/` や `docs/inception/` 配下の `.md`)が実装から取り残されていないか、参照リンクが壊れていないかを点検したいとき。
|
|
26
|
+
- リリース前・大規模リファクタ後・Unit 完了時のドキュメント健全性確認。
|
|
27
|
+
- 軽量チェックのため、鮮度とポインタの両方をまとめて走らせるのが基本運用。
|
|
28
|
+
|
|
29
|
+
## CLI リファレンス
|
|
30
|
+
|
|
31
|
+
### 1. 鮮度チェック — `p2:check-freshness`(L4-004)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx phasegate p2:check-freshness [--pattern <glob>] [--format text|json] [--dry-run]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| オプション | 意味 |
|
|
38
|
+
|-----------|------|
|
|
39
|
+
| `--pattern <glob>` | 対象ファイルの glob(未指定時は config の設計文書パスが対象) |
|
|
40
|
+
| `--format text\|json` | 出力形式(デフォルト `text`) |
|
|
41
|
+
| `--dry-run` | 副作用なしで診断のみ |
|
|
42
|
+
|
|
43
|
+
`CheckDocFreshnessOutput` の主なフィールド:
|
|
44
|
+
|
|
45
|
+
| フィールド | 意味 |
|
|
46
|
+
|-----------|------|
|
|
47
|
+
| `results[].status` | `ok` / `warn` / `error` |
|
|
48
|
+
| `results[].documentPath` | チェック対象ファイル |
|
|
49
|
+
| `results[].daysSinceUpdate` | 最終更新からの日数 |
|
|
50
|
+
| `results[].threshold` | 設定閾値 |
|
|
51
|
+
| `summary.error` | error 件数(exit code 1 になる) |
|
|
52
|
+
| `summary.warn` | warn 件数 |
|
|
53
|
+
|
|
54
|
+
**exit code**: `summary.error > 0` なら 1、それ以外 0。
|
|
55
|
+
|
|
56
|
+
### 2. ポインタ検証 — `p2:validate-pointers`(L4-005)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx phasegate p2:validate-pointers [--pattern <glob>] [--include-urls] [--format text|json]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| オプション | 意味 |
|
|
63
|
+
|-----------|------|
|
|
64
|
+
| `--pattern <glob>` | 対象ファイルの glob(未指定時は config の設計文書パスが対象) |
|
|
65
|
+
| `--include-urls` | URL ポインタ(`http(s)://`)も検証対象に含める(デフォルトは file-path のみ) |
|
|
66
|
+
| `--format text\|json` | 出力形式(デフォルト `text`) |
|
|
67
|
+
|
|
68
|
+
検出対象ポインタ: Markdown `[text](path)` リンク / `@file:` `@ref:` / `filePath:` フィールドのパス参照。`http(s)://` URL は `--include-urls` 指定時のみ検証。
|
|
69
|
+
|
|
70
|
+
`ValidateDocPointersOutput` の主なフィールド:
|
|
71
|
+
|
|
72
|
+
| フィールド | 意味 |
|
|
73
|
+
|-----------|------|
|
|
74
|
+
| `results[].documentPath` | ポインタを含む文書 |
|
|
75
|
+
| `results[].pointerTarget` | 参照先パス |
|
|
76
|
+
| `results[].pointerType` | `file-path` / `url` |
|
|
77
|
+
| `results[].isResolvable` | 解決可能かどうか |
|
|
78
|
+
| `results[].errorMessage` | エラー内容(null なら正常) |
|
|
79
|
+
| `summary.brokenPointers` | broken 件数 |
|
|
80
|
+
| `summary.skippedUrlPointers` | スキップした URL 件数 |
|
|
81
|
+
| `passed` | 全ポインタ有効なら true |
|
|
82
|
+
|
|
83
|
+
**exit code**: `passed` なら 0、broken があれば 1。
|
|
84
|
+
|
|
85
|
+
> **注意**: このスキルの CLI には自動修正フラグ(`--fix`)は存在しない。ポインタ修正は結果を解釈したうえで手動(`Edit`)で行う。
|
|
86
|
+
|
|
87
|
+
## ワークフロー
|
|
88
|
+
|
|
89
|
+
軽量チェックのため単一フェーズで実行する。鮮度とポインタは独立なので、必要に応じて片方だけ/両方を走らせる。
|
|
90
|
+
|
|
91
|
+
### Step 1: 実行
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 両方まとめて(推奨)
|
|
95
|
+
npx phasegate p2:check-freshness --format json
|
|
96
|
+
npx phasegate p2:validate-pointers --format json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
大量に broken が出そうな大規模リポジトリでは `--pattern` でスコープを絞って段階実行する。
|
|
100
|
+
|
|
101
|
+
### Step 2: 結果の解釈とアクション
|
|
102
|
+
|
|
103
|
+
**鮮度(freshness)**:
|
|
104
|
+
|
|
105
|
+
| 状態 | 推奨アクション |
|
|
106
|
+
|------|--------------|
|
|
107
|
+
| `error`(閾値超過 / drift 疑い) | `cascade-updater` で上位設計文書を更新、コードと設計の乖離を解消 |
|
|
108
|
+
| `warn`(閾値近接) | 設計文書の内容を確認し、必要に応じて更新 |
|
|
109
|
+
| `ok` | 対応不要 |
|
|
110
|
+
|
|
111
|
+
**ポインタ(pointer)**: broken の原因別に対処する。
|
|
112
|
+
|
|
113
|
+
| broken 原因 | 推奨アクション |
|
|
114
|
+
|-----------|--------------|
|
|
115
|
+
| ファイルが移動された | ポインタのパスを新パスに更新(`Edit`) |
|
|
116
|
+
| ファイルが削除された | ポインタを削除 or 代替ファイルに変更 |
|
|
117
|
+
| タイポ | パス修正 |
|
|
118
|
+
| 未作成ファイルへの前方参照 | 意図的なら `[TODO]` マーカーを付ける |
|
|
119
|
+
|
|
120
|
+
修正後は同じコマンドを再実行して 0 件(`passed: true` / `summary.error: 0`)を確認する。
|
|
121
|
+
|
|
122
|
+
### 出力フォーマット(ユーザー報告例)
|
|
123
|
+
|
|
124
|
+
```markdown
|
|
125
|
+
# Doc Health チェック結果
|
|
126
|
+
|
|
127
|
+
## 鮮度(p2:check-freshness / L4-004)
|
|
128
|
+
- 総ドキュメント数: N / ok: N / warn: N / error: N
|
|
129
|
+
### 要対応(error)
|
|
130
|
+
| ファイル | 最終更新 | 閾値超過日数 | 推奨アクション |
|
|
131
|
+
|---------|---------|------------|--------------|
|
|
132
|
+
|
|
133
|
+
## ポインタ(p2:validate-pointers / L4-005)
|
|
134
|
+
- チェック文書数: N / broken: N / URL(スキップ): N
|
|
135
|
+
### Broken Pointers(要修正)
|
|
136
|
+
| 文書ファイル | 参照先 | エラー | 推奨修正 |
|
|
137
|
+
|------------|--------|--------|---------|
|
|
138
|
+
|
|
139
|
+
## 次のアクション
|
|
140
|
+
(error があれば cascade-updater、broken があれば該当ポインタの Edit を提案)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 関連スキル
|
|
144
|
+
|
|
145
|
+
| スキル | 用途 |
|
|
146
|
+
|-------|------|
|
|
147
|
+
| `cascade-updater` | 鮮度 error が出た設計文書の連鎖更新 |
|
|
148
|
+
| `consistency-checker` | 文書間の内容整合性チェック(健全性修正後の確認) |
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-publisher
|
|
3
|
+
kind: advisory
|
|
4
|
+
description: phasegate のリリース手順(version bump / git tag / npm publish)の厳格ガイド。この npm アカウントは security key (FIDO/WebAuthn) 認証のため publish は必ず `npm publish --auth-type=web`。使用タイミング:「リリースして」「publish して」「npm に上げて」「バージョンを上げてタグを打って」「EOTP エラーが出た」「npm publish が認証で失敗する」など、version bump・タグ付与・npm publish・publish 認証トラブルに関わる場面。
|
|
5
|
+
model: sonnet
|
|
6
|
+
review: opus
|
|
7
|
+
languages: [typescript]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Release Publisher
|
|
11
|
+
|
|
12
|
+
## 目的
|
|
13
|
+
|
|
14
|
+
phasegate パッケージのリリース(version bump → git tag → npm publish)を、誤操作・認証事故なしに完遂するための厳格手順ガイド(advisory)。正となる規範は `CLAUDE.md` の「バージョニングルール」「npm publish ルール」節、および `DEVELOPMENT.md` の「Versioning and Release」「Troubleshooting npm publish authentication」節。本スキルはそれらを実行手順として展開する。
|
|
15
|
+
|
|
16
|
+
## 絶対ルール(違反禁止)
|
|
17
|
+
|
|
18
|
+
1. **この npm アカウントは security key (FIDO/WebAuthn) 認証**。publish は必ず:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm publish --auth-type=web # ブラウザで security key 認証 → publish
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. **禁止事項**:
|
|
25
|
+
- `npm publish --otp=<code>` を実行すること(`--otp` フラグは TOTP 専用。security key 認証では拒否される)
|
|
26
|
+
- ユーザーに OTP を尋ねること
|
|
27
|
+
- authenticator アプリの参照を提案すること
|
|
28
|
+
3. **EOTP エラーが出たら、OTP を聞き返さずに直ちに `--auth-type=web` に切り替える。**
|
|
29
|
+
4. **コミットごとに `package.json` の minor バージョンを上げる**(`"0.X.0"` → `"0.(X+1).0"`)。タグ `vX.Y.Z` を付与して push する。
|
|
30
|
+
|
|
31
|
+
## リリース手順
|
|
32
|
+
|
|
33
|
+
### Step 1: 事前クロスチェック(3点照合)
|
|
34
|
+
|
|
35
|
+
local の `package.json` は npm registry と乖離しうる(過去に registry v0.32.0 / local v0.38.0 の乖離事例あり)。publish 前に必ず 3 つを照合する:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm view phasegate version # registry latest
|
|
39
|
+
git tag --list | tail -5 # local tags
|
|
40
|
+
grep '"version"' package.json # local version
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
乖離があれば原因(publish 漏れ / tag 漏れ)を特定してから進む。
|
|
44
|
+
|
|
45
|
+
### Step 2: version bump と CHANGELOG
|
|
46
|
+
|
|
47
|
+
1. `package.json` の `version` を minor bump(例: `0.5.0` → `0.6.0`)
|
|
48
|
+
2. `CHANGELOG.md` にエントリを追加
|
|
49
|
+
|
|
50
|
+
### Step 3: commit / tag / push
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git add package.json CHANGELOG.md
|
|
54
|
+
git commit -m "fix: vX.Y.Z — description"
|
|
55
|
+
git tag vX.Y.Z
|
|
56
|
+
git push origin main --tags
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Step 4: 認証確認と dry-run
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm whoami # 未ログインなら: npm login --auth-type=web
|
|
63
|
+
npm publish --dry-run # tarball の内容を確認
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Step 5: publish
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm publish --auth-type=web
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
ブラウザが開くので security key で認証すると publish が完了する。
|
|
73
|
+
|
|
74
|
+
## 認証トラブルシューティング
|
|
75
|
+
|
|
76
|
+
`npm publish` の挙動はアカウントの 2FA モードに依存する:
|
|
77
|
+
|
|
78
|
+
| 2FA モード | publish の挙動 | 対処 |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| 無効 | そのまま通る | `npm publish` |
|
|
81
|
+
| TOTP (authenticator アプリ) | `EOTP` エラー → OTP 要求 | `npm publish --otp=<6桁>` |
|
|
82
|
+
| Email OTP (Enhanced Login Verification) | `EOTP` エラー → メールでコード送付 | 受信箱を確認して `--otp=<6桁>` |
|
|
83
|
+
| **Security key / Passkey (FIDO/WebAuthn)** ← このアカウント | `EOTP` エラー(`--otp` は TOTP 専用のため使用不可) | **`npm publish --auth-type=web`** — ブラウザが開き、キーで認証して publish 完了 |
|
|
84
|
+
|
|
85
|
+
**security key アカウントの鉄則**: 常に `--auth-type=web` を使う。`--otp` フラグは TOTP 専用であり拒否される。EOTP エラーを見たら OTP を探しに行かず、直ちに `--auth-type=web` で再実行する。
|
|
86
|
+
|
|
87
|
+
**CI / 自動化**: https://www.npmjs.com/settings/<username>/tokens から **Granular Access Token**(scope: 対象パッケージ、permissions: Read and write、Bypass 2FA: enabled)を発行し、`.npmrc` の `//registry.npmjs.org/:_authToken=<TOKEN>` または `NPM_TOKEN` 環境変数で注入する。
|
|
88
|
+
|
|
89
|
+
## よくある失敗と回避
|
|
90
|
+
|
|
91
|
+
| 症状 | 原因 | 回避 |
|
|
92
|
+
|------|------|------|
|
|
93
|
+
| `EOTP` エラーで停止し OTP を探し始める | `--otp` が TOTP 専用であることを知らない | 直ちに `--auth-type=web` に切替(OTP を聞き返さない) |
|
|
94
|
+
| registry と local version の乖離 | publish 漏れ・tag 漏れの蓄積 | Step 1 の 3 点照合を必ず実施 |
|
|
95
|
+
| tag なしで publish | 手順の省略 | Step 3 の tag + `--tags` push を省略しない |
|
|
96
|
+
| CHANGELOG 未更新のまま publish | version bump のみ実施 | Step 2 で CHANGELOG エントリ追加を必ずセットで行う |
|
|
97
|
+
|
|
98
|
+
## 参照
|
|
99
|
+
|
|
100
|
+
- `CLAUDE.md` — 「バージョニングルール(必須)」「npm publish ルール(必須)」
|
|
101
|
+
- `DEVELOPMENT.md` — 「Versioning and Release」「Troubleshooting npm publish authentication」「Cross-check before publishing」
|