pan-wizard 3.29.0 → 3.30.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.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: focus-auto
3
3
  group: Focus
4
- description: Continuous scan-plan-exec loop with purpose-driven categories and 5-layer safety harness
4
+ description: Continuous scan-plan-exec loop with purpose-driven categories and a layered safety harness
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Write
@@ -107,7 +107,7 @@ Wait for the user's reply before proceeding. Do not guess or pick a default cate
107
107
  | `--stop` | — | Gracefully stop active run |
108
108
  | `--status` | — | Show current campaign progress |
109
109
  | `--dry-run` | — | Show plan without executing |
110
- | `--deep-review` | off | After every exec cycle, run inline OWASP security check on changed files. Verdict `block` or `review_required` stops the campaign (6th safety harness). Works with all categories. |
110
+ | `--deep-review` | off | After every exec cycle, run inline OWASP security check on changed files. Verdict `block` or `review_required` stops the campaign (the security-gate layer of the safety harness). Works with all categories. |
111
111
  | `--parallel-research` | off | Fan out the per-item *research* stage via the Workflow tool (read-only agents). No-op fallback to sequential where the host has no Workflow tool. (ADR-0031) |
112
112
  | `--parallel-verify` | off | Fan out the per-item *verify* stage via the Workflow tool (read-only). The implement/exec stage always stays a single agent. (ADR-0031) |
113
113
  | `--clean-seal` | off | After the loop's last item, run one clean build + full verification (commands from `config.json → build`/`verification`) to catch cross-item orphans. (ADR-0031) |
@@ -185,7 +185,7 @@ Phase 2 (each cycle): Scan → Plan → Exec → Commit is strictly sequential w
185
185
  HARD STOP conditions:
186
186
  - Phase 1 fails (tests broken): Do not enter main loop — report and exit
187
187
  - Any cycle: test count drops below baseline after revert → stop campaign, preserve state
188
- - Context drops below 25%: stop campaign cleanly (safety harness 3)
188
+ - Context drops below 25%: stop campaign cleanly (safety harness)
189
189
  </phase_dependencies>
190
190
 
191
191
  ### Phase 2: Main Loop
@@ -413,7 +413,7 @@ Then continue immediately to the next cycle (back to Step 2.1).
413
413
 
414
414
  4. Remove safety tag: `git tag -d focus-auto-baseline 2>/dev/null`
415
415
 
416
- ## 6-Layer Safety Harness
416
+ ## Layered Safety Harness
417
417
 
418
418
  | Layer | Mechanism | Action |
419
419
  |-------|-----------|--------|
@@ -425,7 +425,7 @@ Then continue immediately to the next cycle (back to Step 2.1).
425
425
  | Zero-completed guard | 0 items done in a cycle | Stop — further cycles won't help |
426
426
  | Security gate (`--deep-review`) | Critical/high OWASP pattern in changed files | Revert last commit (critical) or flag for manual review (high), stop campaign |
427
427
 
428
- ## 9 Behavioral Rules
428
+ ## Behavioral Rules
429
429
 
430
430
  1. **Read Before Write** — Read every file before editing. Understand context, callers, invariants.
431
431
  2. **Root Cause** — Fix the actual defect, not symptoms. Trace the code path.
