phasegate 0.160.10 → 0.160.11

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 CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.160.11] - 2026-05-17
11
+
12
+ ### Fixed
13
+
14
+ - **WI-202 / WI-204 — Quick Mode strict recovery** — aligns strict init Quick Mode categories with quick-implementor scope, adds `config:plan --intent quick-mode-relax` for managed `allowedCategories` recovery, updates config edit block guidance to the matching recovery intent, and excludes CWD-external runtime paths from project-local hook policy.
15
+
10
16
  ## [0.160.10] - 2026-05-16
11
17
 
12
18
  ### Fixed
@@ -28,7 +28,7 @@ Command names in this document are split into three surfaces:
28
28
  | `uninstall --dry-run` / `--apply` | Remove PhaseGate-managed files and managed blocks using `.phasegate/manifest.json`; `--force` handles managed conflict cases. |
29
29
  | `reconcile --dry-run` / `--apply` | Update PhaseGate-managed files to current package templates and refresh manifest hashes; `--force` allows managed-file replacement with backup. |
30
30
  | `setup:agent` | Agent-readable setup planner and optional apply path. Options: `--intent <minimal\|recommended\|strict\|ci-only\|agent-hooks\|retrofit>`, `--agent <claude\|codex\|both>`, `--workflow <standard\|strict>`, `--with-husky`, `--with-ci`, `--dry-run`, `--apply`, `--json`. <!-- @work-item-id WI-172 --> |
31
- | `config:plan` | Agent-readable configuration change planner. Options: `--intent <l4-strict\|codex-hooks\|ci-fail-on-warning\|baseline-reset\|quick-mode-strict\|retrofit-bootstrap\|planning-mode-relax>`, `--dry-run`, `--apply`, `--json`. <!-- @work-item-id WI-173 --> <!-- @work-item-id WI-201 --> |
31
+ | `config:plan` | Agent-readable configuration change planner. Options: `--intent <l4-strict\|codex-hooks\|ci-fail-on-warning\|baseline-reset\|quick-mode-strict\|quick-mode-relax\|retrofit-bootstrap\|planning-mode-relax>`, `--dry-run`, `--apply`, `--json`. <!-- @work-item-id WI-173 --> <!-- @work-item-id WI-201 --> <!-- @work-item-id WI-204 --> |
32
32
  | `update-skills` | Compatibility alias for `reconcile`; use `reconcile` for new automation. |
33
33
  | `scaffold-wi <unit> <type>` | Create `docs/inception/{unit}/WI-XXX/description.md` using the next free WI number. |
34
34
  | `emit-agent-rules` | Print the AGENTS.md / CLAUDE.md WI workflow rules block. |
@@ -13,9 +13,10 @@ For agent-assisted changes, preview the intent before editing the config:
13
13
  ```bash
14
14
  npx phasegate config:plan --intent l4-strict --dry-run --json
15
15
  npx phasegate config:plan --intent quick-mode-strict --dry-run --json
16
+ npx phasegate config:plan --intent quick-mode-relax --dry-run --json
16
17
  ```
17
18
 
18
- The plan identifies target fields, managed artifacts, commands, validation, risks, and rollback so an agent can explain the change before applying it. <!-- @work-item-id WI-173 -->
19
+ The plan identifies target fields, managed artifacts, commands, validation, risks, and rollback so an agent can explain the change before applying it. Use `quick-mode-relax` to restore the supported Quick Mode category set after an over-narrowed strict configuration. <!-- @work-item-id WI-173 --> <!-- @work-item-id WI-204 -->
19
20
 
20
21
  ### Full Reference
21
22
 
@@ -58,8 +58,9 @@ Only enable fail-on-warning after reviewing the drift, consistency, dead-code, p
58
58
 
59
59
  ```bash
60
60
  npx phasegate config:plan --intent quick-mode-strict --dry-run --json
61
+ npx phasegate config:plan --intent quick-mode-relax --dry-run --json
61
62
  npx phasegate check-change-category --paths <changed-files> --format json
62
63
  npx phasegate ci-check --quick --dry-run
63
64
  ```
64
65
 
