@phi-code-admin/phi-code 0.93.1 → 0.94.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.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.94.0] - 2026-07-12
|
|
4
|
+
|
|
5
|
+
### Fixed — three defects the run telemetry caught live
|
|
6
|
+
|
|
7
|
+
The .phi/runs.jsonl telemetry (0.93.0) paid for itself on its first outing: it
|
|
8
|
+
showed /fix shots being killed mid-work by the flat 10-min phase cap (601s run,
|
|
9
|
+
empty phases, lost REPRO-CMD handoff), oracles going blind after those aborts,
|
|
10
|
+
and escalations burning ~10 min re-confirming a reproduction the driver itself
|
|
11
|
+
had just executed.
|
|
12
|
+
|
|
13
|
+
- **Per-phase timeout** (OrchestratorPhase.timeoutMs): the /fix single shot now
|
|
14
|
+
gets 25 min (measured: real shots run 8-18 min). The FIRST phase of a generic
|
|
15
|
+
run also gains the standard retry-on-fallback before being skipped (it was
|
|
16
|
+
skip-direct).
|
|
17
|
+
- **Oracle fallback reproduction**: when the shot's REPRO-CMD handoff was lost
|
|
18
|
+
but the conventional repro_issue.py exists, the oracle runs it instead of
|
|
19
|
+
finishing UNVERIFIED blind.
|
|
20
|
+
- **Escalation skips REPRODUCE**: the oracle just executed the red reproduction;
|
|
21
|
+
/fix now goes straight to LOCALIZE -> FIX -> VERIFY with the red run seeded,
|
|
22
|
+
saving ~10 min under tight budgets.
|
|
23
|
+
|
|
24
|
+
Full suite green (1472).
|
|
25
|
+
|
|
26
|
+
## [0.93.2] - 2026-07-12
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- /fix: a single shot that changed NOTHING (text-only reply, zero edits — a
|
|
31
|
+
measured 91s failure mode) no longer ends UNVERIFIED; in a git repo with a
|
|
32
|
+
clean tree it ESCALATES to the full pipeline (nothing to verify = the work
|
|
33
|
+
was not done). The shot instruction now states that acting with tools is
|
|
34
|
+
mandatory and a textual plan forfeits the attempt. +1 integration test.
|
|
35
|
+
|
|
3
36
|
## [0.93.1] - 2026-07-12
|
|
4
37
|
|
|
5
38
|
### Fixed
|
|
@@ -307,6 +307,11 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
|
|
|
307
307
|
// 0..4 for the five base phases (explore..review); undefined for synthetic
|
|
308
308
|
// phases (the review fix + re-review). Used to checkpoint resume position.
|
|
309
309
|
baseIndex?: number;
|
|
310
|
+
// Per-phase timeout override (generic driver only). Defaults to
|
|
311
|
+
// MAX_PHASE_DURATION_MS. Measured need: a /fix single shot legitimately
|
|
312
|
+
// runs 8–18 min — the flat 10 min cap killed it mid-work and lost its
|
|
313
|
+
// REPRO-CMD handoff (telemetry: 601s run, phases empty, UNVERIFIED).
|
|
314
|
+
timeoutMs?: number;
|
|
310
315
|
}
|
|
311
316
|
|
|
312
317
|
let phaseQueue: OrchestratorPhase[] = [];
|
|
@@ -1360,10 +1365,34 @@ Tag the note with relevant keywords for vector search.
|
|
|
1360
1365
|
fixContext.oracleRan = true;
|
|
1361
1366
|
const cwd = ctx.cwd || process.cwd();
|
|
1362
1367
|
const sandbox = getSessionSandbox(cwd);
|
|
1363
|
-
|
|
1368
|
+
let reproCmd =
|
|
1364
1369
|
fixContext.state.failingTest?.trim() || fixContext.state.reproCommand?.trim() || fixContext.reproFromShot;
|
|
1370
|
+
// Conventional fallback: the shot is instructed to write `repro_issue.py`.
|
|
1371
|
+
// If its handoff was lost (phase timeout/abort) but the file exists, the
|
|
1372
|
+
// oracle can still run it instead of going blind (telemetry-measured gap).
|
|
1373
|
+
if (!reproCmd && existsSync(join(cwd, "repro_issue.py"))) {
|
|
1374
|
+
reproCmd = "python repro_issue.py";
|
|
1375
|
+
ctx.ui.notify(
|
|
1376
|
+
`\n🧷 Oracle fallback: found \`repro_issue.py\` — using \`${reproCmd}\` as the reproduction.`,
|
|
1377
|
+
"info",
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1365
1380
|
const suiteCmd = sandbox.recipe.test?.trim();
|
|
1366
1381
|
|
|
1382
|
+
// A shot that changed NOTHING has nothing to verify — "UNVERIFIED" would be
|
|
1383
|
+
// a lie of omission (measured: a text-only 91s shot ended UNVERIFIED with
|
|
1384
|
+
// zero edits). In a git repo with a clean diff, escalate to the full
|
|
1385
|
+
// pipeline instead: the work simply was not done.
|
|
1386
|
+
const inGitRepo = passed(runCommand("git rev-parse --is-inside-work-tree", { cwd, timeoutMs: 15_000 }));
|
|
1387
|
+
if (inGitRepo && !gitIn(cwd, "diff").trim() && !gitIn(cwd, "status --porcelain").trim()) {
|
|
1388
|
+
ctx.ui.notify(
|
|
1389
|
+
`\n🔺 **/fix escalating: the single shot made NO changes** — nothing to verify, the full /debug pipeline takes over.`,
|
|
1390
|
+
"warning",
|
|
1391
|
+
);
|
|
1392
|
+
phaseQueue.push(...buildDebugPhases(fixContext.state));
|
|
1393
|
+
return false; // dispatch REPRODUCE
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1367
1396
|
if (!sandbox.available() && (reproCmd || suiteCmd)) {
|
|
1368
1397
|
ctx.ui.notify(
|
|
1369
1398
|
`\n⚠️ **/fix oracle: sandbox unavailable** (${sandbox.reason}) — the single shot cannot be verified. Treating it as UNVERIFIED.`,
|
|
@@ -1395,13 +1424,16 @@ Tag the note with relevant keywords for vector search.
|
|
|
1395
1424
|
);
|
|
1396
1425
|
return true;
|
|
1397
1426
|
}
|
|
1398
|
-
// escalate: the pipeline inherits the RED run as its concrete failing
|
|
1427
|
+
// escalate: the pipeline inherits the RED run as its concrete failing
|
|
1428
|
+
// state. REPRODUCE is SKIPPED — the oracle just executed the reproduction
|
|
1429
|
+
// and it is red; re-confirming it would burn ~10 min re-running what the
|
|
1430
|
+
// driver already ran (measured: escalations were dying on outer budgets).
|
|
1399
1431
|
ctx.ui.notify(
|
|
1400
|
-
`\n🔺 **/fix escalating
|
|
1432
|
+
`\n🔺 **/fix escalating** — ${decision.diagnostic}. The reproduction is already established (the oracle just ran it); going straight to LOCALIZE → FIX → VERIFY.`,
|
|
1401
1433
|
"warning",
|
|
1402
1434
|
);
|
|
1403
|
-
phaseQueue.push(...buildDebugPhases(decision.failing));
|
|
1404
|
-
return false; // fall through: sendNextGenericPhase
|
|
1435
|
+
phaseQueue.push(...buildDebugPhases(decision.failing).slice(1));
|
|
1436
|
+
return false; // fall through: sendNextGenericPhase dispatches LOCALIZE
|
|
1405
1437
|
}
|
|
1406
1438
|
|
|
1407
1439
|
// Multi-candidate arbitration — the oracle disposes. Apply each captured
|
|
@@ -1508,6 +1540,7 @@ Tag the note with relevant keywords for vector search.
|
|
|
1508
1540
|
if (warning) ctx.ui.notify(`\n⚠️ ${warning}`, "warning");
|
|
1509
1541
|
setTimeout(() => pi.sendUserMessage(phase.instruction, { deliverAs: "followUp" }), 500);
|
|
1510
1542
|
if (phaseTimeoutId) clearTimeout(phaseTimeoutId);
|
|
1543
|
+
const phaseBudget = phase.timeoutMs ?? MAX_PHASE_DURATION_MS;
|
|
1511
1544
|
phaseTimeoutId = setTimeout(() => {
|
|
1512
1545
|
if (orchestrationActive && phasePending) {
|
|
1513
1546
|
phasePending = false;
|
|
@@ -1522,7 +1555,7 @@ Tag the note with relevant keywords for vector search.
|
|
|
1522
1555
|
phase.useFallback = true;
|
|
1523
1556
|
phaseQueue.unshift(phase);
|
|
1524
1557
|
ctx.ui.notify(
|
|
1525
|
-
`\n⏰ **Phase timed out** (${
|
|
1558
|
+
`\n⏰ **Phase timed out** (${Math.round(phaseBudget / 60000)} min) — retrying once on the fallback model.`,
|
|
1526
1559
|
"warning",
|
|
1527
1560
|
);
|
|
1528
1561
|
} else {
|
|
@@ -1531,7 +1564,7 @@ Tag the note with relevant keywords for vector search.
|
|
|
1531
1564
|
}
|
|
1532
1565
|
sendNextGenericPhase(ctx);
|
|
1533
1566
|
}
|
|
1534
|
-
},
|
|
1567
|
+
}, phaseBudget);
|
|
1535
1568
|
});
|
|
1536
1569
|
}
|
|
1537
1570
|
|
|
@@ -1729,6 +1762,7 @@ Tag the note with relevant keywords for vector search.
|
|
|
1729
1762
|
ctx.ui.notify(`\n${first.label} → \`${modelId}\``, "info");
|
|
1730
1763
|
if (warning) ctx.ui.notify(`\n⚠️ ${warning}`, "warning");
|
|
1731
1764
|
if (phaseTimeoutId) clearTimeout(phaseTimeoutId);
|
|
1765
|
+
const firstBudget = first.timeoutMs ?? MAX_PHASE_DURATION_MS;
|
|
1732
1766
|
phaseTimeoutId = setTimeout(() => {
|
|
1733
1767
|
if (orchestrationActive && phasePending) {
|
|
1734
1768
|
phasePending = false;
|
|
@@ -1738,11 +1772,24 @@ Tag the note with relevant keywords for vector search.
|
|
|
1738
1772
|
} catch {
|
|
1739
1773
|
/* best effort */
|
|
1740
1774
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1775
|
+
// Same policy as every other phase: one retry on the fallback
|
|
1776
|
+
// model before skipping (the old skip-direct lost the whole run
|
|
1777
|
+
// when the very first phase was slow).
|
|
1778
|
+
if (!first.retried) {
|
|
1779
|
+
first.retried = true;
|
|
1780
|
+
first.useFallback = true;
|
|
1781
|
+
phaseQueue.unshift(first);
|
|
1782
|
+
ctx.ui.notify(
|
|
1783
|
+
`\n⏰ **First phase timed out** (${Math.round(firstBudget / 60000)} min) — retrying once on the fallback model.`,
|
|
1784
|
+
"warning",
|
|
1785
|
+
);
|
|
1786
|
+
} else {
|
|
1787
|
+
skippedPhases++;
|
|
1788
|
+
ctx.ui.notify(`\n⏰ **First phase timed out again** — skipping.`, "warning");
|
|
1789
|
+
}
|
|
1743
1790
|
sendNextGenericPhase(ctx);
|
|
1744
1791
|
}
|
|
1745
|
-
},
|
|
1792
|
+
}, firstBudget);
|
|
1746
1793
|
setTimeout(() => pi.sendUserMessage(first.instruction, { deliverAs: "followUp" }), 200);
|
|
1747
1794
|
});
|
|
1748
1795
|
}
|
|
@@ -2305,6 +2352,9 @@ With no runnable check at all, the result is honestly labelled UNVERIFIED.`,
|
|
|
2305
2352
|
await ensurePlansDir();
|
|
2306
2353
|
fixContext = { state, oracleRan: false };
|
|
2307
2354
|
const shot = genericPhase("shot", "🎯 Phase 1 — SINGLE SHOT", "code", "code", singleShotInstruction(state));
|
|
2355
|
+
// A real single shot legitimately runs 8–18 min (measured on the
|
|
2356
|
+
// baselines); the flat 10 min phase cap was killing it mid-work.
|
|
2357
|
+
shot.timeoutMs = 25 * 60 * 1000;
|
|
2308
2358
|
startGenericOrchestration(
|
|
2309
2359
|
"fix",
|
|
2310
2360
|
[shot],
|
|
@@ -126,6 +126,8 @@ export function singleShotInstruction(state: FailingState): string {
|
|
|
126
126
|
**The problem:**
|
|
127
127
|
${failing}
|
|
128
128
|
|
|
129
|
+
**You must ACT in this turn — call the read/edit/write tools and actually change the code. A textual plan or analysis with no edits counts as a FAILED shot (measured failure mode) and forfeits your attempt to the full pipeline.**
|
|
130
|
+
|
|
129
131
|
**Do exactly this:**
|
|
130
132
|
1. Read the relevant code and locate the root cause.
|
|
131
133
|
2. Make the smallest change that addresses it. Do NOT edit tests. Every added guard/branch is a liability.
|