phasegate 0.191.0 → 0.222.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.ja.md +52 -15
  3. package/README.md +39 -11
  4. package/docs/ADR/030-injection-threat-model-and-trust-root.md +145 -0
  5. package/docs/guide/hooks-integration.md +101 -1
  6. package/docs/guide/installation.md +1 -1
  7. package/docs/guide/quick-vs-full-mode.md +1 -1
  8. package/docs/guide/skills-overview.md +7 -8
  9. package/package.json +1 -1
  10. package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +131 -63
  11. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +31 -4
  12. package/scripts/harness/agent-integration/presentation/spotlight.ts +65 -0
  13. package/scripts/harness/biome-ast-engine/application/mappers/build-harness-error-payload-output-mapper.ts +3 -3
  14. package/scripts/harness/biome-ast-engine/infrastructure/mappers/rule-violation-code-mapper.ts +3 -3
  15. package/scripts/harness/biome-ast-engine/presentation/cli/harness-lint-command-handler.ts +6 -4
  16. package/scripts/harness/ci-governance/application/dto/pin-integrity-input.ts +8 -0
  17. package/scripts/harness/ci-governance/application/dto/pin-integrity-output.ts +9 -0
  18. package/scripts/harness/ci-governance/application/dto/verify-integrity-input.ts +7 -0
  19. package/scripts/harness/ci-governance/application/dto/verify-integrity-output.ts +10 -0
  20. package/scripts/harness/ci-governance/application/usecases/pin-integrity-usecase.ts +60 -0
  21. package/scripts/harness/ci-governance/application/usecases/verify-integrity-usecase.ts +46 -0
  22. package/scripts/harness/ci-governance/composition-root.ts +67 -64
  23. package/scripts/harness/ci-governance/domain/ports/integrity-manifest-repository-port.ts +14 -0
  24. package/scripts/harness/ci-governance/domain/ports/sha256-hasher-port.ts +10 -0
  25. package/scripts/harness/ci-governance/domain/services/integrity-checker.ts +42 -0
  26. package/scripts/harness/ci-governance/domain/value-objects/integrity-drift.ts +16 -0
  27. package/scripts/harness/ci-governance/domain/value-objects/integrity-manifest.ts +49 -0
  28. package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +40 -0
  29. package/scripts/harness/ci-governance/infrastructure/adapters/file-system-sha256-hasher-adapter.ts +17 -0
  30. package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +7 -73
  31. package/scripts/harness/ci-governance/infrastructure/adapters/integrity-manifest-json-repository-adapter.ts +83 -0
  32. package/scripts/harness/ci-governance/presentation/handlers/integrity-handler.ts +76 -0
  33. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +60 -28
  34. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +33 -8
  35. package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +90 -0
  36. package/scripts/harness/harness-error/infrastructure/registry/l1-error-definitions.ts +9 -9
  37. package/scripts/harness/installation/application/bundled-skill-selection.ts +2 -5
  38. package/scripts/harness/installation/application/usecases/run-reconcile.ts +69 -1
  39. package/scripts/harness/main.ts +257 -105
  40. package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +65 -3
  41. package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +44 -40
  42. package/scripts/harness/setup/skill-deployer.ts +2 -4
  43. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +82 -49
  44. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +133 -53
  45. package/scripts/harness/validator-system/composition-root.ts +153 -99
  46. package/scripts/harness/validator-system/domain/ports/coverage-attestation-gating-policy-port.ts +14 -0
  47. package/scripts/harness/validator-system/domain/ports/coverage-attestation-verification-policy-port.ts +30 -0
  48. package/scripts/harness/validator-system/domain/ports/injection-scan-policy-port.ts +14 -0
  49. package/scripts/harness/validator-system/domain/services/coverage-attestation-gating-service.ts +56 -0
  50. package/scripts/harness/validator-system/domain/services/coverage-attestation-verification-service.ts +45 -0
  51. package/scripts/harness/validator-system/domain/services/injection-pattern-scan-service.ts +118 -0
  52. package/scripts/harness/validator-system/domain/value-objects/attestation-verification-report.ts +59 -0
  53. package/scripts/harness/validator-system/domain/value-objects/coverage-gating-report.ts +67 -0
  54. package/scripts/harness/validator-system/domain/value-objects/injection-scan-report.ts +55 -0
  55. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +37 -31
  56. package/scripts/harness/validator-system/infrastructure/adapters/adr-foundation-reference-adapter.ts +13 -7
  57. package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-gating-adapter.ts +87 -0
  58. package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-verification-adapter.ts +165 -0
  59. package/scripts/harness/validator-system/infrastructure/adapters/file-system-injection-scan-adapter.ts +82 -0
  60. package/skills/README.md +1 -1
  61. package/skills/codebase-mapper/SKILL.md +1 -1
  62. 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
  63. package/skills/doc-health-checker/SKILL.md +148 -0
  64. package/skills/release-publisher/SKILL.md +101 -0
  65. package/skills/skill-creator/SKILL.md +74 -332
  66. package/skills/story-implementor/SKILL.md +52 -0
  67. package/skills/story-mapper/SKILL.md +4 -0
  68. package/skills/story-writer/SKILL.md +9 -0
  69. package/skills/uiux-designer/references/uiux-design-template.md +4 -4
  70. package/skills/unit-designer/SKILL.md +3 -1
  71. package/templates/.claude/scripts/deny-check.sh +260 -0
  72. package/skills/doc-freshness-checker/SKILL.md +0 -140
  73. package/skills/implementation-planner/SKILL.md +0 -169
  74. package/skills/implementation-planner/references/document-structure.md +0 -116
  75. package/skills/implementation-planner/references/plan-template.md +0 -177
  76. package/skills/implementation-planner/references/workflow.md +0 -164
  77. package/skills/pointer-validator/SKILL.md +0 -105