65
- Use `config:plan` before changing `phasegate.config.json`. It separates repo-managed artifacts from user-level settings, previews the local config before/after for config-backed intents, and lists the checks needed after the change. <!-- @work-item-id WI-175 -->
66
+ Use `config:plan` before changing `phasegate.config.json`. It separates repo-managed artifacts from user-level settings, previews the local config before/after for config-backed intents, and lists the checks needed after the change. `quick-mode-relax` is the managed recovery path when strict category narrowing blocks docs/test/config changes. <!-- @work-item-id WI-175 --> <!-- @work-item-id WI-204 -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.160.10",
3
+ "version": "0.160.11",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "MIT",
@@ -6,6 +6,7 @@
6
6
  export interface HandlePreToolUseInput {
7
7
  toolName: string;
8
8
  targetFilePaths: string[];
9
+ callerSkill?: string;
9
10
  targetChanges?: {
10
11
  filePath: string;
11
12
  beforeContent?: string | null;
@@ -160,6 +160,7 @@ export class HandlePreToolUseUseCase {
160
160
  fullModeResult,
161
161
  guidance,
162
162
  unitIdForGuidance,
163
+ input.callerSkill,
163
164
  );
164
165
  }
165
166
  } else {
@@ -236,11 +237,12 @@ export class HandlePreToolUseUseCase {
236
237
  },
237
238
  guidance: ErrorGuidance | null,
238
239
  unitId: string | undefined,
240
+ callerSkill?: string,
239
241
  ): HandlePreToolUseOutput {
240
242
  const fp = blockedFilePath ?? "不明なファイル";
241
243
  if (result.dominantCategory === "config" && /(?:^|\/)phasegate\.config\.json$/.test(fp)) {
242
- const dryRunCommand = "phasegate config:plan --intent retrofit-bootstrap --dry-run --json";
243
- const applyCommand = "phasegate config:plan --intent retrofit-bootstrap --apply --json";
244
+ const dryRunCommand = "phasegate config:plan --intent quick-mode-relax --dry-run --json";
245
+ const applyCommand = "phasegate config:plan --intent quick-mode-relax --apply --json";
244
246
  const lines = [
245
247
  `Full mode 必須変更が検出されました: ${fp}`,
246
248
  "カテゴリ: config",
@@ -263,6 +265,33 @@ export class HandlePreToolUseUseCase {
263
265
  nextAction: `${dryRunCommand} && ${applyCommand}`,
264
266
  };
265
267
  }
268
+ if (
269
+ callerSkill === "quick-implementor" &&
270
+ result.dominantCategory !== undefined &&
271
+ ["bugfix", "docs", "test", "config"].includes(result.dominantCategory)
272
+ ) {
273
+ const dryRunCommand = "phasegate config:plan --intent quick-mode-relax --dry-run --json";
274
+ const applyCommand = "phasegate config:plan --intent quick-mode-relax --apply --json";
275
+ const lines: string[] = [`Full mode 必須変更が検出されました: ${fp}`];
276
+ lines.push(`カテゴリ: ${result.dominantCategory}`);
277
+ if (result.rejectionRule) {
278
+ lines.push(`判定ルール: ${result.rejectionRule}`);
279
+ }
280
+ if (result.rejectionReason) {
281
+ lines.push(`理由: ${result.rejectionReason}`);
282
+ }
283
+ lines.push(`次のアクション: Quick Mode の許可カテゴリを確認してください。緩和する場合は ${dryRunCommand} で差分を確認し、承認後に ${applyCommand} を実行してください。`);
284
+
285
+ return {
286
+ shouldBlock: true,
287
+ blockedFilePath,
288
+ blockReason: "FULL_MODE_REQUIRED",
289
+ error: { message: lines.join("\n") },
290
+ fullModeRejectionRule: result.rejectionRule,
291
+ fullModeDominantCategory: result.dominantCategory,
292
+ nextAction: `${dryRunCommand} && ${applyCommand}`,
293
+ };
294
+ }
266
295
  const lines: string[] = [`Full mode 必須変更が検出されました: ${fp}`];
267
296
  if (result.dominantCategory) {
268
297
  lines.push(`カテゴリ: ${result.dominantCategory}`);
@@ -402,7 +431,7 @@ export class HandlePreToolUseUseCase {
402
431
  {
403
432
  pattern: /(?:^|\/)phasegate\.config\.json$/,
404
433
  message: (fp) =>
405
- `保護ファイルへの書き込みがブロックされました: ${fp}\n設定変更は CLI 経由で計画・適用してください: phasegate config:plan --intent retrofit-bootstrap --dry-run --json / phasegate config:plan --intent retrofit-bootstrap --apply --json`,
434
+ `保護ファイルへの書き込みがブロックされました: ${fp}\nQuick Mode 設定の復旧は CLI 経由で計画・適用してください: phasegate config:plan --intent quick-mode-relax --dry-run --json / phasegate config:plan --intent quick-mode-relax --apply --json`,
406
435
  },
407
436
  {
408
437
  pattern: /(?:^|\/)harness\.config\.json$/,
@@ -22,6 +22,7 @@ import * as fs from 'node:fs/promises';
22
22
  interface PreToolUseHookInput {
23
23
  cwd?: string;
24
24
  tool_name?: string;
25
+ caller_skill?: string;
25
26
  tool_input?: {
26
27
  path?: string;
27
28
  file_path?: string;
@@ -50,8 +51,8 @@ async function readStdin(): Promise<string> {
50
51
  return Buffer.concat(chunks).toString('utf8');
51
52
  }
52
53
 
53
- async function findConfigPath(): Promise<string> {
54
- let dir = process.cwd();
54
+ async function findConfigPath(startDir: string): Promise<string> {
55
+ let dir = startDir;
55
56
  while (true) {
56
57
  const candidate = path.join(dir, 'phasegate.config.json');
57
58
  try {
@@ -63,7 +64,11 @@ async function findConfigPath(): Promise<string> {
63
64
  dir = parent;
64
65
  }
65
66
  }
66
- return path.join(process.cwd(), 'phasegate.config.json');
67
+ return path.join(startDir, 'phasegate.config.json');
68
+ }
69
+
70
+ function isProjectExternalAbsolutePath(filePath: string): boolean {
71
+ return path.isAbsolute(filePath);
67
72
  }
68
73
 
69
74
  async function main(): Promise<void> {
@@ -89,7 +94,7 @@ async function main(): Promise<void> {
89
94
  process.exit(2);
90
95
  }
91
96
 
92
- const cwd = input.cwd ?? process.cwd();
97
+ const cwd = path.resolve(input.cwd ?? process.cwd());
93
98
  const toRelative = (p: string): string => {
94
99
  if (path.isAbsolute(p)) {
95
100
  const rel = path.relative(cwd, p);
@@ -128,7 +133,9 @@ async function main(): Promise<void> {
128
133
  }
129
134
 
130
135
  try {
131
- const configPath = await findConfigPath();
136
+ const configPath = await findConfigPath(cwd);
137
+ const projectTargetFilePaths = targetFilePaths.filter((filePath) => !isProjectExternalAbsolutePath(filePath));
138
+ const projectTargetChanges = targetChanges.filter((change) => !isProjectExternalAbsolutePath(change.filePath));
132
139
  const configQueryPort = new HarnessConfigConfigQueryAdapter(configPath);
133
140
  const phaseGateQueryPort = new PhaseGateQueryAdapter();
134
141
  const storyReflectionQueryPort = new FileSystemStoryReflectionQueryAdapter({
@@ -154,7 +161,13 @@ async function main(): Promise<void> {
154
161
  errorGuidanceQueryPort,
155
162
  });
156
163
 
157
- const output = await useCase.execute({ toolName: effectiveToolName, targetFilePaths, targetChanges });
164
+ const callerSkill = input.caller_skill ?? process.env.PHASEGATE_CALLER_SKILL;
165
+ const output = await useCase.execute({
166
+ toolName: effectiveToolName,
167
+ targetFilePaths: projectTargetFilePaths,
168
+ callerSkill,
169
+ targetChanges: projectTargetChanges,
170
+ });
158
171
 
159
172
  if (output.shouldBlock) {
160
173
  const msg = output.error?.message
@@ -219,7 +219,7 @@ Gate semantics:
219
219
  refresh-claude-md Refresh CLAUDE.md standard sections (--dry-run, --apply, --json)
220
220
  p2:check-agent-context Check AGENTS.md / CLAUDE.md freshness (--threshold-days <n>, --json)
221
221
  setup:agent Plan agent-driven setup (--intent <minimal|recommended|strict|ci-only|agent-hooks|retrofit>, --agent <claude|codex|both>, --dry-run|--apply, --json)
222
- config:plan Plan safe config changes (--intent <l4-strict|codex-hooks|ci-fail-on-warning|baseline-reset|quick-mode-strict|retrofit-bootstrap|planning-mode-relax>, --dry-run|--apply, --json)
222
+ config:plan Plan safe config changes (--intent <l4-strict|codex-hooks|ci-fail-on-warning|baseline-reset|quick-mode-strict|quick-mode-relax|retrofit-bootstrap|planning-mode-relax>, --dry-run|--apply, --json)
223
223
  ci:check-repetition Check error repetition (--code <errorCode>, --reset, --json)
224
224
  baseline Create retrofit baseline snapshot (--dry-run, --force, --paths <glob,glob,...>, --json)
225
225
  scaffold-design Scaffold a design doc (--unit <id>, --phase <logical|domain|uiux|unit-test|it-test>, --dry-run|--apply, --force, --json)
@@ -536,7 +536,7 @@ Options:
536
536
  Produce an agent-readable configuration change plan.
537
537
 
538
538
  Intents:
539
- l4-strict, codex-hooks, ci-fail-on-warning, baseline-reset, quick-mode-strict, retrofit-bootstrap, planning-mode-relax
539
+ l4-strict, codex-hooks, ci-fail-on-warning, baseline-reset, quick-mode-strict, quick-mode-relax, retrofit-bootstrap, planning-mode-relax
540
540
 
541
541
  Options:
542
542
  --dry-run
@@ -779,7 +779,7 @@ function parseCoverageThreshold(raw: string | undefined): number {
779
779
  type InitPhasePreset = "full" | "standard" | "minimal" | "custom";
780
780
  type AgentTarget = "claude" | "codex" | "both";
781
781
  type SetupIntent = "minimal" | "recommended" | "strict" | "ci-only" | "agent-hooks" | "retrofit";
782
- type ConfigChangeIntent = "l4-strict" | "codex-hooks" | "ci-fail-on-warning" | "baseline-reset" | "quick-mode-strict" | "retrofit-bootstrap" | "planning-mode-relax";
782
+ type ConfigChangeIntent = "l4-strict" | "codex-hooks" | "ci-fail-on-warning" | "baseline-reset" | "quick-mode-strict" | "quick-mode-relax" | "retrofit-bootstrap" | "planning-mode-relax";
783
783
  type SetupCompletenessStatus = "configured" | "planned" | "manual" | "not-applicable" | "unknown";
784
784
 
785
785
  interface SetupCompletenessEntry {
@@ -864,6 +864,7 @@ function parseConfigChangeIntent(value: string | undefined): ConfigChangeIntent
864
864
  value === "ci-fail-on-warning" ||
865
865
  value === "baseline-reset" ||
866
866
  value === "quick-mode-strict" ||
867
+ value === "quick-mode-relax" ||
867
868
  value === "retrofit-bootstrap" ||
868
869
  value === "planning-mode-relax"
869
870
  ) {
@@ -1229,9 +1230,12 @@ function buildConfigPatchPreview(intent: ConfigChangeIntent, before: unknown | n
1229
1230
  { pointer: "/layers/L4/failOnWarning", path: ["layers", "L4", "failOnWarning"], value: true },
1230
1231
  ],
1231
1232
  "quick-mode-strict": [
1232
- { pointer: "/quickMode/allowedCategories", path: ["quickMode", "allowedCategories"], value: ["chore"] },
1233
+ { pointer: "/quickMode/allowedCategories", path: ["quickMode", "allowedCategories"], value: ["bugfix"] },
1233
1234
  { pointer: "/quickMode/relaxedGates", path: ["quickMode", "relaxedGates"], value: [] },
1234
1235
  ],
1236
+ "quick-mode-relax": [
1237
+ { pointer: "/quickMode/allowedCategories", path: ["quickMode", "allowedCategories"], value: ["bugfix", "docs", "test", "config"] },
1238
+ ],
1235
1239
  "codex-hooks": [],
1236
1240
  "baseline-reset": [],
1237
1241
  "retrofit-bootstrap": [
@@ -1363,6 +1367,14 @@ async function buildConfigChangePlan(rootDir: string, intent: ConfigChangeIntent
1363
1367
  validations: ["phasegate ci-check --quick --dry-run", "phasegate phasegate:check-ready"],
1364
1368
  risks: ["More changes will require Full Mode validation before commit."],
1365
1369
  },
1370
+ "quick-mode-relax": {
1371
+ targets: ["phasegate.config.json: quickMode.allowedCategories"],
1372
+ managedTargets: ["phasegate.config.json"],
1373
+ externalActions: [],
1374
+ commands: ["phasegate config:plan --intent quick-mode-relax --json", "phasegate config:plan --intent quick-mode-relax --apply --json", "phasegate check-change-category --paths <changed-files> --format json"],
1375
+ validations: ["phasegate ci-check --quick --dry-run", "phasegate phasegate:check-ready"],
1376
+ risks: ["Small bugfix/docs/test/config changes can proceed through Quick Mode again; protected files remain governed by hook and managed command policies."],
1377
+ },
1366
1378
  "retrofit-bootstrap": {
1367
1379
  targets: ["phasegate.config.json: planningMode.default", "phasegate.config.json: phaseDependencies.override", "phasegate.config.json: quickMode.relaxedGates"],
1368
1380
  managedTargets: ["phasegate.config.json"],
@@ -1722,7 +1734,7 @@ async function main(): Promise<void> {
1722
1734
  if (configResult.created) {
1723
1735
  console.log(`✓ phasegate.config.json created`);
1724
1736
  if (workflow === "strict") {
1725
- console.log(`✓ strict workflow configured (quickMode.relaxedGates: [], allowedCategories: ["chore"])`);
1737
+ console.log(`✓ strict workflow configured (quickMode.relaxedGates: [], allowedCategories: ["bugfix","docs","test","config"])`);
1726
1738
  }
1727
1739
  } else {
1728
1740
  console.log(` phasegate.config.json already exists, skipped`);
@@ -26,6 +26,16 @@ const RISK_PRIORITY: Record<string, number> = {
26
26
  function categorizeFile(file: ChangedFile): ChangeCategory {
27
27
  const { filePath, changeKind } = file;
28
28
 
29
+ // Config files must stay config even when Edit payload snippets look like
30
+ // comment/whitespace-only diffs. Config recovery guidance depends on this.
31
+ if (
32
+ filePath.endsWith('.config.json') ||
33
+ filePath.endsWith('.config.ts') ||
34
+ filePath.endsWith('phasegate.config.json')
35
+ ) {
36
+ return ChangeCategory.fromString('config');
37
+ }
38
+
29
39
  if (isCommentOnlyDiff(file)) {
30
40
  return ChangeCategory.fromString('docs');
31
41
  }
@@ -54,15 +64,6 @@ function categorizeFile(file: ChangedFile): ChangeCategory {
54
64
  return ChangeCategory.fromString('docs');
55
65
  }
56
66
 
57
- // config: *.config.json / *.config.ts / phasegate.config.json
58
- if (
59
- filePath.endsWith('.config.json') ||
60
- filePath.endsWith('.config.ts') ||
61
- filePath.endsWith('phasegate.config.json')
62
- ) {
63
- return ChangeCategory.fromString('config');
64
- }
65
-
66
67
  // feature: domain/ 以外の CREATE
67
68
  if (changeKind === 'CREATE') {
68
69
  return ChangeCategory.fromString('feature');
@@ -481,7 +481,7 @@ export async function initHarnessConfig(
481
481
  layers: {},
482
482
  quickMode: strictWorkflow
483
483
  ? {
484
- allowedCategories: ["chore"],
484
+ allowedCategories: ["bugfix", "docs", "test", "config"],
485
485
  relaxedGates: [],
486
486
  }
487
487
  : {},
@@ -13,7 +13,7 @@ phasegate を導入した直後の config は単純な default で、実プロ
13
13
 
14
14
  ## 設計原則
15
15
 
16
- 1. **silent 書き換え禁止** — 提案は diff として提示し、`AskUserQuestion` で承認を取ってから Edit
16
+ 1. **silent 書き換え禁止** — 提案は diff として提示し、`AskUserQuestion` で承認を取ってから適用する。`config:plan` で表現できる変更は managed command を優先し、直接 `Edit` は hook が許可する範囲に限定する
17
17
  2. **検出結果を優先** — 機械的に決定可能な部分 (workspace 構造、formatter、bash 互換性) は AI 推論ではなく検出結果を採用
18
18
  3. **AI 推論は判断要素のみ** — architecture preset 選定、relaxedGates 推奨値などは AI が判断するが根拠を必ず示す
19
19
  4. **schema は enum 違反確認時のみ Read** — 日常診断は本 SKILL 内の判定基準で十分。schema 全文 Read は値域不明時に限定する
@@ -200,7 +200,14 @@ options:
200
200
  - label: "適用しない (情報のみ受け取る)"
201
201
  ```
202
202
 
203
- ユーザーが適用対象を確定したら `Edit` `phasegate.config.json` を変更。
203
+ ユーザーが適用対象を確定したら、対応する managed intent がある場合は `config:plan` で適用する。
204
+
205
+ ```bash
206
+ npx phasegate config:plan --intent quick-mode-relax --dry-run --json
207
+ npx phasegate config:plan --intent quick-mode-relax --apply --json
208
+ ```
209
+
210
+ managed intent がない個別提案のみ、hook が `config` category を許可していることを確認したうえで `Edit` で `phasegate.config.json` を変更する。
204
211
 
205
212
  **変更後の検証**:
206
213