pi-orchestrator-config 4.2.0 → 4.3.2

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 (100) hide show
  1. package/README.md +76 -67
  2. package/agents/code-reviewer-docs.md +11 -1
  3. package/agents/code-reviewer-guidelines.md +11 -1
  4. package/agents/code-reviewer-quality.md +11 -1
  5. package/agents/code-reviewer-security.md +11 -1
  6. package/agents/code-reviewer-spec.md +11 -1
  7. package/agents/git-expert.md +42 -3
  8. package/agents/github-expert.md +11 -0
  9. package/extensions/acpx-provider/runtime-models.ts +18 -6
  10. package/extensions/cli-provider/agents/claude.ts +3 -2
  11. package/extensions/cli-provider/agents/cursor.ts +3 -5
  12. package/extensions/cli-provider/agents/gemini.ts +3 -2
  13. package/extensions/cli-provider/discover.ts +7 -7
  14. package/extensions/cli-provider/index.ts +5 -0
  15. package/extensions/cli-provider/runtime-models.ts +18 -6
  16. package/extensions/cli-provider/session-reaper.ts +5 -7
  17. package/extensions/cli-provider/sessions.ts +4 -0
  18. package/extensions/cli-provider/shared/discover-cache.ts +17 -176
  19. package/extensions/coms/coms-p2p.ts +1719 -423
  20. package/extensions/coms/coms-shared.ts +261 -39
  21. package/extensions/coms/coms-wrapper.ts +100 -62
  22. package/extensions/coms/index.ts +8 -4
  23. package/extensions/coms/mixed-turn.ts +26 -0
  24. package/extensions/coms/probe-socket.ts +42 -0
  25. package/extensions/coms/themeMap.ts +0 -1
  26. package/extensions/image-gen/image-gen.ts +19 -1
  27. package/extensions/orchestrator/async-agents.ts +563 -102
  28. package/extensions/orchestrator/async-capability.ts +8 -8
  29. package/extensions/orchestrator/cron.ts +15 -9
  30. package/extensions/orchestrator/dreaming.ts +32 -25
  31. package/extensions/orchestrator/enforcement-helpers.ts +459 -17
  32. package/extensions/orchestrator/enforcement-rules.ts +5 -4
  33. package/extensions/orchestrator/enforcement.ts +80 -27
  34. package/extensions/orchestrator/extended-autocomplete.ts +7 -0
  35. package/extensions/orchestrator/git-helpers.ts +2 -2
  36. package/extensions/orchestrator/index.ts +78 -10
  37. package/extensions/orchestrator/markdown-transformer.ts +313 -0
  38. package/extensions/orchestrator/memory-embeddings.ts +36 -34
  39. package/extensions/orchestrator/memory-provenance.ts +29 -25
  40. package/extensions/orchestrator/memory-scoring.ts +23 -18
  41. package/extensions/orchestrator/memory-tools.ts +10 -3
  42. package/extensions/orchestrator/overlay-dashboard.ts +17 -5
  43. package/extensions/orchestrator/parse-model-override.ts +29 -0
  44. package/extensions/orchestrator/pi-config-review-state.ts +57 -33
  45. package/extensions/orchestrator/project-settings.ts +333 -127
  46. package/extensions/orchestrator/resolve-agent-model.ts +24 -13
  47. package/extensions/orchestrator/review-porcelain.ts +4 -0
  48. package/extensions/orchestrator/review-ui.ts +63 -15
  49. package/extensions/orchestrator/rule-placeholders.ts +411 -2
  50. package/extensions/orchestrator/rules.ts +80 -132
  51. package/extensions/orchestrator/settings-tui-helpers.ts +380 -0
  52. package/extensions/orchestrator/settings-tui-submenus.ts +647 -0
  53. package/extensions/orchestrator/settings-tui.ts +636 -0
  54. package/extensions/orchestrator/state-jsonl.ts +431 -0
  55. package/extensions/orchestrator/subagent-tool.ts +157 -59
  56. package/extensions/orchestrator/sync-limit.ts +23 -0
  57. package/extensions/orchestrator/task-lifecycle.ts +77 -67
  58. package/extensions/orchestrator/utils.ts +8 -5
  59. package/extensions/pidash/pidash.ts +193 -156
  60. package/extensions/pidiff/pidiff.ts +96 -38
  61. package/extensions/pidiff/spawn-lock.ts +62 -0
  62. package/extensions/pitasks/index.ts +430 -0
  63. package/extensions/pitasks/reminders.ts +37 -0
  64. package/extensions/pitasks/task-store.ts +467 -0
  65. package/extensions/pitasks/task-tools.ts +375 -0
  66. package/extensions/pitasks/task-widget.ts +181 -0
  67. package/extensions/pitasks/types.ts +38 -0
  68. package/extensions/providers/acpx-driver.ts +12 -2
  69. package/extensions/providers/built-in-drivers.ts +6 -6
  70. package/extensions/providers/claude-driver.ts +1 -1
  71. package/extensions/providers/cursor-cli-driver.ts +1 -1
  72. package/extensions/providers/gemini-driver.ts +1 -1
  73. package/extensions/providers/index.ts +144 -26
  74. package/extensions/providers/initialized-guard.ts +27 -0
  75. package/extensions/providers/restore-default-model.ts +596 -0
  76. package/extensions/providers/session-shutdown.ts +39 -0
  77. package/extensions/shared/coms-active.ts +23 -0
  78. package/extensions/shared/create-runtime-provider.ts +2 -0
  79. package/extensions/shared/daemon-manager.ts +0 -8
  80. package/extensions/shared/file-logger.ts +132 -25
  81. package/extensions/shared/logger.ts +50 -0
  82. package/extensions/shared/models-dev.ts +421 -0
  83. package/extensions/shared/oneshot.ts +116 -0
  84. package/extensions/shared/resolve-binary.ts +191 -0
  85. package/extensions/shared/types.ts +3 -0
  86. package/package.json +9 -3
  87. package/prompts/coderabbit-rate-limit.md +2 -0
  88. package/rules/00-orchestrator-core.md +3 -0
  89. package/rules/10-agent-routing.md +12 -1
  90. package/rules/20-code-review-loop.md +52 -25
  91. package/rules/30-prompt-templates.md +4 -0
  92. package/rules/35-memory.md +3 -3
  93. package/rules/40-critical-rules.md +3 -0
  94. package/rules/55-coms-protocol.md +84 -18
  95. package/rules/60-task-tracking.md +2 -0
  96. package/settings-keys.json +563 -0
  97. package/templates/coms-feature-manager-prompt.md +4 -6
  98. package/extensions/coms/coms-net-server.ts +0 -1642
  99. package/extensions/coms/coms-net-wrapper.ts +0 -521
  100. package/extensions/coms/coms-net.ts +0 -1589
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Oneshot argv helpers — skip session extras so `pi -p` / `--mode json` can exit.
3
+ *
4
+ * Lives in shared/ so coms/pidash/pidiff/pitasks can import without the
5
+ * orchestrator utils module. Keep ONESHOT_VALUE_FLAGS in sync with pi
6
+ * `parseArgs` (`@earendil-works/pi-coding-agent` `dist/cli/args.js`).
7
+ */
8
+
9
+ import { createLogger } from "./logger.js";
10
+
11
+ const log = createLogger("oneshot");
12
+
13
+ /**
14
+ * Value-taking flags from pi `parseArgs`. `--mode` is handled separately.
15
+ * Next token is always a value, even if it starts with `-` (including `--`).
16
+ */
17
+ const ONESHOT_VALUE_FLAGS = new Set([
18
+ "--model",
19
+ "--models",
20
+ "--provider",
21
+ "--api-key",
22
+ "--system-prompt",
23
+ "--append-system-prompt",
24
+ "--name",
25
+ "-n",
26
+ "--session",
27
+ "--session-id",
28
+ "--fork",
29
+ "--session-dir",
30
+ "--tools",
31
+ "-t",
32
+ "--exclude-tools",
33
+ "-xt",
34
+ "--thinking",
35
+ "--export",
36
+ "--extension",
37
+ "-e",
38
+ "--skill",
39
+ "--prompt-template",
40
+ "--theme",
41
+ ]);
42
+
43
+ const PI_MODES = new Set(["text", "json", "rpc"]);
44
+
45
+ /**
46
+ * True when argv is a oneshot pi invocation (`-p` / `--print` / `--mode json`).
47
+ * Session extras must not register — watchers and sockets keep the Node event
48
+ * loop alive after the reply.
49
+ *
50
+ * Mirrors pi `parseArgs` + `resolveAppMode` (without TTY): last valid
51
+ * `--mode <text|json|rpc>` wins; rpc is never oneshot; json is; print flag
52
+ * is oneshot unless last mode is rpc. `--mode=json` / `--mode=rpc` are unknown
53
+ * flags in pi (not mode). Value-aware so `--mode -p` does not treat `-p`
54
+ * as the print flag. No `--` end-of-options (parseArgs has none).
55
+ *
56
+ * Debug log uses isDebugEnabled() (same log_oneshot / PI_LOG_ONESHOT sources
57
+ * as fileLog). Level is cached 30s so register/argv scan does not re-stat.
58
+ *
59
+ * CLI/ACPX providers still load. Non-TTY stdin or stdout print (`echo | pi`,
60
+ * `pi | cat`) without those flags is not detected here — argv has no flags;
61
+ * use `ctx.mode` after session_start.
62
+ */
63
+ export function isPiOneshotInvocation(argv: string[] = process.argv): boolean {
64
+ const args = argv.slice(2);
65
+ let lastMode: string | undefined;
66
+ let print = false;
67
+ for (let i = 0; i < args.length; i++) {
68
+ const arg = args[i];
69
+ if (arg === "-p" || arg === "--print") {
70
+ print = true;
71
+ continue;
72
+ }
73
+ if (arg === "--mode") {
74
+ const value = args[i + 1];
75
+ if (value !== undefined) {
76
+ i += 1;
77
+ if (PI_MODES.has(value)) lastMode = value;
78
+ }
79
+ continue;
80
+ }
81
+ if (ONESHOT_VALUE_FLAGS.has(arg) && args[i + 1] !== undefined) {
82
+ i += 1;
83
+ }
84
+ }
85
+ const oneshot = lastMode === "rpc" ? false : lastMode === "json" || print;
86
+ if (log.isDebugEnabled()) {
87
+ log.debug("oneshot argv", { oneshot, lastMode, print });
88
+ }
89
+ return oneshot;
90
+ }
91
+
92
+ /** True when pitasks/pidash/pidiff/coms should skip register. Caller returns if true. */
93
+ export function shouldSkipOneshotRegister(
94
+ logger: { info: (msg: string) => void },
95
+ argv: string[] = process.argv,
96
+ ): boolean {
97
+ if (!isPiOneshotInvocation(argv)) return false;
98
+ logger.info("skip register: oneshot print/json");
99
+ return true;
100
+ }
101
+
102
+ /**
103
+ * Shutdown dream runs `runDreamAsync` → `spawnAsyncAgent` (not detached/unref'd).
104
+ * Skip when argv is oneshot OR session `mode` is print/json so `pi -p` can exit.
105
+ * Does not skip rpc/tui unless argv is oneshot (`-p` + `--mode rpc` is not oneshot).
106
+ */
107
+ export function shouldSkipOneshotShutdownDream(
108
+ mode?: string | null,
109
+ argv: string[] = process.argv,
110
+ ): boolean {
111
+ const oneshot = isPiOneshotInvocation(argv);
112
+ const printOrJson = mode === "print" || mode === "json";
113
+ const skip = oneshot || printOrJson;
114
+ log.debug("skip shutdown dream?", { skip, oneshot, mode });
115
+ return skip;
116
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Shared binary resolution helpers.
3
+ *
4
+ * In-process PATH scan for resolving CLI binaries. Avoids spawnSync("which")
5
+ * on auth/filter hot paths. Used by both CLI and ACPX provider drivers.
6
+ *
7
+ * Moved from cli-provider/shared/discover-cache.ts so both provider families
8
+ * can import without cross-provider dependencies.
9
+ *
10
+ * @module shared/resolve-binary
11
+ */
12
+
13
+ import {
14
+ accessSync,
15
+ constants,
16
+ realpathSync,
17
+ statSync,
18
+ } from "node:fs";
19
+ import { extname, isAbsolute, join, posix, win32 } from "node:path";
20
+
21
+ /** Cache keyed by `${platform}\0${binary}\0${PATH}\0${PATHEXT}` — successful resolves only. */
22
+ const resolveBinaryCache = new Map<string, string>();
23
+
24
+ export type ResolveBinaryEnv = {
25
+ PATH?: string;
26
+ PATHEXT?: string;
27
+ };
28
+
29
+ /** Parse PATHEXT into uppercase extensions (e.g. ".EXE"). Exported for tests. */
30
+ export function parsePathext(pathextEnv?: string): string[] {
31
+ const raw = pathextEnv ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
32
+ return raw
33
+ .split(";")
34
+ .map((s) => s.trim())
35
+ .filter(Boolean)
36
+ .map((s) => (s.startsWith(".") ? s : `.${s}`).toUpperCase());
37
+ }
38
+
39
+ /**
40
+ * True when filePath has an extension listed in PATHEXT (case-insensitive).
41
+ * Extensionless files are not CreateProcess-launchable on Windows.
42
+ */
43
+ export function isLaunchableWin32(
44
+ filePath: string,
45
+ pathextEnv?: string,
46
+ ): boolean {
47
+ const ext = extname(filePath);
48
+ if (!ext) return false;
49
+ return parsePathext(pathextEnv).includes(ext.toUpperCase());
50
+ }
51
+
52
+ /**
53
+ * Suffixes to try when scanning PATH for `binary`.
54
+ * On win32: if `binary` already has a PATHEXT extension, try as-is only;
55
+ * otherwise try each PATHEXT suffix (no bare/empty suffix).
56
+ */
57
+ export function candidateSuffixesFor(
58
+ binary: string,
59
+ platform: NodeJS.Platform = process.platform,
60
+ pathextEnv?: string,
61
+ ): string[] {
62
+ if (platform !== "win32") return [""];
63
+ const ext = extname(binary);
64
+ if (ext && parsePathext(pathextEnv).includes(ext.toUpperCase())) {
65
+ return [""];
66
+ }
67
+ return parsePathext(pathextEnv);
68
+ }
69
+
70
+ /**
71
+ * PATH list separator for the *resolved* platform (not the host OS).
72
+ * Needed so win32 simulation on Unix splits semicolon-separated PATH correctly.
73
+ */
74
+ export function pathDelimiterFor(platform: NodeJS.Platform): string {
75
+ return platform === "win32" ? win32.delimiter : posix.delimiter;
76
+ }
77
+
78
+ /** Exported for tests — regular file + platform launchability rules. */
79
+ export function isExecutableForPlatform(
80
+ filePath: string,
81
+ platform: NodeJS.Platform,
82
+ pathextEnv?: string,
83
+ ): boolean {
84
+ let st;
85
+ try {
86
+ st = statSync(filePath);
87
+ } catch {
88
+ return false;
89
+ }
90
+ // Directories are often X_OK (searchable); only regular files are binaries.
91
+ if (!st.isFile()) return false;
92
+ // Win32: never treat any regular file as executable — require PATHEXT.
93
+ if (platform === "win32") return isLaunchableWin32(filePath, pathextEnv);
94
+ try {
95
+ accessSync(filePath, constants.X_OK);
96
+ return true;
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ function finalizeResolved(filePath: string): string {
103
+ try {
104
+ return realpathSync(filePath);
105
+ } catch {
106
+ return filePath;
107
+ }
108
+ }
109
+
110
+ function cacheKeyFor(
111
+ binary: string,
112
+ platform: NodeJS.Platform,
113
+ pathEnv: string,
114
+ pathextEnv: string,
115
+ ): string {
116
+ return `${platform}\0${binary}\0${pathEnv}\0${pathextEnv}`;
117
+ }
118
+
119
+ /**
120
+ * Resolve a binary for an explicit platform (injectable for win32 tests on Unix).
121
+ * Absolute paths still require a launchable PATHEXT extension on win32.
122
+ */
123
+ export function resolveBinaryForPlatform(
124
+ binary: string,
125
+ platform: NodeJS.Platform,
126
+ env: ResolveBinaryEnv = {},
127
+ ): string | null {
128
+ if (!binary) return null;
129
+
130
+ const pathEnv = env.PATH ?? process.env.PATH ?? "";
131
+ const pathextEnv =
132
+ env.PATHEXT ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
133
+ const cacheKey = cacheKeyFor(binary, platform, pathEnv, pathextEnv);
134
+ const cached = resolveBinaryCache.get(cacheKey);
135
+ if (cached !== undefined) {
136
+ if (isExecutableForPlatform(cached, platform, pathextEnv)) return cached;
137
+ resolveBinaryCache.delete(cacheKey);
138
+ }
139
+
140
+ const isExec = (p: string) =>
141
+ isExecutableForPlatform(p, platform, pathextEnv);
142
+
143
+ // Absolute or explicit relative path — check directly (mirrors `which /path`).
144
+ if (isAbsolute(binary) || binary.includes("/") || binary.includes("\\")) {
145
+ if (!isExec(binary)) return null;
146
+ const resolved = finalizeResolved(binary);
147
+ resolveBinaryCache.set(cacheKey, resolved);
148
+ return resolved;
149
+ }
150
+
151
+ const suffixes = candidateSuffixesFor(binary, platform, pathextEnv);
152
+ const envDelimiter = pathDelimiterFor(platform);
153
+ for (const dir of pathEnv.split(envDelimiter)) {
154
+ if (!dir) continue;
155
+ for (const suffix of suffixes) {
156
+ // PATHEXT is typically uppercase; Windows FS is case-insensitive.
157
+ // On case-sensitive hosts (win32 simulation in tests), also try lowercase.
158
+ const variants =
159
+ platform === "win32" && suffix && suffix !== suffix.toLowerCase()
160
+ ? [binary + suffix, binary + suffix.toLowerCase()]
161
+ : [binary + suffix];
162
+ for (const name of variants) {
163
+ const candidate = join(dir, name);
164
+ if (!isExec(candidate)) continue;
165
+ const resolved = finalizeResolved(candidate);
166
+ resolveBinaryCache.set(cacheKey, resolved);
167
+ return resolved;
168
+ }
169
+ }
170
+ }
171
+
172
+ // Do not cache misses — mid-session install with same PATH must rediscover.
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * Resolve a binary name (or absolute path) via an in-process PATH scan.
178
+ * Avoids spawnSync("which") on auth/filter hot paths.
179
+ * Returns realpath when possible, null if missing.
180
+ */
181
+ export function resolveBinary(binary: string): string | null {
182
+ return resolveBinaryForPlatform(binary, process.platform, {
183
+ PATH: process.env.PATH,
184
+ PATHEXT: process.env.PATHEXT,
185
+ });
186
+ }
187
+
188
+ /** Clear PATH resolve cache (tests / PATH mutation). */
189
+ export function clearResolveBinaryCache(): void {
190
+ resolveBinaryCache.clear();
191
+ }
@@ -17,4 +17,7 @@ export interface SessionInfo {
17
17
  diffPort?: number | null;
18
18
  working?: boolean;
19
19
  name?: string;
20
+ comsName?: string;
21
+ comsPurpose?: string;
22
+ comsProject?: string;
20
23
  }
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "pi-orchestrator-config",
3
- "version": "4.2.0",
3
+ "version": "4.3.2",
4
4
  "description": "Orchestrator pattern for pi: specialist subagents, enforcement, code review loop, workflow commands",
5
5
  "keywords": [
6
6
  "pi-package"
7
7
  ],
8
8
  "license": "MIT",
9
- "workspaces": ["packages/pi-sidecar", "packages/pi-vertex-claude"],
9
+ "workspaces": [
10
+ "packages/pi-sidecar",
11
+ "packages/pi-vertex-claude"
12
+ ],
10
13
  "files": [
11
14
  "extensions/acpx-provider/",
12
15
  "extensions/cli-provider/",
@@ -17,17 +20,20 @@
17
20
  "extensions/image-gen/",
18
21
  "extensions/pidash/*.ts",
19
22
  "extensions/pidiff/*.ts",
23
+ "extensions/pitasks/",
20
24
  "agents/",
21
25
  "rules/",
22
26
  "prompts/",
23
27
  "templates/",
24
- "skills/"
28
+ "skills/",
29
+ "settings-keys.json"
25
30
  ],
26
31
  "dependencies": {
27
32
  "@huggingface/transformers": "^4.2.0",
28
33
  "acpx": ">=0.12.0 <1",
29
34
  "chokidar": "^5.0.0",
30
35
  "discord.js": "^14.0.0",
36
+ "strip-json-comments": "^5.0.3",
31
37
  "ws": "^8.0.0"
32
38
  },
33
39
  "devDependencies": {
@@ -72,6 +72,8 @@ This outputs JSON to stdout. Parse the result:
72
72
 
73
73
  - If `rate_limited` is `false` — notify user "Not rate limited" and exit
74
74
  - If `rate_limited` is `true` — read `wait_seconds` and proceed to Phase 3
75
+ - If `fallback` is `true`, the wait time is **estimated** from the comment timestamp
76
+ (1-hour window), not parsed from the comment body. Warn the user that timing is approximate.
75
77
 
76
78
  ### Phase 3: Wait and Trigger Review
77
79
 
@@ -8,6 +8,8 @@
8
8
 
9
9
  ---
10
10
 
11
+ {{IF:orchestrator_edit_write_block}}
12
+
11
13
  ## Delegation Model
12
14
 
13
15
  The orchestrator delegates — never implements directly.
@@ -28,6 +30,7 @@ Outside slash commands, never use edit, write, or bash directly (except `mcpl`)
28
30
  **Never delegate:** slash commands — execute them directly (see slash command rules).
29
31
 
30
32
  ---
33
+ {{/IF}}
31
34
 
32
35
  ## Before Implementation (MANDATORY)
33
36
 
@@ -19,7 +19,7 @@
19
19
  | Debugging | `debugger` |
20
20
  | API docs | `api-documenter` |
21
21
  | External repo security audit | `security-auditor` |
22
- | External AI agents (cursor, codex, gemini, claude, copilot, etc.) | `/acpx-prompt` |
22
+ | {{IF:external_ai_agents}}External AI agents (cursor, codex, gemini, claude, copilot, etc.){{/IF}} | {{IF:external_ai_agents}}`/acpx-prompt`{{/IF}} |
23
23
  | External library/framework docs (React, FastAPI, Django, etc.) | `docs-fetcher` |
24
24
 
25
25
  ## Routing by Intent, Not Tool
@@ -56,3 +56,14 @@ Some agents are dispatched internally by rules or prompt templates, not by the r
56
56
  ## Fallback
57
57
 
58
58
  **Fallback:** No specialist? → `worker` agent
59
+
60
+ ## Model Override
61
+
62
+ When the user asks to run an agent with a specific model:
63
+
64
+ 1. Use `list_models` tool to discover available providers and models.
65
+ 2. Pass the model to `subagent(model="provider/model-id")`.
66
+ 3. Format: `provider/model-id` (e.g., `litellm/claude-opus-4-6-1m`) or just `model-id` (overrides model only; provider follows normal resolution chain).
67
+ 4. For parallel tasks, set `model` per-task in the tasks array. Top-level `model` is used as fallback for tasks without their own.
68
+ 5. Chain steps share the top-level `model` — no per-step override.
69
+ 6. Explicit `model` param overrides agent_overrides, frontmatter, and settings.
@@ -4,6 +4,7 @@ After ANY code change, send to ALL 6 agents (5 reviewers + test-automator) IN PA
4
4
 
5
5
  ## Commit enforcement
6
6
 
7
+ {{IF:review_loop_enforcement}}
7
8
  When `review_loop_enforcement` is enabled, the enforcement rule in `enforcement.ts` blocks `git commit` unless:
8
9
 
9
10
  - Review status is `clean` (all reviewers returned 0 findings)
@@ -17,23 +18,26 @@ call `addReviewerPending`/`recordReviewerResult` from bash, write to the JSON fi
17
18
  or use any method to fake a clean state. The enforcement system detects and blocks this.
18
19
  The ONLY way to reach `clean` status is to run the actual review agents.
19
20
 
20
- Resolution: project `.pi/pi-config-settings.json` → global `~/.pi/pi-config-settings.json` → `PI_REVIEW_LOOP_ENFORCEMENT` env var → `false`.
21
+ Resolution is handled automatically via project settings injection.
21
22
 
22
23
  - MUST loop until clean or the max cycle cap is reached — see "Cycle Definition & Max Cycles" below.
24
+ {{/IF}}
23
25
 
24
- If disabled:
26
+ {{IFNOT:review_loop_enforcement}}
27
+ Single review pass is sufficient — no commit blocking from the review loop.
28
+ Step 5a (fix/explain every finding) is optional: fix what you agree with, skip what you don't.
29
+ {{/IFNOT}}
25
30
 
26
- - Single review pass is sufficient
27
- - No commit blocking
31
+ **All 6 agents are always called.** `review_loop_enforcement` controls whether the loop repeats.
28
32
 
29
- **In both cases, all 6 agents are always called.** `review_loop_enforcement` controls whether the loop repeats; when enabled,
30
- `review_loop_max_cycles` caps **total** cycles including the first (see Cycle Definition below).
33
+ {{IF:review_loop_enforcement}}
34
+ When enabled, `review_loop_max_cycles` caps **total** cycles including the first (see Cycle Definition below).
31
35
 
32
36
  ### Cycle Definition & Max Cycles
33
37
 
34
38
  One **cycle** = call all 6 reviewers (in parallel) → fix/explain findings → end of cycle.
35
39
 
36
- **The cycle count IS the `cycle` field in `pi-config-review-state.json`** — the same counter shown by
40
+ **The cycle count IS the `cycle` field in `pi-config-review-state.jsonl`** — the same counter shown by
37
41
  `/review-status` and the review UI status card. It increments each time reviewers are (re-)dispatched
38
42
  for a fresh pass. Step 5's branches below MUST compare against that same counter — do not track cycles
39
43
  mentally or separately.
@@ -46,8 +50,8 @@ Disable the review loop via `review_loop_enforcement: false` — not via max_cyc
46
50
  Cap check is after 5a; it only blocks `go to 2` (re-dispatch of step 2 / all 6 agents,
47
51
  including test-automator), not completing the current cycle's fix/explain.
48
52
 
49
- **After 5a completes on the max cycle, two outcomes — neither unblocks `git commit`**
50
- (still controlled solely by `review_loop_enforcement`; status must be `clean` + `tests_passed: true`):
53
+ **After 5a completes on the max cycle, two outcomes** (report them; commit may still be
54
+ allowed via the max-cycle path — see step 6):
51
55
 
52
56
  - **Not fixed** (explained why not) → outstanding — report them.
53
57
  - **Fixed** (verification blocked by the cap) → cannot re-dispatch to confirm clean — report that
@@ -64,24 +68,30 @@ cycle3: dispatch → findings → 5a (fix|explain) → cycle >= max → stop (no
64
68
  ```text
65
69
  1. Specialist writes/fixes code
66
70
  2. Send ALL 6 agents IN PARALLEL (async): 5 reviewers + test-automator
71
+ **On cycle 2+: MANDATORY — include prior cycle findings + responses in each reviewer's prompt (see Step 5a format).**
72
+ Reviewers without this context will blindly repeat the same findings. This is not optional.
67
73
  3. Wait for all 6 to complete
68
74
  4. Merge & deduplicate review findings
69
75
  5. Has findings OR tests failed?
70
76
  ── Neither? ↓ status: clean, tests_passed: true
71
77
  ── Findings and/or tests failed? → ALWAYS run 5a first: fix code OR explain why not, for EACH finding;
72
- fix code for failing tests. Then check state.cycle (from `pi-config-review-state.json`):
78
+ fix code for failing tests. Then check state.cycle (from `pi-config-review-state.jsonl`):
73
79
  · cycle < {{REVIEW_LOOP_MAX_CYCLES}}? → go to 2 (re-run all 6 with prior findings + responses)
74
80
  · cycle >= {{REVIEW_LOOP_MAX_CYCLES}}? → **STOP** (terminal): report the two-outcome result
75
81
  (see "Cycle Definition & Max Cycles" above). Do NOT proceed to step 6.
76
- 6. NOW you can commit — the pre-commit hook will pass (only when status is `clean` AND `tests_passed: true`)
77
- ✅ DONE — commit/push allowed (enforcement checks status: clean AND tests_passed: true)
82
+ 6. NOW you can commit — the pre-commit hook will pass when:
83
+ - status is `clean` AND `tests_passed: true`, OR
84
+ - max cycles exhausted (`has_findings` or `clean`, no pending reviewers, `cycle >= review_loop_max_cycles` — no `tests_passed` check)
85
+ ✅ DONE — commit/push allowed
78
86
  ```
79
87
 
80
- 🚨 **Step 6 applies only when clean.** Do NOT attempt `git commit` before reaching `clean` + `tests_passed: true`.
88
+ 🚨 **Step 6 applies when clean OR max cycles exhausted.** Do NOT attempt `git commit` before that.
81
89
  The enforcement rule blocks commits until this is satisfied — if it blocks you, run the reviewers.
82
90
  **After a cap stop:** report the two-outcome result (**Not fixed** → outstanding, **Fixed** → verification
83
- blocked); raise `review_loop_max_cycles` or disable `review_loop_enforcement` if needed — do **not**
91
+ blocked); commit is allowed when status is `has_findings` or `clean`, no reviewers pending, and
92
+ `cycle >= review_loop_max_cycles` (no `tests_passed` requirement); do **not**
84
93
  return to step 2 (re-dispatch of all 6 agents, including test-automator).
94
+ {{/IF}}
85
95
 
86
96
  ## Review Agents
87
97
 
@@ -100,7 +110,7 @@ Six agents run in parallel for comprehensive coverage:
100
110
  Do NOT block waiting for results — continue working while they run.**
101
111
 
102
112
  Send reviewers "Review the code changes" — never mention `git diff HEAD` in the task prompt.
103
- Reviewers get `$PI_REVIEW_BASE_BRANCH` env var and use `git diff origin/$PI_REVIEW_BASE_BRANCH...HEAD` themselves.
113
+ Reviewers get `$PI_REVIEW_BASE_BRANCH` env var and use `git diff origin/$PI_REVIEW_BASE_BRANCH` themselves (includes uncommitted changes).
104
114
 
105
115
  Reviewers return structured JSON: `{"findings": [{"severity": "...", "file": "...", "line": N, "description": "...", "suggestion": "..."}]}`.
106
116
  The async runner validates the output is valid JSON and retries if not (up to 3 times).
@@ -118,7 +128,9 @@ reviewers, even on the same file/line. A quality finding about *how* code is wri
118
128
  address *whether* the code matches the spec. Always keep both. Spec findings CAN still be
119
129
  deduplicated against other spec findings when they are truly the same issue.
120
130
 
121
- ## Step 5a: Respond to Findings (when `review_loop_enforcement` is enabled)
131
+ {{IF:review_loop_enforcement}}
132
+
133
+ ## Step 5a: Respond to Findings
122
134
 
123
135
  For each finding from step 4, do ONE of:
124
136
 
@@ -137,7 +149,7 @@ The following findings were raised in review cycle {N}. Each has been either fix
137
149
  explained. Do NOT re-raise findings that were adequately addressed. Only re-raise if:
138
150
  - The code fix is incorrect or incomplete
139
151
  - The explanation is invalid or factually wrong
140
- - You disagree with the reasoning (explain why)
152
+ - You disagree with the reasoning (state why in the finding description field)
141
153
 
142
154
  Findings with valid explanations that you accept → do not re-raise.
143
155
  Findings that were fixed in code → verify the fix, do not re-raise if correct.
@@ -151,29 +163,38 @@ Findings that were fixed in code → verify the fix, do not re-raise if correct.
151
163
  - If a finding was fixed → verify the fix is correct, don't re-raise
152
164
  - If a finding was explained with a valid technical reason → accept it, don't re-raise
153
165
  - If the explanation is wrong or the fix is incomplete → re-raise with specific pushback
154
-
155
- **When `review_loop_enforcement` is disabled:** Step 5a is optional. Single review pass
156
- is sufficient — fix what you agree with, skip what you don't. No need to explain skips.
166
+ {{/IF}}
157
167
 
158
168
  ## Key Rules
159
169
 
160
170
  Never skip code review — all 6 agents always run (5 reviewers + test-automator).
171
+ Exception: on `chore/bump-version` branches (release version bumps), review dirty-tracking and commit blocking are skipped.
172
+ {{IF:review_loop_enforcement}}
161
173
  When `review_loop_enforcement` is enabled: loop, respond to each finding (fix or explain), and re-run all 6 from step 2
162
174
  until clean or the cycle cap is reached — see "Cycle Definition & Max Cycles" above for the exact stop conditions.
163
- When disabled: single pass is sufficient; no mandatory re-loop or explanations.
175
+ {{/IF}}
176
+ {{IFNOT:review_loop_enforcement}}
177
+ When `review_loop_enforcement` is disabled: single pass is sufficient; no mandatory re-loop or explanations.
178
+ {{/IFNOT}}
164
179
  Minor test/config-only fixes skip re-review (go to step 5); substantive code changes require full re-review.
165
180
 
166
181
  ## Test Tracking
167
182
 
168
- Test results are tracked in `pi-config-review-state.json` via the `tests_passed` field.
169
- This is **code-enforced** — commit/push is blocked unless `tests_passed: true` (when `review_loop_enforcement` is enabled).
183
+ Test results are tracked in `pi-config-review-state.jsonl` via the `tests_passed` field.
184
+
185
+ {{IF:review_loop_enforcement}}
186
+ This is **code-enforced** — commit/push is blocked unless `tests_passed: true`.
187
+ {{/IF}}
188
+ {{IFNOT:review_loop_enforcement}}
189
+ Tests still run and `tests_passed` is still recorded; commit is not blocked by review state when enforcement is off.
190
+ {{/IFNOT}}
170
191
 
171
192
  **How `tests_passed` gets set:**
172
193
 
173
194
  - **Bash hook detection:** When any agent runs a test command (`pytest`, `npm test`, `npx tsx --test`, `tox`, `go test`, `vitest`, `jest`, `mocha`),
174
195
  the enforcement hook detects it and auto-marks `tests_passed` based on exit code.
175
196
  - **Agent completion:** When `test-automator` or `test-runner` agents complete, their result auto-marks `tests_passed`.
176
- - **Reset on edit:** Any file edit triggers `markNeedsReview()`, which resets `tests_passed: false` —
197
+ - **Reset on edit:** Any file edit triggers `markNeedsReview()` (except on `chore/bump-version` branches), which resets `tests_passed: false` —
177
198
  preventing stale results.
178
199
 
179
200
  **Duplicate test run avoidance:** If a specialist already ran tests before the review loop, `tests_passed` may already be `true`.
@@ -215,20 +236,26 @@ If both `git apply` methods fail, skip baseline comparison and note "baseline co
215
236
  For automated review flows (autorabbit, autoqodo), use **two-stage order** instead of parallel:
216
237
 
217
238
  1. **Stage 1 — Spec Compliance:** Does code meet requirements? All deliverables implemented? No scope creep?
239
+ {{IF:review_loop_enforcement}}
218
240
  Loop Stage 1 until passed OR the shared cycle cap is reached.
241
+ {{/IF}}
219
242
  2. **Stage 2 — Code Quality:** Quality, security, guidelines adherence.
243
+ {{IF:review_loop_enforcement}}
220
244
  Loop Stage 2 until passed OR the shared cycle cap is reached.
245
+ {{/IF}}
221
246
 
247
+ {{IF:review_loop_enforcement}}
222
248
  **Staged cycle definition:** one **cycle** = one pass of the *current* stage's reviewers (in parallel)
223
249
  → fix/explain that stage's findings → end of cycle. This mirrors the parallel-mode cycle definition
224
250
  (see "Cycle Definition & Max Cycles" above), scoped to one stage's reviewers instead of all 6.
225
251
 
226
252
  **The `review_loop_max_cycles` cap is shared across both stages — one total budget, not one per stage.**
227
253
  Stage 1 and Stage 2 both draw from and increment the same `cycle` counter in
228
- `pi-config-review-state.json`. Cap check, 5a-before-cap, and two-outcome reporting follow
254
+ `pi-config-review-state.jsonl`. Cap check, 5a-before-cap, and two-outcome reporting follow
229
255
  "Cycle Definition & Max Cycles" above. Stage-specific stops: if Stage 1 is **not clean** when the cap is hit, stop entirely — do not proceed
230
256
  to Stage 2 (covers both **Not fixed** and **Fixed** (verification blocked)). If hit during Stage 2,
231
257
  stop looping Stage 2.
258
+ {{/IF}}
232
259
 
233
260
  Don't polish code that doesn't meet spec — it wastes work.
234
261
  Parallel mode (all 6 agents at once) remains default for manual reviews.
@@ -7,12 +7,16 @@
7
7
  3. **The prompt decides** when to run directly and when to delegate to agents
8
8
  4. If the prompt says "run this bash command" — run it directly
9
9
  5. If the prompt says "delegate to X agent" — delegate
10
+ {{IF:orchestrator_edit_write_block}}
10
11
  6. If the prompt doesn't specify — follow normal orchestrator rules (delegate to specialists)
12
+ {{/IF}}
11
13
 
12
14
  ## Key Rules
13
15
 
14
16
  - The orchestrator **maintains control** of the prompt workflow
17
+ {{IF:orchestrator_edit_write_block}}
15
18
  - The prompt template's instructions **override** general delegation rules when they conflict
19
+ {{/IF}}
16
20
  - **NEVER** delegate the `/command` itself to an agent — only delegate sub-tasks when the prompt says to
17
21
 
18
22
  ❌ **WRONG**: `/mycommand` → delegate entire prompt to an agent
@@ -56,7 +56,7 @@ Search past conversation summaries for references to previous sessions. Returns
56
56
 
57
57
  ```text
58
58
  .pi/memory/
59
- ├── memory-scores.json # Scoring backend (auto-managed)
59
+ ├── memory-scores.jsonl # Scoring backend (auto-managed)
60
60
  └── topics/
61
61
  ├── preferences.md # [preference] entries
62
62
  ├── lessons.md # [lesson] entries
@@ -228,8 +228,8 @@ On reinforce crossing a threshold, a promotion pass runs automatically.
228
228
 
229
229
  `memory_add` / `memory_edit` accept optional `sourceSession`, `derivedFrom`, `informs`.
230
230
  Preference extractor sets `sourceSession` when the session id is available.
231
- Dream may write `.pi/memory/provenance-pending.json`; merge happens on dream complete.
232
- Stored in `memory-scores.json` only — not injected into the situation report.
231
+ Dream may write `.pi/memory/provenance-pending.jsonl`; merge happens on dream complete.
232
+ Stored in `memory-scores.jsonl` only — not injected into the situation report.
233
233
  Shown in `memory_search` / `memory_reflect` when present.
234
234
 
235
235
  ---
@@ -49,6 +49,8 @@ Always pass `cwd` when delegating to subagents in all modes — omitting it caus
49
49
 
50
50
  ---
51
51
 
52
+ {{IF:use_worktrees}}
53
+
52
54
  ## Multi-PR / Multi-Branch Work
53
55
 
54
56
  **NEVER switch branches in the main worktree** when working on multiple PRs — other agents may be running there.
@@ -60,6 +62,7 @@ git worktree remove .worktrees/pr-42 # when done
60
62
  ```
61
63
 
62
64
  ---
65
+ {{/IF}}
63
66
 
64
67
  ## User Interaction
65
68