@@ -0,0 +1,165 @@
1
+ // @unit validator-system
2
+ // @layer infrastructure
3
+ // @work-item-id WI-268
4
+
5
+ import { readdir, readFile } from "node:fs/promises";
6
+ import { isAbsolute, join } from "node:path";
7
+ import type {
8
+ CoverageAttestationVerificationCollectResult,
9
+ CoverageAttestationVerificationPolicyPort,
10
+ } from "../../domain/ports/coverage-attestation-verification-policy-port.js";
11
+ import type { AttestationReference } from "../../domain/value-objects/attestation-verification-report.js";
12
+
13
+ const COVERAGE_REPORT_FILE = "coverage_report.md";
14
+ const CONSTRUCTION_REL = join("docs", "product", "construction");
15
+ const LEGACY_MARKER = /<!--\s*@coverage-gating:\s*ungated-legacy\s*-->/;
16
+ /** `<!-- @attestation <id> -->` の id を捕捉する(1 行内複数を許容)。 */
17
+ const ATTESTATION_REF_GLOBAL = /<!--\s*@attestation\s+([^\s>-][^>]*?)\s*-->/g;
18
+ const DEFAULT_MATRIX_PATH = ".harness/requirement-test-matrix.json";
19
+
20
+ interface MatrixTestReference {
21
+ readonly filePath?: string;
22
+ }
23
+
24
+ interface MatrixAcMapping {
25
+ readonly testReferences?: readonly MatrixTestReference[];
26
+ }
27
+
28
+ interface MatrixStory {
29
+ readonly storyId?: string;
30
+ readonly storyMappings?: readonly MatrixAcMapping[];
31
+ readonly acMappings?: readonly MatrixAcMapping[];
32
+ }
33
+
34
+ /**
35
+ * WI-268 / ADR-030 §Decision.1・§Decision.3.②(第2段) — L3-007 の走査・解決アダプタ。
36
+ *
37
+ * `docs/product/construction/*​/coverage_report.md` を cwd 起点で走査し(targetPaths 非依存の
38
+ * corpus 走査。L2-016 と同様に自前でファイル探索する)、ungated-legacy マーカー付きファイルを除外して
39
+ * `<!-- @attestation <id> -->` 参照を抽出する。
40
+ *
41
+ * 参照が 1 件以上ある場合のみ requirement-test-matrix を読み、resolvable scope
42
+ * (storyId 存在 かつ testReferences >= 1 の story-id 集合)を解決する。matrix 不在・parse 不能は
43
+ * fail-closed(matrixError を返す)。参照 0 件なら matrix を読まず空 evidence + matrixError=null。
44
+ */
45
+ export class FileSystemCoverageAttestationVerificationAdapter
46
+ implements CoverageAttestationVerificationPolicyPort
47
+ {
48
+ constructor(
49
+ private readonly projectRoot: string,
50
+ private readonly matrixFilePath: string = DEFAULT_MATRIX_PATH,
51
+ ) {}
52
+
53
+ async collect(): Promise<CoverageAttestationVerificationCollectResult> {
54
+ const references = await this.collectReferences();
55
+
56
+ // 参照が 1 件も無ければ matrix を読みに行かず PASS(最小副作用)。
57
+ if (references.length === 0) {
58
+ return Object.freeze({
59
+ references: Object.freeze([]),
60
+ evidence: { resolvableScopeIds: new Set<string>() },
61
+ matrixError: null,
62
+ });
63
+ }
64
+
65
+ const resolved = await this.resolveScopeEvidence();
66
+ if (resolved.error !== null) {
67
+ // 参照ありで matrix を読めない → fail-closed。
68
+ return Object.freeze({
69
+ references: Object.freeze(references),
70
+ evidence: { resolvableScopeIds: new Set<string>() },
71
+ matrixError: resolved.error,
72
+ });
73
+ }
74
+
75
+ return Object.freeze({
76
+ references: Object.freeze(references),
77
+ evidence: { resolvableScopeIds: resolved.scopeIds },
78
+ matrixError: null,
79
+ });
80
+ }
81
+
82
+ private async collectReferences(): Promise<AttestationReference[]> {
83
+ const constructionRoot = join(this.projectRoot, CONSTRUCTION_REL);
84
+ let unitDirs: string[];
85
+ try {
86
+ const entries = await readdir(constructionRoot, { withFileTypes: true });
87
+ unitDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
88
+ } catch {
89
+ return [];
90
+ }
91
+
92
+ const references: AttestationReference[] = [];
93
+ for (const unitDir of unitDirs) {
94
+ const filePath = join(constructionRoot, unitDir, COVERAGE_REPORT_FILE);
95
+ let content: string;
96
+ try {
97
+ content = await readFile(filePath, "utf-8");
98
+ } catch {
99
+ continue;
100
+ }
101
+ const relPath = `${CONSTRUCTION_REL.replace(/\\/g, "/")}/${unitDir}/${COVERAGE_REPORT_FILE}`;
102
+ this.parseReferences(relPath, content, references);
103
+ }
104
+ return references;
105
+ }
106
+
107
+ /** ungated-legacy マーカー付きファイルは免除。それ以外は各 @attestation 参照を抽出する。 */
108
+ private parseReferences(path: string, content: string, out: AttestationReference[]): void {
109
+ const lines = content.split(/\r?\n/);
110
+ if (lines.some((line) => LEGACY_MARKER.test(line))) return;
111
+
112
+ for (let i = 0; i < lines.length; i++) {
113
+ const line = lines[i];
114
+ ATTESTATION_REF_GLOBAL.lastIndex = 0;
115
+ let match: RegExpExecArray | null = ATTESTATION_REF_GLOBAL.exec(line);
116
+ while (match !== null) {
117
+ const id = match[1].trim();
118
+ if (id.length > 0) {
119
+ out.push({ id, sourcePath: path, lineNumber: i + 1 });
120
+ }
121
+ match = ATTESTATION_REF_GLOBAL.exec(line);
122
+ }
123
+ }
124
+ }
125
+
126
+ private async resolveScopeEvidence(): Promise<{ scopeIds: Set<string>; error: string | null }> {
127
+ const relativeOrAbsolute = this.matrixFilePath.length > 0 ? this.matrixFilePath : DEFAULT_MATRIX_PATH;
128
+ const absPath = isAbsolute(relativeOrAbsolute)
129
+ ? relativeOrAbsolute
130
+ : join(this.projectRoot, relativeOrAbsolute);
131
+
132
+ let parsed: unknown;
133
+ try {
134
+ const raw = await readFile(absPath, "utf-8");
135
+ parsed = JSON.parse(raw);
136
+ } catch (error) {
137
+ const message = error instanceof Error ? error.message : String(error);
138
+ return {
139
+ scopeIds: new Set<string>(),
140
+ error: `requirement-test-matrix を読み込めません(L3-007 は fail-closed): ${relativeOrAbsolute}: ${message}`,
141
+ };
142
+ }
143
+
144
+ const scopeIds = new Set<string>();
145
+ for (const story of this.extractStories(parsed)) {
146
+ const storyId = story.storyId;
147
+ if (!storyId) continue;
148
+ const mappings = story.storyMappings ?? story.acMappings ?? [];
149
+ const hasTestRef = mappings.some((ac) => (ac.testReferences?.length ?? 0) > 0);
150
+ if (hasTestRef) scopeIds.add(storyId);
151
+ }
152
+ return { scopeIds, error: null };
153
+ }
154
+
155
+ private extractStories(parsed: unknown): readonly MatrixStory[] {
156
+ if (typeof parsed !== "object" || parsed === null) return [];
157
+ const obj = parsed as { stories?: unknown; storyMappings?: unknown };
158
+ const raw = Array.isArray(obj.stories)
159
+ ? obj.stories
160
+ : Array.isArray(obj.storyMappings)
161
+ ? obj.storyMappings
162
+ : [];
163
+ return raw as readonly MatrixStory[];
164
+ }
165
+ }
@@ -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エージェントの共有スキル定義が含まれています。現在の配布対象は 30 skills です。公開一覧は `docs/guide/skills-overview.md`、setup lifecycle の管理対象は `docs/guide/setup-artifacts.md` を正とします。<!-- @work-item-id WI-154 -->
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
 
@@ -161,4 +161,4 @@ unit-c
161
161
  |--------------|------|
162
162
  | `npx phasegate lint` | L1 archgateルールの全体スキャン |
163
163
  | `consistency-checker` | マップ生成後の設計文書整合性確認 |
164
- | `doc-freshness-checker` | マップと設計文書の鮮度を合わせて確認 |
164
+ | `doc-health-checker` | マップと設計文書の鮮度・ポインタ有効性を合わせて確認 |
@@ -50,7 +50,7 @@
50
50
 
51
51
  ```
52
52
  # ストーリーID抽出(マークダウンテーブルから)
53
- # {storyIdPattern} はプロジェクトのストーリーID正規表現(例: US-[0-9]+)
53
+ # {storyIdPattern} はプロジェクトのストーリーID正規表現(phasegate 既定は H[F0-9][0-9]*-[0-9]{2})
54
54
  grep -oE '{storyIdPattern}' {userStoriesPath}
55
55
 
56
56
  # Unit内のストーリー参照抽出
@@ -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/&lt;username&gt;/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」