pi-orchestrator-config 4.2.1 → 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 (92) hide show
  1. package/README.md +52 -61
  2. package/agents/git-expert.md +5 -1
  3. package/agents/github-expert.md +4 -1
  4. package/extensions/acpx-provider/runtime-models.ts +18 -6
  5. package/extensions/cli-provider/agents/claude.ts +3 -2
  6. package/extensions/cli-provider/agents/cursor.ts +3 -5
  7. package/extensions/cli-provider/agents/gemini.ts +3 -2
  8. package/extensions/cli-provider/discover.ts +7 -7
  9. package/extensions/cli-provider/index.ts +5 -0
  10. package/extensions/cli-provider/runtime-models.ts +18 -6
  11. package/extensions/cli-provider/session-reaper.ts +5 -7
  12. package/extensions/cli-provider/sessions.ts +4 -0
  13. package/extensions/cli-provider/shared/discover-cache.ts +17 -176
  14. package/extensions/coms/coms-p2p.ts +1719 -423
  15. package/extensions/coms/coms-shared.ts +261 -39
  16. package/extensions/coms/coms-wrapper.ts +100 -62
  17. package/extensions/coms/index.ts +8 -4
  18. package/extensions/coms/mixed-turn.ts +26 -0
  19. package/extensions/coms/probe-socket.ts +42 -0
  20. package/extensions/coms/themeMap.ts +0 -1
  21. package/extensions/image-gen/image-gen.ts +19 -1
  22. package/extensions/orchestrator/async-agents.ts +509 -85
  23. package/extensions/orchestrator/cron.ts +14 -8
  24. package/extensions/orchestrator/dreaming.ts +31 -24
  25. package/extensions/orchestrator/enforcement-helpers.ts +459 -17
  26. package/extensions/orchestrator/enforcement-rules.ts +5 -4
  27. package/extensions/orchestrator/enforcement.ts +79 -25
  28. package/extensions/orchestrator/extended-autocomplete.ts +7 -0
  29. package/extensions/orchestrator/git-helpers.ts +2 -2
  30. package/extensions/orchestrator/index.ts +23 -10
  31. package/extensions/orchestrator/markdown-transformer.ts +313 -0
  32. package/extensions/orchestrator/memory-embeddings.ts +36 -34
  33. package/extensions/orchestrator/memory-provenance.ts +29 -25
  34. package/extensions/orchestrator/memory-scoring.ts +23 -18
  35. package/extensions/orchestrator/memory-tools.ts +10 -3
  36. package/extensions/orchestrator/overlay-dashboard.ts +17 -5
  37. package/extensions/orchestrator/pi-config-review-state.ts +44 -33
  38. package/extensions/orchestrator/project-settings.ts +220 -21
  39. package/extensions/orchestrator/review-porcelain.ts +4 -0
  40. package/extensions/orchestrator/review-ui.ts +11 -5
  41. package/extensions/orchestrator/rule-placeholders.ts +384 -1
  42. package/extensions/orchestrator/rules.ts +80 -132
  43. package/extensions/orchestrator/settings-tui-helpers.ts +380 -0
  44. package/extensions/orchestrator/settings-tui-submenus.ts +647 -0
  45. package/extensions/orchestrator/settings-tui.ts +636 -0
  46. package/extensions/orchestrator/state-jsonl.ts +431 -0
  47. package/extensions/orchestrator/subagent-tool.ts +105 -50
  48. package/extensions/orchestrator/sync-limit.ts +23 -0
  49. package/extensions/orchestrator/task-lifecycle.ts +77 -67
  50. package/extensions/orchestrator/utils.ts +8 -5
  51. package/extensions/pidash/pidash.ts +193 -156
  52. package/extensions/pidiff/pidiff.ts +64 -38
  53. package/extensions/pidiff/spawn-lock.ts +62 -0
  54. package/extensions/pitasks/index.ts +430 -0
  55. package/extensions/pitasks/reminders.ts +37 -0
  56. package/extensions/pitasks/task-store.ts +467 -0
  57. package/extensions/pitasks/task-tools.ts +375 -0
  58. package/extensions/pitasks/task-widget.ts +181 -0
  59. package/extensions/pitasks/types.ts +38 -0
  60. package/extensions/providers/acpx-driver.ts +12 -2
  61. package/extensions/providers/built-in-drivers.ts +6 -6
  62. package/extensions/providers/claude-driver.ts +1 -1
  63. package/extensions/providers/cursor-cli-driver.ts +1 -1
  64. package/extensions/providers/gemini-driver.ts +1 -1
  65. package/extensions/providers/index.ts +144 -26
  66. package/extensions/providers/initialized-guard.ts +27 -0
  67. package/extensions/providers/restore-default-model.ts +596 -0
  68. package/extensions/providers/session-shutdown.ts +39 -0
  69. package/extensions/shared/coms-active.ts +23 -0
  70. package/extensions/shared/create-runtime-provider.ts +2 -0
  71. package/extensions/shared/daemon-manager.ts +0 -8
  72. package/extensions/shared/file-logger.ts +132 -25
  73. package/extensions/shared/logger.ts +50 -0
  74. package/extensions/shared/models-dev.ts +421 -0
  75. package/extensions/shared/oneshot.ts +116 -0
  76. package/extensions/shared/resolve-binary.ts +191 -0
  77. package/extensions/shared/types.ts +3 -0
  78. package/package.json +7 -2
  79. package/prompts/coderabbit-rate-limit.md +2 -0
  80. package/rules/00-orchestrator-core.md +3 -0
  81. package/rules/10-agent-routing.md +1 -1
  82. package/rules/20-code-review-loop.md +38 -16
  83. package/rules/30-prompt-templates.md +4 -0
  84. package/rules/35-memory.md +3 -3
  85. package/rules/40-critical-rules.md +3 -0
  86. package/rules/55-coms-protocol.md +84 -18
  87. package/rules/60-task-tracking.md +2 -0
  88. package/settings-keys.json +481 -20
  89. package/templates/coms-feature-manager-prompt.md +4 -6
  90. package/extensions/coms/coms-net-server.ts +0 -1642
  91. package/extensions/coms/coms-net-wrapper.ts +0 -521
  92. 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.1",
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,6 +20,7 @@
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/",
@@ -29,6 +33,7 @@
29
33
  "acpx": ">=0.12.0 <1",