@@ -182,7 +182,7 @@ function readActiveSessionMeta(cwd) {
182
182
  const COMMAND_TAIL_BYTES = 262144;
183
183
 
184
184
  // PAN's own command namespace, as a runtime writes it: `/pan:exec-phase` (Claude Code,
185
- // Gemini) or `/pan-exec-phase` (Codex, OpenCode, Copilot). Only these spawn PAN agents,
185
+ // Gemini) or `/pan-exec-phase` (Codex, OpenCode, Copilot, and Claude Code after --unified-skills). Only these spawn PAN agents,
186
186
  // so only these are attributed — a host UI command (`/model`, `/compact`) and a plain
187
187
  // typed prompt leave the row honestly unattributed instead of borrowing a name.
188
188
  const PAN_COMMAND_RE = /<command-name>\s*\/?pan[:-]([a-z0-9][a-z0-9-]*)\s*<\/command-name>/i;
@@ -39,6 +39,19 @@
39
39
  // per stop chain, so a user who genuinely wants to stop is delayed by exactly
40
40
  // one continuation, never trapped.
41
41
  //
42
+ // Gemini CLI (R29, 2026-09-23): the guard is registered there on AfterAgent,
43
+ // Gemini's end-of-turn event — until this date PAN registered it under Claude's
44
+ // `Stop` key, which Gemini skips with an "Invalid hook event name" warning, so it
45
+ // had never run. AfterAgent honours the same {decision: 'block', reason} output
46
+ // (a block re-prompts the agent with the reason), but its stop_hook_active is only
47
+ // true for the AfterAgent that evaluates a continuation the block started
48
+ // directly: a continuation that used tools reports false again (gemini-cli
49
+ // client.ts / useGeminiStream.ts, read 2026-09-23). The flag alone would let the
50
+ // guard block every such turn. So on AfterAgent the one-shot promise is kept with
51
+ // a marker per session, project and target phase in the per-user 0700 hook
52
+ // directory: a second stop aimed at the same phase is allowed. No safe marker
53
+ // directory, or no session id, means no block (fail open, as everywhere else).
54
+ //
42
55
  // Escape hatch: set workflow.stop_guard to false in .planning/config.json to
43
56
  // disable the guard entirely without turning off auto_advance.
44
57
  //
@@ -51,7 +64,9 @@
51
64
  // rather than only reachable via stdin (same pattern as the other PAN hooks).
52
65
 
53
66
  const fs = require('fs');
67
+ const os = require('os');
54
68
  const path = require('path');
69
+ const crypto = require('crypto');
55
70
  /**
56
71
  * Which planning tree this hook acts on.
57
72
  *
@@ -155,6 +170,63 @@ function readIfExists(p) {
155
170
  try { return fs.readFileSync(p, 'utf8'); } catch { return null; }
156
171
  }
157
172
 
173
+ // Per-user hook state directory inside tmpdir, created 0700 — the same directory
174
+ // and the same checks as bridgeDir() in pan-context-monitor.js (hooks are
175
+ // standalone files and cannot require one another). Fail CLOSED (null) when the
176
+ // directory is not provably ours: a shared host must not be able to pre-plant a
177
+ // marker that silences the guard, or a symlink it writes through (M60).
178
+ function hookStateDir() {
179
+ const uid = (typeof process.getuid === 'function' ? process.getuid() : process.env.USERNAME || 'win');
180
+ const dir = path.join(os.tmpdir(), `pan-hooks-${uid}`);
181
+ try {
182
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
183
+ const st = fs.lstatSync(dir);
184
+ if (st.isSymbolicLink()) return null;
185
+ // POSIX-only ownership and mode checks: Windows fakes mode bits (N15).
186
+ if (typeof process.getuid === 'function') {
187
+ if (st.uid !== process.getuid()) return null;
188
+ if ((st.mode & 0o077) !== 0) return null;
189
+ }
190
+ return dir;
191
+ } catch { return null; }
192
+ }
193
+
194
+ /**
195
+ * The file name of the one-shot marker for an AfterAgent block (Gemini CLI).
196
+ * Keyed by session, project and the block's reason — the reason names the target
197
+ * phase, so a later drop at a DIFFERENT phase in the same session is still caught.
198
+ * Pure; null when there is no session id to key on.
199
+ */
200
+ function onceMarkerName(sessionId, projectDir, reason) {
201
+ if (typeof sessionId !== 'string' || !sessionId) return null;
202
+ const key = crypto.createHash('sha256').update(`${sessionId}\0${projectDir}\0${reason}`).digest('hex').slice(0, 32);
203
+ return `stop-guard-${key}.json`;
204
+ }
205
+
206
+ const MARKER_MAX_AGE_MS = 7 * 24 * 3600 * 1000;
207
+
208
+ /**
209
+ * Apply the AfterAgent one-shot rule: returns true when this block may be issued
210
+ * (and records it), false when the same session already got it or no safe place
211
+ * to remember it exists. Best-effort pruning keeps the directory from growing.
212
+ */
213
+ function claimOnceMarker(dir, name, now = Date.now()) {
214
+ if (!dir || !name) return false;
215
+ const marker = path.join(dir, name);
216
+ try {
217
+ for (const f of fs.readdirSync(dir)) {
218
+ if (!/^stop-guard-[0-9a-f]{32}\.json$/.test(f)) continue;
219
+ try { if (now - fs.statSync(path.join(dir, f)).mtimeMs > MARKER_MAX_AGE_MS) fs.unlinkSync(path.join(dir, f)); } catch { /* keep */ }
220
+ }
221
+ } catch { /* unreadable dir — the exclusive create below still decides */ }
222
+ try {
223
+ // 'wx' fails when the marker exists: the create IS the check, so two hook
224
+ // processes racing on the same stop cannot both block.
225
+ fs.writeFileSync(marker, JSON.stringify({ at: new Date(now).toISOString() }), { flag: 'wx', mode: 0o600 });
226
+ return true;
227
+ } catch { return false; }
228
+ }
229
+
158
230
  function main() {
159
231
  let input = '';
160
232
  process.stdin.setEncoding('utf8');
@@ -171,13 +243,20 @@ function main() {
171
243
  let config = null;
172
244
  try { config = JSON.parse(fs.readFileSync(path.join(planningDir, 'config.json'), 'utf8')); } catch { /* no project / bad config -> allow */ }
173
245
 
174
- const decision = buildStopDecision({
246
+ let decision = buildStopDecision({
175
247
  stopHookActive: payload.stop_hook_active === true,
176
248
  config,
177
249
  stateContent: readIfExists(path.join(planningDir, 'state.md')),
178
250
  roadmapContent: readIfExists(path.join(planningDir, 'roadmap.md')),
179
251
  });
180
252
 
253
+ // Gemini CLI's end-of-turn event: its stop_hook_active cannot carry the
254
+ // one-shot promise on its own (see the header), so a marker does.
255
+ if (decision && payload.hook_event_name === 'AfterAgent') {
256
+ const name = onceMarkerName(payload.session_id, projectDir, decision.reason);
257
+ if (!claimOnceMarker(hookStateDir(), name)) decision = null;
258
+ }
259
+
181
260
  if (decision) process.stdout.write(JSON.stringify(decision));
182
261
  } catch { /* fail open — never break a stop */ }
183
262
  process.exit(0);
@@ -188,4 +267,4 @@ if (require.main === module) {
188
267
  main();
189
268
  }
190
269
 
191
- module.exports = { buildStopDecision, UNTICKED_PHASE_RE };
270
+ module.exports = { buildStopDecision, UNTICKED_PHASE_RE, onceMarkerName, claimOnceMarker };
@@ -175,7 +175,7 @@ function finalizeSession(cwd, sid) {
175
175
  const COMMAND_TAIL_BYTES = 262144;
176
176
 
177
177
  // PAN's own command namespace, as a runtime writes it: `/pan:exec-phase` (Claude Code,
178
- // Gemini) or `/pan-exec-phase` (Codex, OpenCode, Copilot). Only these spawn PAN agents,
178
+ // Gemini) or `/pan-exec-phase` (Codex, OpenCode, Copilot, and Claude Code after --unified-skills). Only these spawn PAN agents,
179
179
  // so only these are attributed — a host UI command (`/model`, `/compact`) and a plain
180
180
  // typed prompt leave the row honestly unattributed instead of borrowing a name.
181
181
  const PAN_COMMAND_RE = /<command-name>\s*\/?pan[:-]([a-z0-9][a-z0-9-]*)\s*<\/command-name>/i;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pan-wizard",
3
- "version": "3.29.0",
3
+ "version": "3.30.0",
4
4
  "description": "Command a bot army for your codebase: a reasoning-tier Mission Control delegates whole-project goals to specialist squads and ships behind a human merge gate. Five AI CLIs, zero context rot.",
5
5
  "bin": {
6
6
  "pan-wizard": "bin/install.js"
@@ -2,8 +2,9 @@
2
2
 
3
3
  // ─── AGENTS.md universal rules layer (ADR-0028 Phase 3) ─────────────────────
4
4
  //
5
- // AGENTS.md is the cross-runtime project-instructions standard; every PAN
6
- // target runtime (and Antigravity CLI) reads it natively. PAN contributes one
5
+ // AGENTS.md is the cross-runtime project-instructions standard; Codex,
6
+ // OpenCode and Copilot CLI read it natively, Claude Code only through the @AGENTS.md
7
+ // import in CLAUDE.md; Gemini CLI reads GEMINI.md by default, not this file. PAN contributes one
7
8
  // marker-fenced section so agents in any runtime understand the PAN context
8
9
  // when reading the repo. User content outside the markers is never touched.
9
10
  //
@@ -31,10 +31,23 @@ const { planningPath, planningRel } = require('./utils.cjs');
31
31
  * Each provider maps reasoning/mid/fast to its native model identifiers.
32
32
  * "inherit" means the host runtime uses its own top-tier model selection.
33
33
  */
34
+ //
35
+ // Every non-inherit value must be a model id the host accepts: a Claude Code alias
36
+ // (sonnet, haiku) for Anthropic, otherwise a concrete API id that DEFAULT_RATES in
37
+ // cost.cjs prices exactly (tests/documented-default-models.test.cjs pins both).
38
+ // Until 2026-09-22 the OpenAI row held the literal strings 'mid' and 'fast', so a
39
+ // budget-profile project on Codex or OpenCode was told to spawn a model called
40
+ // "mid" (reality check R28).
41
+ // - openai: Codex's subagent docs (learn.chatgpt.com/docs/agent-configuration/
42
+ // subagents, read 2026-09-23): "start with gpt-6-sol. Use gpt-6-luna when you
43
+ // want a faster, lower-cost option". gpt-6-astra is the flagship above both.
44
+ // - google: the newest stable Flash and Flash-Lite on ai.google.dev's models page
45
+ // (read 2026-09-23). The 2.5 family these rows used is "not deprecated" but
46
+ // limited to users who have used it before, so a new project may not reach it.
34
47
  const PROVIDER_MODELS = {
35
48
  anthropic: { reasoning: 'inherit', mid: 'sonnet', fast: 'haiku' },
36
- openai: { reasoning: 'inherit', mid: 'mid', fast: 'fast' },
37
- google: { reasoning: 'inherit', mid: 'gemini-2.5-flash', fast: 'gemini-2.5-flash-lite' },
49
+ openai: { reasoning: 'inherit', mid: 'gpt-6-sol', fast: 'gpt-6-luna' },
50
+ google: { reasoning: 'inherit', mid: 'gemini-3.8-flash', fast: 'gemini-3.5-flash-lite' },
38
51
  default: { reasoning: 'inherit', mid: 'sonnet', fast: 'haiku' },
39
52
  };
40
53
 
@@ -52,7 +65,7 @@ const COST_MULTIPLIERS = { reasoning: 15, mid: 3, fast: 1 };
52
65
  // isolation (each subagent runs in its own window), not a cheaper model, is what
53
66
  // keeps the main conversation clean. Cheapness is now OPT-IN: choose the `budget`
54
67
  // profile (the only column that still down-tiers) or pin a specific agent via
55
- // config `model_overrides`. The 3 security agents additionally pin `model: opus`
68
+ // config `model_overrides`. The reviewer-class agents additionally pin `model: opus`
56
69
  // in their own frontmatter (a native, deliberate exception). resolve-model /
57
70
  // MODEL_PROFILES is advisory + cost-estimation; native Claude Code delegation
58
71
  // reads each agent file's static `model:` (unset → inherit).
@@ -781,7 +794,7 @@ function adjustTierForCapabilities(tier, opts) {
781
794
  * @param {string} agentType - Agent name (e.g., "pan-planner", "pan-executor")
782
795
  * @param {Object} [taskMetadata] - Optional metadata. Supports complexity fields and
783
796
  * capability hints: {context_estimate, needs_thinking, cache_warm}.
784
- * @returns {string} Model identifier: "inherit", "sonnet", "haiku", "mid", "fast", etc.
797
+ * @returns {string} Model identifier: "inherit", a Claude Code alias ("sonnet", "haiku"), or a provider model id from PROVIDER_MODELS
785
798
  */
786
799
  function resolveModelInternal(cwd, agentType, taskMetadata) {
787
800
  const config = loadConfig(cwd);
@@ -30,8 +30,9 @@
30
30
  * - hit rate: cache_read / (cache_read + input - cache_write) if any cache activity
31
31
  *
32
32
  * Rate table is approximate — real pricing comes from the provider's API.
33
- * Rates are US dollars per million tokens, indicative as of 2026-08. Users
34
- * can override with `.planning/config.json` → `cost.rates`.
33
+ * Rates are US dollars per million tokens, verified against each provider's
34
+ * pricing page on the date in RATES_VERIFIED_AT (below). Users can override with
35
+ * `.planning/config.json` → `cost.rates`.
35
36
  */
36
37
 
37
38
  const fs = require('fs');
@@ -47,63 +48,109 @@ const TOKENS_FILE = 'tokens.jsonl';
47
48
  * Override per-model in config.json → cost.rates.
48
49
  */
49
50
  const DEFAULT_RATES = {
50
- // Anthropic — verified against platform pricing 2026-09-10. Opus 4.6+/Opus 5 are
51
- // $5/$25 (the old $15/$75 Opus pricing ended with the 4.5 generation). Cache
52
- // rates follow Anthropic's convention: read ≈ 0.1× input, write ≈ 1.25× input —
53
- // EXCEPT Fable 5.1, whose cache reads bill at 0.025× input ($0.25). Fable 5.1
54
- // needs its own row: without it the family-prefix fallback priced its reads at
55
- // the Fable 5 rate, 4× too high on the model the `fable`/`best` aliases resolve to
56
- // (model-config, read 2026-09-10: neither Fable model is any plan's default), and
57
- // cached re-reads are the bulk of PAN's traffic (ADR-0044).
51
+ // Anthropic — platform.claude.com/docs/en/about-claude/pricing, every row read
52
+ // 2026-09-23. Opus 4.6+/Opus 5 are $5/$25 (the old $15/$75 Opus pricing ended with
53
+ // the 4.5 generation). cache_write is the 5-minute write rate (1.25× input; the
54
+ // 1-hour write bills 2×, which the ledger does not split yet). Cache reads follow
55
+ // the 0.1× convention EXCEPT on three models the page footnotes: Fable 5.1 and
56
+ // Mythos 5.1 at 0.025× input, Opus 5.5 at 0.05×. Each needs its own row, because
57
+ // the family-prefix fallback would otherwise price it at its predecessor's reads.
58
+ // Fable 5.1 is what the `fable`/`best` aliases resolve to (model-config: neither
59
+ // Fable model is any plan's default); cached re-reads are the bulk of PAN's
60
+ // traffic (ADR-0044).
58
61
  'claude-fable-5-1': { input: 10.0, output: 50.0, cache_read: 0.25, cache_write: 12.5 },
59
62
  'claude-fable-5': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5 },
60
- // Mythos 5.1 / Mythos 5 (limited availability) — platform.claude.com/docs/en/about-claude/pricing,
61
- // read 2026-09-10: $10/$50; the page's cache footnote names Fable 5.1 AND Mythos 5.1 as
62
- // the two models whose cache reads bill at 0.025× input; Mythos 5 follows the 0.1× rule.
63
- // Added for reality check R7: resolveRate returned null for both ids.
63
+ // Mythos 5.1 / Mythos 5 (limited availability): $10/$50; Mythos 5.1 reads at
64
+ // 0.025× input like Fable 5.1, Mythos 5 follows the 0.1× rule (R7).
64
65
  'claude-mythos-5-1': { input: 10.0, output: 50.0, cache_read: 0.25, cache_write: 12.5 },
65
66
  'claude-mythos-5': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5 },
67
+ // Opus 5.5 (claude-opus-5-5, released 2026-09-22): $4 input / $20 output, 5-minute
68
+ // writes $5, cache hits $0.20 — the page prices its hits at 0.05× input. Claude
69
+ // Code 2.1.280 made it the default model on every plan (model-config, read
70
+ // 2026-09-23), so PAN's inherit tier now runs on it. Without this row the family
71
+ // prefix priced it as Opus 5: cache reads 2.5× high, the rest a quarter high
72
+ // (reality check 2026-09-22, R25). The models overview lists no dated snapshot —
73
+ // from the 4.6 generation on, the dateless id is canonical.
74
+ 'claude-opus-5-5': { input: 4.0, output: 20.0, cache_read: 0.20, cache_write: 5.0 },
66
75
  'claude-opus-5': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
67
76
  'claude-opus-4-8': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
68
77
  'claude-opus-4-7': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
69
78
  'claude-opus-4-6': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
70
- // Opus 4.5 (dated id claude-opus-4-5-20251101) — same pricing page, read 2026-09-10:
71
- // $5/$25/$0.50/$6.25. Without this row the dated id had no family prefix to land on
72
- // and priced as null (R7).
79
+ // Opus 4.5 (dated id claude-opus-4-5-20251101): without this row the dated id had
80
+ // no family prefix to land on and priced as null (R7).
73
81
  'claude-opus-4-5': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
74
82
  // Sonnet 5 is $2/$10: the launch price announced as introductory through
75
- // 2026-08-31 was made permanent and the scheduled rise to $3/$15 cancelled
76
- // (pricing page, read 2026-09-10). Lesson: never write down a pre-announced
77
- // price — this row carried the future rate for a month and over-billed by half.
83
+ // 2026-08-31 was made permanent and the scheduled rise to $3/$15 cancelled.
84
+ // Lesson: never write down a pre-announced price — this row carried the future
85
+ // rate for a month and over-billed by half.
78
86
  'claude-sonnet-5': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50 },
79
87
  'claude-sonnet-4-6': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75 },
80
- // Sonnet 4.5 (dated id claude-sonnet-4-5-20250929) — pricing page, read 2026-09-10:
81
- // $3/$15/$0.30/$3.75 (the pre-Sonnet-5 rate; Sonnet 5 is $2/$10). R7.
88
+ // Sonnet 4.5 (dated id claude-sonnet-4-5-20250929): $3/$15/$0.30/$3.75 (R7).
82
89
  'claude-sonnet-4-5': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75 },
90
+ // Haiku 4.5: API id claude-haiku-4-5-20251001, alias claude-haiku-4-5.
83
91
  'claude-haiku-4-5': { input: 1.0, output: 5.0, cache_read: 0.1, cache_write: 1.25 },
84
92
 
85
- // OpenAI — verified against published pricing 2026-08. Prompt caching is a 90%
86
- // input discount with no separate write charge, so cache_write bills at the
87
- // plain input rate. GPT-5.6 ships in three tiers; the bare `gpt-5.6` id prices
88
- // as the Sol flagship, with tier-specific keys for Terra/Luna (longest-prefix
89
- // match wins in resolveRate). Luna reflects the 2026-07-30 price cut.
90
- 'gpt-5.6': { input: 5.0, output: 30.0, cache_read: 0.5, cache_write: 5.0 },
91
- 'gpt-5.6-terra': { input: 2.0, output: 12.0, cache_read: 0.2, cache_write: 2.0 },
92
- 'gpt-5.6-luna': { input: 0.20, output: 1.20, cache_read: 0.02, cache_write: 0.20 },
93
+ // OpenAI — developers.openai.com/api/docs/pricing, Standard tier, short context
94
+ // (≤272K input tokens), read 2026-09-23. On the GPT-5.6 and GPT-6 families a cache
95
+ // write bills at 1.25× input IN PLACE of the input rate ("writes are not an
96
+ // additive fee"); GPT-5.5 charges no cache write ("-"), so its write tokens bill
97
+ // as plain input. Until 2026-09-22 this block said "no separate write charge" for
98
+ // every row, which had stopped being true — Terra and Luna writes were a fifth
99
+ // low (R27). Longest-prefix match lets each tier key win over a shorter one.
100
+ // GPT-6: Astra is the flagship; Sol and Luna were released 2026-09-22. Codex's
101
+ // config docs use gpt-6-sol as the default model and gpt-6-luna for lower-cost
102
+ // subagents (learn.chatgpt.com/docs/config-file/config-basic, /agent-configuration/subagents).
103
+ 'gpt-6-astra': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5 },
104
+ 'gpt-6-sol': { input: 2.0, output: 10.0, cache_read: 0.2, cache_write: 2.5 },
105
+ 'gpt-6-luna': { input: 0.10, output: 0.50, cache_read: 0.01, cache_write: 0.125 },
106
+ // GPT-5.6. The bare `gpt-5.6` id is an alias that "routes requests to GPT-5.6 Sol"
107
+ // (its model page, read 2026-09-23), so it carries Sol's row — not the $5/$30 it
108
+ // carried before, which no page listed. Sol's price is promotional "at least
109
+ // through November 21, 2026": re-read it after that date.
110
+ 'gpt-5.6': { input: 4.0, output: 20.0, cache_read: 0.4, cache_write: 5.0 },
111
+ 'gpt-5.6-sol': { input: 4.0, output: 20.0, cache_read: 0.4, cache_write: 5.0 },
112
+ 'gpt-5.6-terra': { input: 2.0, output: 12.0, cache_read: 0.2, cache_write: 2.5 },
113
+ 'gpt-5.6-luna': { input: 0.20, output: 1.20, cache_read: 0.02, cache_write: 0.25 },
114
+ // Priced only in the page's separate "Cyber models" table, not in the Standard tiers.
115
+ 'gpt-5.6-cyber': { input: 12.5, output: 75.0, cache_read: 1.25, cache_write: 15.625 },
93
116
  'gpt-5.5': { input: 5.0, output: 30.0, cache_read: 0.5, cache_write: 5.0 },
94
117
 
95
- // Google Gemini — published rates (per million tokens, approximate; users can override via config.json → cost.rates).
96
- // Pro tiers use the <=200K-context tier; long-context calls may be billed at ~2x. Cache rates are Google's context-cache pricing (~25% of input rate).
118
+ // Google Gemini — ai.google.dev/gemini-api/docs/pricing, paid tier, text input,
119
+ // the ≤200K-token prompt tier where the page splits by length, read 2026-09-23.
120
+ // cache_read is the page's "context caching" price: 0.1× input on every row, not
121
+ // the 0.25× this block carried until 2026-09-22 (cache reads 2.5× high, R26). The
122
+ // tokens that CREATE a cache bill at the standard input price (Google Cloud's
123
+ // context-cache overview; the Gemini API pages list only cached-token and storage
124
+ // charges), so cache_write = input. Cache STORAGE bills per million tokens per
125
+ // HOUR, which a per-token table cannot carry: PAN's Gemini totals exclude it.
126
+ // The 3.6/3.7/3.8 Flash prices run "through December 31, 2026" and double from
127
+ // January 1, 2027 (the page states both) — re-read them after that date.
128
+ 'gemini-3.8-flash': { input: 0.75, output: 3.75, cache_read: 0.075, cache_write: 0.75 },
129
+ 'gemini-3.7-flash': { input: 0.75, output: 3.75, cache_read: 0.075, cache_write: 0.75 },
130
+ 'gemini-3.6-flash': { input: 0.75, output: 3.75, cache_read: 0.075, cache_write: 0.75 },
131
+ 'gemini-3.5-flash': { input: 1.50, output: 9.00, cache_read: 0.15, cache_write: 1.50 },
132
+ 'gemini-3.5-flash-lite': { input: 0.30, output: 2.50, cache_read: 0.03, cache_write: 0.30 },
133
+ // Shutdown announced for 2027-05-07 (deprecations page); replacement gemini-3.5-flash-lite.
134
+ 'gemini-3.1-flash-lite': { input: 0.25, output: 1.50, cache_read: 0.025, cache_write: 0.25 },
135
+ 'gemini-3-flash-preview': { input: 0.50, output: 3.00, cache_read: 0.05, cache_write: 0.50 },
136
+ // The page lists 3.1 Pro only as gemini-3.1-pro-preview; the bare id keeps the
137
+ // preview's row so records written under it stay priced.
138
+ 'gemini-3.1-pro': { input: 2.00, output: 12.0, cache_read: 0.20, cache_write: 2.00 },
139
+ 'gemini-3.1-pro-preview': { input: 2.00, output: 12.0, cache_read: 0.20, cache_write: 2.00 },
140
+ // The 2.5 family is "not deprecated", but the models page limits access to users
141
+ // who have used it before — new projects cannot count on it (PAN's Google routing
142
+ // tiers moved off it, R28). Rows kept for the ledgers that recorded it.
143
+ 'gemini-2.5-pro': { input: 1.25, output: 10.0, cache_read: 0.125, cache_write: 1.25 },
144
+ 'gemini-2.5-flash': { input: 0.30, output: 2.50, cache_read: 0.03, cache_write: 0.30 },
145
+ 'gemini-2.5-flash-lite': { input: 0.10, output: 0.40, cache_read: 0.01, cache_write: 0.10 },
97
146
  // (gemini-1.5-pro removed 2026-06: retired model; records for it fall back to tier rates.)
98
- 'gemini-3.1-pro': { input: 2.00, output: 12.0, cache_read: 0.50, cache_write: 2.00 },
99
- 'gemini-3.1-pro-preview': { input: 2.00, output: 12.0, cache_read: 0.50, cache_write: 2.00 },
100
- 'gemini-2.5-pro': { input: 1.25, output: 10.0, cache_read: 0.3125, cache_write: 1.25 },
101
- 'gemini-2.5-flash': { input: 0.30, output: 2.50, cache_read: 0.075, cache_write: 0.30 },
102
- 'gemini-2.5-flash-lite': { input: 0.10, output: 0.40, cache_read: 0.025, cache_write: 0.10 },
103
-
104
- // Tier fallbacks when model id is unknown (reasoning tracks current Opus pricing)
105
- 'reasoning': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
106
- 'mid': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75 },
147
+
148
+ // Tier fallbacks when the model id is unknown: reasoning tracks the default Opus
149
+ // (Opus 5.5 since 2026-09-22 — the model the inherit tier runs on for most users),
150
+ // mid the Sonnet the `sonnet` alias resolves to on the Anthropic API (Sonnet 5),
151
+ // fast Haiku 4.5.
152
+ 'reasoning': { input: 4.0, output: 20.0, cache_read: 0.20, cache_write: 5.0 },
153
+ 'mid': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50 },
107
154
  'fast': { input: 1.0, output: 5.0, cache_read: 0.1, cache_write: 1.25 },
108
155
  };
