@phi-code-admin/phi-code 0.91.0 → 0.92.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.92.1] - 2026-07-12
4
+
5
+ ### Docs
6
+
7
+ - README: document the execution-grounded modes (/plan, /debug, /build, /sandbox,
8
+ the sandbox_run oracle, per-phase model routing, the honest n=13 measurement)
9
+ and the one-command Alibaba Cloud Coding Plan setup — three releases of
10
+ features were undocumented.
11
+
12
+ ## [0.92.0] - 2026-07-11
13
+
14
+ ### Fixed — the two defects the n=13 SWE-bench measurement exposed
15
+
16
+ The measured head-to-head (baseline 7/13 vs /debug 6/13, official harness) traced
17
+ /debug's two lost points to concrete, reparable defects — both fixed here.
18
+
19
+ - **Hasty BLOCKED (sympy-11897, halted at 51s).** The generic /debug//build
20
+ driver now routes phase completion through the same pure decision machine as
21
+ /plan (`decidePhaseTransition`), gaining what it measurably lacked: a fatal
22
+ 401 stops the run, a transient provider error (timeout / 5xx / 429 / broken
23
+ stream) retries the SAME phase once on the fallback model instead of being
24
+ swallowed, and — new — a BLOCKED verdict gets ONE second chance on the
25
+ fallback model (a different family re-checks the reproduction) before the
26
+ halt. A confirmed second BLOCKED still halts honestly: no fabricated fix.
27
+ REPRODUCE's instructions now state BLOCKED is a last resort — adjust and
28
+ retry once before concluding.
29
+ - **Timeout-blind VERIFY (sympy-11870, fix passed its repro but hung the real
30
+ suite for 900s).** VERIFY's instructions now treat a TIMEOUT of the
31
+ reproduction or the suite as a FAILURE of the candidate — a massive slowdown
32
+ or infinite loop IS a regression — with verdict BLOCKED, never FIXED. A
33
+ dramatic duration increase below the timeout is flagged as a red flag too.
34
+
35
+ 8 integration tests cover the new behaviours (BLOCKED retry → honest halt,
36
+ BLOCKED retry → proceed on success, transient-error retry); full suite green
37
+ (1439).
38
+
3
39
  ## [0.91.0] - 2026-07-11
4
40
 
5
41
  ### Added — Alibaba Cloud Coding Plan, first-class and one-command
package/README.md CHANGED
@@ -16,6 +16,7 @@ phi-code runs in four modes: interactive, print or JSON, RPC for process integra
16
16
 