30
34
  "chokidar": "^5.0.0",
31
35
  "discord.js": "^14.0.0",
36
+ "strip-json-comments": "^5.0.3",
32
37
  "ws": "^8.0.0"
33
38
  },
34
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
@@ -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)
@@ -20,20 +21,23 @@ The ONLY way to reach `clean` status is to run the actual review agents.
20
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.
@@ -71,7 +75,7 @@ cycle3: dispatch → findings → 5a (fix|explain) → cycle >= max → stop (no
71
75
  5. Has findings OR tests failed?
72
76
  ── Neither? ↓ status: clean, tests_passed: true
73
77
  ── Findings and/or tests failed? → ALWAYS run 5a first: fix code OR explain why not, for EACH finding;
74
- 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`):
75
79
  · cycle < {{REVIEW_LOOP_MAX_CYCLES}}? → go to 2 (re-run all 6 with prior findings + responses)
76
80
  · cycle >= {{REVIEW_LOOP_MAX_CYCLES}}? → **STOP** (terminal): report the two-outcome result
77
81
  (see "Cycle Definition & Max Cycles" above). Do NOT proceed to step 6.
@@ -87,6 +91,7 @@ The enforcement rule blocks commits until this is satisfied — if it blocks you
87
91
  blocked); commit is allowed when status is `has_findings` or `clean`, no reviewers pending, and
88
92
  `cycle >= review_loop_max_cycles` (no `tests_passed` requirement); do **not**
89
93
  return to step 2 (re-dispatch of all 6 agents, including test-automator).
94
+ {{/IF}}
90
95
 
91
96
  ## Review Agents
92
97
 
@@ -123,7 +128,9 @@ reviewers, even on the same file/line. A quality finding about *how* code is wri
123
128
  address *whether* the code matches the spec. Always keep both. Spec findings CAN still be
124
129
  deduplicated against other spec findings when they are truly the same issue.
125
130
 
126
- ## Step 5a: Respond to Findings (when `review_loop_enforcement` is enabled)
131
+ {{IF:review_loop_enforcement}}
132
+
133
+ ## Step 5a: Respond to Findings
127
134
 
128
135
  For each finding from step 4, do ONE of:
129
136
 
@@ -156,29 +163,38 @@ Findings that were fixed in code → verify the fix, do not re-raise if correct.
156
163
  - If a finding was fixed → verify the fix is correct, don't re-raise
157
164
  - If a finding was explained with a valid technical reason → accept it, don't re-raise
158
165
  - If the explanation is wrong or the fix is incomplete → re-raise with specific pushback
159
-
160
- **When `review_loop_enforcement` is disabled:** Step 5a is optional. Single review pass
161
- is sufficient — fix what you agree with, skip what you don't. No need to explain skips.
166
+ {{/IF}}
162
167
 
163
168
  ## Key Rules
164
169
 
165
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}}
166
173
  When `review_loop_enforcement` is enabled: loop, respond to each finding (fix or explain), and re-run all 6 from step 2
167
174
  until clean or the cycle cap is reached — see "Cycle Definition & Max Cycles" above for the exact stop conditions.
168
- 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}}
169
179
  Minor test/config-only fixes skip re-review (go to step 5); substantive code changes require full re-review.
170
180
 
171
181
  ## Test Tracking
172
182
 
173
- Test results are tracked in `pi-config-review-state.json` via the `tests_passed` field.
174
- 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}}
175
191
 
176
192
  **How `tests_passed` gets set:**
177
193
 
178
194
  - **Bash hook detection:** When any agent runs a test command (`pytest`, `npm test`, `npx tsx --test`, `tox`, `go test`, `vitest`, `jest`, `mocha`),
179
195
  the enforcement hook detects it and auto-marks `tests_passed` based on exit code.
180
196
  - **Agent completion:** When `test-automator` or `test-runner` agents complete, their result auto-marks `tests_passed`.
181
- - **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` —
182
198
  preventing stale results.
183
199
 
184
200
  **Duplicate test run avoidance:** If a specialist already ran tests before the review loop, `tests_passed` may already be `true`.
@@ -220,20 +236,26 @@ If both `git apply` methods fail, skip baseline comparison and note "baseline co
220
236
  For automated review flows (autorabbit, autoqodo), use **two-stage order** instead of parallel:
221
237
 
222
238
  1. **Stage 1 — Spec Compliance:** Does code meet requirements? All deliverables implemented? No scope creep?
239
+ {{IF:review_loop_enforcement}}
223
240
  Loop Stage 1 until passed OR the shared cycle cap is reached.
241
+ {{/IF}}
224
242
  2. **Stage 2 — Code Quality:** Quality, security, guidelines adherence.
243
+ {{IF:review_loop_enforcement}}
225
244
  Loop Stage 2 until passed OR the shared cycle cap is reached.
245
+ {{/IF}}
226
246
 
247
+ {{IF:review_loop_enforcement}}
227
248
  **Staged cycle definition:** one **cycle** = one pass of the *current* stage's reviewers (in parallel)
228
249
  → fix/explain that stage's findings → end of cycle. This mirrors the parallel-mode cycle definition
229
250
  (see "Cycle Definition & Max Cycles" above), scoped to one stage's reviewers instead of all 6.
230
251
 
231
252
  **The `review_loop_max_cycles` cap is shared across both stages — one total budget, not one per stage.**
232
253
  Stage 1 and Stage 2 both draw from and increment the same `cycle` counter in
233
- `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
234
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
235
256
  to Stage 2 (covers both **Not fixed** and **Fixed** (verification blocked)). If hit during Stage 2,
236
257
  stop looping Stage 2.
258
+ {{/IF}}
237
259
 
238
260
  Don't polish code that doesn't meet spec — it wastes work.
239
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