109
156
 
@@ -587,12 +634,22 @@ function cmdCostClear(cwd, raw) {
587
634
 
588
635
  // ─── Rate-table staleness ───────────────────────────────────────────────────
589
636
 
590
- // Date DEFAULT_RATES was last verified against published provider pricing.
591
- // Bump this whenever the table is re-verified; `models check` flags the table
592
- // once it is older than RATES_STALE_AFTER_DAYS (provider prices move faster
593
- // than PAN releases do).
594
- const RATES_VERIFIED_AT = '2026-09-10';
595
- const RATES_STALE_AFTER_DAYS = 180;
637
+ // Date DEFAULT_RATES was last verified against published provider pricing — ALL
638
+ // providers' pricing pages, not only Anthropic's (the 2026-09-10 bump covered the
639
+ // Anthropic rows alone, and the Gemini and OpenAI rows it vouched for had never
640
+ // matched their pages: reality check 2026-09-22, RC29). Bump it only when every
641
+ // row has been read against its page that day.
642
+ //
643
+ // `models check` flags the table once it is older than RATES_STALE_AFTER_DAYS.
644
+ // That window was half a year until 2026-09-22 (R38), which is longer than the
645
+ // interval at which the lineup now moves: the default Claude model changed on
646
+ // every plan inside twelve days of the last verification. Sixty days is the
647
+ // review cadence the ecosystem reviews settled on. The calendar still cannot see
648
+ // a default-model change inside the window — that is the job of the
649
+ // documented-default-ids fixture test (tests/fixtures/documented-default-models.json,
650
+ // R31), which fails the suite the day a documented default id has no exact row.
651
+ const RATES_VERIFIED_AT = '2026-09-23';
652
+ const RATES_STALE_AFTER_DAYS = 60;
596
653
  const RATE_TIERS = ['reasoning', 'mid', 'fast'];
597
654
 
598
655
  function checkRatesStaleness(now = new Date()) {
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * Three derived targets:
12
12
  * 1. AGENTS.md — the universal, cross-runtime tools memory. PAN owns exactly
13
- * the marker-fenced `<!-- BEGIN/END PAN WIZARD -->` section (every runtime,
14
- * including Copilot/.github, reads AGENTS.md natively). User content
13
+ * the marker-fenced `<!-- BEGIN/END PAN WIZARD -->` section (Codex, Copilot and
14
+ * OpenCode read AGENTS.md natively; Claude via the CLAUDE.md bridge; Gemini reads GEMINI.md by default). User content
15
15
  * outside the markers is preserved byte-for-byte.
16
16
  * 2. CLAUDE.md — the Claude bridge (`@AGENTS.md` import), regenerated only
17
17
  * when the Claude runtime is installed here.
@@ -126,7 +126,7 @@ function cmdMemoryRebuild(cwd, opts = {}, raw) {
126
126
  const targets = [];
127
127
  const warnings = [];
128
128
 
129
- // 1. AGENTS.md — universal PAN section (all runtimes read it natively).
129
+ // 1. AGENTS.md — universal PAN section (Claude Code reads it through the bridge in step 2).
130
130
  {
131
131
  const p = path.join(cwd, 'AGENTS.md');
132
132
  const existing = safeReadFile(p);
@@ -107,7 +107,7 @@ function validateRuntimeInstall(cwd, configDir, runtime) {
107
107
  }
108
108
  }
109
109
  }
110
- // Copilot/Gemini statusLine
110
+ // statusLine (Claude, Copilot; PAN writes none for Gemini since 2026-09-23)
111
111
  if (settings.statusLine && settings.statusLine.command) {
112
112
  hookCommands.push(settings.statusLine.command);
113
113
  }
@@ -11,8 +11,8 @@ PAN uses three abstract tiers instead of hardcoded model names:
11
11
  | Tier | Purpose | Anthropic | OpenAI | Google |
12
12
  |------|---------|-----------|--------|--------|
13
13
  | `reasoning` | Architecture, planning, complex decisions | inherit (your session's top-tier model) | inherit | inherit |
14
- | `mid` | Execution, research, verification | Sonnet | mid | gemini-2.5-flash |
15
- | `fast` | Read-only extraction, budget tasks | Haiku | fast | gemini-2.5-flash-lite |
14
+ | `mid` | Execution, research, verification | Sonnet | gpt-6-sol | gemini-3.8-flash |
15
+ | `fast` | Read-only extraction, budget tasks | Haiku | gpt-6-luna | gemini-3.5-flash-lite |
16
16
 
17
17
  **Why `inherit` for reasoning?** Host runtimes map "opus" to a specific model version. PAN returns `inherit` for reasoning-tier agents, so they use whatever top-tier model the user has configured. This avoids version conflicts and silent fallbacks.
18
18
 
@@ -39,7 +39,7 @@ At install time PAN also runs a **best-effort, advisory** capability check on th
39
39
  | Class | Example model IDs | Role in PAN | Context | Relative cost | Notes |
40
40
  |-------|-------------------|-------------|---------|---------------|-------|
41
41
  | Fable / Mythos | `claude-fable-5` | **Recommended flagship** — deepest long-horizon reasoning; best for the bot army's Mission Control + planning | 1M | ~2× Opus | Runs input safety classifiers (see caveat below); requires 30-day data retention |
42
- | Opus | `claude-opus-5`, `claude-opus-4-8` | **Cost-conscious pick** — same 1M context + thinking, about half the cost, no cyber classifier | 1M | 1× | The safe pick when you want Opus behavior without Fable's refusal surface |
42
+ | Opus | `claude-opus-5-5`, `claude-opus-5`, `claude-opus-4-8` | **Cost-conscious pick** — same 1M context + thinking, about half the cost, no cyber classifier | 1M | 1× | The safe pick when you want Opus behavior without Fable's refusal surface |
43
43
 
44
44
  **Why the Fable class is the recommended flagship.** It is Anthropic's deepest class for demanding, long-horizon agentic work — exactly what PAN's hierarchical bot army (Mission Control → squads → workers) asks of its reasoning tier. Select the current release in that class in your host runtime and `inherit` routes the reasoning-tier agents to it automatically.
45
45
 
@@ -117,7 +117,7 @@ PAN auto-detects the LLM provider to map tiers to the right model names:
117
117
 
118
118
  1. **Explicit config** — `routing.provider` in config.json (if not `"auto"`)
119
119
  2. **Environment variable** — `PAN_PROVIDER` env var
120
- 3. **Runtime directory** — `.claude/` → Anthropic, `.codex/` → OpenAI, `.gemini/` → Google
120
+ 3. **Runtime directory** — `.claude/` → Anthropic, `.codex/` → OpenAI, `.gemini/` → Google, `.opencode/` → OpenAI, `.github/` → default (first match wins)
121
121
  4. **Fallback** — Default provider map (Anthropic-style names)
122
122
 
123
123
  ---
@@ -4,24 +4,22 @@ Configuration options for `.planning/` directory behavior.
4
4
 
5
5
  <config_schema>
6
6
  ```json
7
- "planning": {
8
- "commit_docs": true,
9
- "search_gitignored": false
10
- },
11
- "git": {
12
- "branching_strategy": "none",
13
- "phase_branch_template": "pan/phase-{phase}-{slug}",
14
- "milestone_branch_template": "pan/{milestone}-{slug}"
15
- }
7
+ "commit_docs": true,
8
+ "search_gitignored": false,
9
+ "branching_strategy": "none",
10
+ "phase_branch_template": "pan/phase-{phase}-{slug}",
11
+ "milestone_branch_template": "pan/{milestone}-{slug}"
16
12
  ```
17
13
 
18
14
  | Option | Default | Description |
19
15
  |--------|---------|-------------|
20
16
  | `commit_docs` | `true` | Whether to commit planning artifacts to git |
21
17
  | `search_gitignored` | `false` | Add `--no-ignore` to broad rg searches |
22
- | `git.branching_strategy` | `"none"` | Git branching approach: `"none"`, `"phase"`, or `"milestone"` |
23
- | `git.phase_branch_template` | `"pan/phase-{phase}-{slug}"` | Branch template for phase strategy |
24
- | `git.milestone_branch_template` | `"pan/{milestone}-{slug}"` | Branch template for milestone strategy |
18
+ | `branching_strategy` | `"none"` | Git branching approach: `"none"`, `"phase"`, or `"milestone"` |
19
+ | `phase_branch_template` | `"pan/phase-{phase}-{slug}"` | Branch template for phase strategy |
20
+ | `milestone_branch_template` | `"pan/{milestone}-{slug}"` | Branch template for milestone strategy |
21
+
22
+ These keys are top-level. The nested `planning.*` / `git.*` forms older versions wrote are read only when the top-level key is absent. A config `config-ensure-section` creates carries every top-level key, and one `/pan:new-project` writes carries a top-level `commit_docs` — so write the top-level key; a nested one is ignored whenever the top-level key exists.
25
23
  </config_schema>
26
24
 
27
25
  <commit_docs_behavior>
@@ -84,10 +82,8 @@ To use uncommitted mode:
84
82
 
85
83
  1. **Set config:**
86
84
  ```json
87
- "planning": {
88
- "commit_docs": false,
89
- "search_gitignored": true
90
- }
85
+ "commit_docs": false,
86
+ "search_gitignored": true
91
87
  ```
92
88
 
93
89
  2. **Add to .gitignore:**
@@ -101,7 +97,7 @@ To use uncommitted mode:
101
97
  git commit -m "chore: stop tracking planning docs"
102
98
  ```
103
99
 
104
- 4. **Branch merges:** When using `branching_strategy: phase` or `milestone`, the `milestone-done` workflow automatically strips `.planning/` files from staging before merge commits when `commit_docs: false`.
100
+ 4. **Branch merges:** PAN never merges branches; when you merge a phase or milestone branch yourself with `commit_docs: false`, keep `.planning/` out of the merge commit.
105
101
 
106
102
  </setup_uncommitted_mode>
107
103
 
@@ -113,24 +109,24 @@ To use uncommitted mode:
113
109
  |----------|---------------------|--------------|-------------|
114
110
  | `none` | Never | N/A | N/A |
115
111
  | `phase` | At `execute-phase` start | Single phase | User merges after phase |
116
- | `milestone` | At first `execute-phase` of milestone | Entire milestone | At `milestone-done` |
112
+ | `milestone` | At first `execute-phase` of milestone | Entire milestone | User merges after milestone |
117
113
 
118
- **When `git.branching_strategy: "none"` (default):**
114
+ **When `branching_strategy: "none"` (default):**
119
115
  - All work commits to current branch
120
116
  - Standard PAN behavior
121
117
 
122
- **When `git.branching_strategy: "phase"`:**
118
+ **When `branching_strategy: "phase"`:**
123
119
  - `execute-phase` creates/switches to a branch before execution
124
120
  - Branch name from `phase_branch_template` (e.g., `pan/phase-03-authentication`)
125
121
  - All plan commits go to that branch
126
122
  - User merges branches manually after phase completion
127
- - `milestone-done` offers to merge all phase branches
123
+ - `milestone-done` does not merge — merge the phase branches yourself
128
124
 
129
- **When `git.branching_strategy: "milestone"`:**
125
+ **When `branching_strategy: "milestone"`:**
130
126
  - First `execute-phase` of milestone creates the milestone branch
131
127
  - Branch name from `milestone_branch_template` (e.g., `pan/v1.0-mvp`)
132
128
  - All phases in milestone commit to same branch
133
- - `milestone-done` offers to merge milestone branch to main
129
+ - `milestone-done` archives and tags but does not merge — merge the milestone branch yourself
134
130
 
135
131
  **Template variables:**
136
132
 
@@ -31,7 +31,7 @@ Parse current values (default to `true` if not present):
31
31
  - `workflow.nyquist_validation` — validation architecture research during plan-phase
32
32
  - `model_profile` — which model each agent uses (default: `balanced`)
33
33
  - `routing.strategy` — how model tiers are adjusted at runtime (default: `static`)
34
- - `git.branching_strategy` — branching approach (default: `"none"`)
34
+ - `branching_strategy` — branching approach (default: `"none"`; a top-level key — a nested `git.branching_strategy` is ignored when the top-level key exists, as it does in every config `config-ensure-section` creates — write the top-level key)
35
35
  </step>
36
36
 
37
37
  <step name="present_settings">
@@ -131,9 +131,7 @@ Merge new settings into existing config.json:
131
131
  "auto_advance": true/false,
132
132
  "nyquist_validation": true/false
133
133
  },
134
- "git": {
135
- "branching_strategy": "none" | "phase" | "milestone"
136
- },
134
+ "branching_strategy": "none" | "phase" | "milestone",
137
135
  "routing": {
138
136
  "strategy": "static" | "complexity"
139
137
  }