17
17
  - [Quick Start](#quick-start)
18
18
  - [Providers & Models](#providers--models)
19
+ - [Execution-Grounded Modes (/plan, /debug, /build, /sandbox)](#execution-grounded-modes)
19
20
  - [Interactive Mode](#interactive-mode)
20
21
  - [Editor](#editor)
21
22
  - [Commands](#commands)
@@ -103,13 +104,59 @@ For each built-in provider, phi maintains a list of tool-capable models, updated
103
104
  - Xiaomi MiMo Token Plan (China)
104
105
  - Xiaomi MiMo Token Plan (Amsterdam)
105
106
  - Xiaomi MiMo Token Plan (Singapore)
107
+ - Alibaba Cloud Coding Plan (`qwen3.7-plus`, GLM, Kimi, MiniMax — subscription quota)
106
108
 
107
109
  See [docs/providers.md](docs/providers.md) for detailed setup instructions.
108
110
 
111
+ **One-command setup for known providers:** `/keys set alibaba-codingplan sk-sp-…` (or `opencode-go <key>`) bootstraps the full provider entry — endpoint, protocol, model list — from just the API key, then validates it with `/keys test <id>`. The live catalog refreshes on session start.
112
+
109
113
  **Custom providers & models:** Add providers via `~/.phi/agent/models.json` if they speak a supported API (OpenAI, Anthropic, Google). For custom APIs or OAuth, use extensions. See [docs/models.md](docs/models.md) and [docs/custom-provider.md](docs/custom-provider.md).
110
114
 
111
115
  ---
112
116
 
117
+ ## Execution-Grounded Modes
118
+
119
+ Phi's multi-phase modes share one principle, learned the hard way on SWE-bench:
120
+ **execution is the only oracle**. A verdict must come from running real code —
121
+ never from a model reviewing its own output (which we measured approving wrong
122
+ code, even across model families). Full design:
123
+ [docs/design/plan-debug-build.md](docs/design/plan-debug-build.md).
124
+
125
+ | Command | Job | Pipeline |
126
+ |---|---|---|
127
+ | `/plan <spec>` | Plan AND build a project | EXPLORE → PLAN → CODE → TEST → REVIEW |
128
+ | `/debug <failing test \| repro \| description>` | Turn a REAL failure green | REPRODUCE → LOCALIZE → FIX → VERIFY |
129
+ | `/build <spec>` | Build until it runs | EXPLORE → PLAN → CODE → BUILD-VERIFY (run recipe + acceptance + executable red-team) |
130
+ | `/sandbox [status\|prepare\|run <cmd>]` | Inspect the guaranteed environment | — |
131
+
132
+ **The execution sandbox.** Every oracle run goes through the `sandbox_run` tool:
133
+ a per-project Docker container (toolchain auto-detected — node/python/go/rust/ruby
134
+ or your `Dockerfile`; override anything in `.phi/sandbox.json`) that returns the
135
+ REAL exit code. No Docker and nothing to containerize → runs fall back to the
136
+ host; if an environment is demanded but unavailable, phases report `BLOCKED`
137
+ rather than fabricate a pass.
138
+
139
+ **Honesty guarantees** (each one exists because we measured its absence losing):
140
+ - `/debug` refuses to "fix" a failure it cannot reproduce — and a BLOCKED verdict
141
+ gets one second chance on a different model family before the halt.
142
+ - VERIFY requires the reproduction to pass AND the suite not to regress; a
143
+ TIMEOUT counts as a regression (a fix that hangs the suite is worse than none).
144
+ - `/build` reports `SUCCESS` only when a real run meets the acceptance criteria,
145
+ otherwise an honest `PARTIAL` listing what still fails.
146
+
147
+ **Per-phase model routing.** Each phase can run on its own model via
148
+ `~/.phi/agent/routing.json` (e.g. code on `alibaba-codingplan/qwen3.7-plus`,
149
+ verification on a different family so it does not share the coder's blind spot),
150
+ with automatic fallback on transient provider errors.
151
+
152
+ **Measured, honestly:** on a 13-instance SWE-bench-lite slice (official harness),
153
+ a strong single-shot baseline resolved 7/13 vs `/debug` 6/13 at ~2.5× the time —
154
+ with `/debug` producing smaller patches and zero toxic ones. These modes are
155
+ shipped as *tools with honest failure modes*, not as a claimed benchmark win;
156
+ the measurement lives in the repo and is re-runnable.
157
+
158
+ ---
159
+
113
160
  ## Interactive Mode
114
161
 
115
162
  <p align="center"><img src="docs/images/interactive-mode.png" alt="Interactive Mode" width="600"></p>
@@ -284,6 +284,11 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
284
284
  // Set true once this phase has been retried once (transient proxy failure /
285
285
  // timeout / 0-tool-call). Hard cap of one retry per phase.
286
286
  retried?: boolean;
287
+ // Generic driver (/debug, /build) only: set true once this phase was re-run
288
+ // after a BLOCKED verdict. One second chance on the fallback model — a hasty
289
+ // BLOCKED (measured: 51s on sympy-11897) is cheaper to re-check than to
290
+ // trust — then the halt is honest.
291
+ blockedRetried?: boolean;
287
292
  // When true, switchModelForPhase resolves the fallback model first (used on retry
288
293
  // to swap to a different model family rather than re-hit the one that just failed).
289
294
  useFallback?: boolean;
@@ -1323,13 +1328,63 @@ Tag the note with relevant keywords for vector search.
1323
1328
  phaseTimeoutId = null;
1324
1329
  }
1325
1330
 
1326
- // A BLOCKED verdict (REPRODUCE cannot reproduce / VERIFY could not confirm)
1327
- // halts the pipeline honestly no fabricated success.
1331
+ // Route through the SAME pure decision machine as /plan so the generic
1332
+ // driver gets the protections it was measured to lack (sympy-11897, a 51s
1333
+ // hasty halt): fatal 401 stop, one retry on a transient provider error.
1328
1334
  const structuredForThisPhase = currentPhaseResultKey === (currentPhase?.key ?? null) ? currentPhaseResult : null;
1329
1335
  const outcome = resolvePhaseOutcome(structuredForThisPhase, null);
1330
- if (outcome.verdict === "BLOCKED" && currentPhase) {
1336
+ const decision = decidePhaseTransition({
1337
+ analysis,
1338
+ phase: currentPhase ? { key: currentPhase.key, retried: currentPhase.retried } : null,
1339
+ verdict: outcome.verdict,
1340
+ // The generic driver has no review-fix cycle; pass the cap as spent.
1341
+ reviewFixRounds: 1,
1342
+ maxToolCallsPerPhase: MAX_TOOL_CALLS_PER_PHASE,
1343
+ });
1344
+
1345
+ if (decision.action === "stop") {
1346
+ ctx.ui.notify(
1347
+ `\n❌ **/${orchestrationMode} aborted:** API authentication error (401). Check your API key.`,
1348
+ "error",
1349
+ );
1350
+ finishGenericOrchestration(ctx, `❌ **/${orchestrationMode} aborted (auth error).**`);
1351
+ return;
1352
+ }
1353
+
1354
+ if (decision.action === "retry-fallback" && currentPhase) {
1355
+ // Transient provider failure (timeout / 5xx / 429 / broken stream): retry
1356
+ // the SAME phase once on the fallback model before treating any of its
1357
+ // output as a real outcome.
1358
+ currentPhase.retried = true;
1359
+ currentPhase.useFallback = true;
1360
+ phaseQueue.unshift(currentPhase);
1361
+ phasePending = false;
1362
+ ctx.ui.notify(
1363
+ `\n🔁 **Transient provider error** in ${currentPhase.label} — retrying once on the fallback model.`,
1364
+ "warning",
1365
+ );
1366
+ sendNextGenericPhase(ctx);
1367
+ return;
1368
+ }
1369
+
1370
+ // BLOCKED (REPRODUCE cannot reproduce / VERIFY could not confirm): give the
1371
+ // phase ONE second chance on the fallback model — a hasty BLOCKED is cheaper
1372
+ // to re-check than to trust — then halt honestly. No fabricated success.
1373
+ if (decision.action === "pause-blocked" && currentPhase) {
1374
+ if (!currentPhase.blockedRetried) {
1375
+ currentPhase.blockedRetried = true;
1376
+ currentPhase.useFallback = true;
1377
+ phaseQueue.unshift(currentPhase);
1378
+ phasePending = false;
1379
+ ctx.ui.notify(
1380
+ `\n🔁 **${currentPhase.label} reported BLOCKED on the first attempt** — retrying once on the fallback model before halting. ${outcome.handoff || ""}`,
1381
+ "warning",
1382
+ );
1383
+ sendNextGenericPhase(ctx);
1384
+ return;
1385
+ }
1331
1386
  ctx.ui.notify(
1332
- `\n⏸️ **${currentPhase.label} reported BLOCKED.** ${outcome.handoff || "No reproducible/verifiable result."}`,
1387
+ `\n⏸️ **${currentPhase.label} reported BLOCKED (confirmed on retry).** ${outcome.handoff || "No reproducible/verifiable result."}`,
1333
1388
  "warning",
1334
1389
  );
1335
1390
  finishGenericOrchestration(ctx, `⏸️ **/${orchestrationMode} stopped: BLOCKED at ${currentPhase.label}.**`);
@@ -59,7 +59,8 @@ ${failing}
59
59
  - If it PASSES → STOP. Write \`BLOCKED: cannot reproduce — passes on current code\` with the run pasted. Do not invent a bug.
60
60
  - If \`sandbox_run\` reports \`SANDBOX UNAVAILABLE\` → STOP. Write \`BLOCKED: no executable environment\`.
61
61
  4. Do NOT edit any source yet. This phase only observes.
62
- 5. **Last action:** call \`phase_result\` \`verdict: PASS\` if it reproduced (proceed to LOCALIZE), or \`verdict: BLOCKED\` with the reason if you stopped.` +
62
+ 5. **BLOCKED is a last resort, not a first reaction.** If a run fails for an infrastructure-looking reason (tool error, missing file you guessed wrong, transient failure), adjust and retry at least once — e.g. locate the real test paths, try an alternative reproduction — before concluding. Only report BLOCKED after a genuine attempt showed the state is not reproducible or not runnable.
63
+ 6. **Last action:** call \`phase_result\` — \`verdict: PASS\` if it reproduced (proceed to LOCALIZE), or \`verdict: BLOCKED\` with the reason if you stopped.` +
63
64
  DEBUG_RULES,
64
65
 
65
66
  localize: `You are the LOCALIZE agent (phase 2 of /debug). Drive from the REAL symptom the previous phase captured.
@@ -96,6 +97,7 @@ ${failing}
96
97
  3. Verdict (from what \`sandbox_run\` returned, not from inspection):
97
98
  - Both green → \`FIXED\`, and paste the before(fail)/after(pass) reproduction runs and the green suite as evidence.
98
99
  - Reproduction still fails, or the suite regresses → \`BLOCKED\` with the closest diagnostic. Do NOT ship the least-bad patch; a wrong fix is worse than an honest BLOCKED.
100
+ - **A TIMEOUT counts as a FAILURE.** If the reproduction or the suite TIMES OUT with the fix applied (\`sandbox_run\` verdict TIMEOUT), the fix has introduced a massive slowdown or an infinite loop — that IS a regression (measured: a fix that passed its repro but made the real test suite hang for 900s). Verdict \`BLOCKED\`, never \`FIXED\`. Compare rough durations before/after: a check that got dramatically slower is a red flag even below the timeout.
99
101
  4. Write the final verdict block, then call \`phase_result\` with \`verdict: PASS\` (FIXED) or \`verdict: BLOCKED\`, plus a one-line handoff:
100
102
  \`\`\`
101
103
  VERDICT: FIXED | BLOCKED
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.91.0",
3
+ "version": "0.92.1",
4
4
  "description": "Coding agent CLI with persistent memory, sub-agents, intelligent routing, and orchestration",
5
5
  "type": "module",
6
6
  "piConfig": {