@phi-code-admin/phi-code 0.88.0 → 0.90.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,99 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.90.0] - 2026-07-11
4
+
5
+ ### Added — the execution sandbox (the guaranteed oracle)
6
+
7
+ This is the infrastructure the 0.89.0 design (`docs/design/plan-debug-build.md`)
8
+ called its "real investment": it turns `/debug` and `/build` from prompt-enforced
9
+ discipline into a **guaranteed oracle**. The agent can no longer merely claim it
10
+ ran the test — a real container runs it and returns the true exit code.
11
+
12
+ - **`providers/sandbox-plan.ts` (pure, 29 tests)** — detects the project toolchain
13
+ (node / python / go / rust / ruby / Dockerfile), builds an environment recipe
14
+ (base image, dependency-install, test command, env), decides the backend
15
+ **honestly**, and constructs the `docker run` argv. The Windows path/quoting
16
+ pitfalls that defeated the earlier SWE-bench harness (bind-mount mangling,
17
+ stdlib shadowing) are handled here and unit-tested: `-v C:/…:/work`,
18
+ `PYTHONSAFEPATH=1`.
19
+ - **`providers/sandbox.ts` (IO shell, 8 tests incl. a real-Docker smoke)** — the
20
+ `Sandbox` interface with three backends: `docker` (guaranteed environment),
21
+ `local` (real runs, but not dependency-guaranteed), and `unavailable` (honest —
22
+ every exec reports `SANDBOX UNAVAILABLE` so the modes emit `BLOCKED`, never a
23
+ fabricated pass). Ephemeral container, host bind-mount, so a `/debug` fix lands
24
+ on disk and the next run sees it. Docker is demanded-but-absent → `unavailable`,
25
+ never a silent downgrade.
26
+ - **`sandbox_run` tool** — exposed to the `/debug` and `/build` phase agents. The
27
+ reproduction, the suite, and acceptance/red-team checks run through it; the
28
+ verdict is tied to its structured result (`passed`, real `exitCode`), not to the
29
+ model's prose. `execution.ts` gained `runArgv` (no-shell argv spawn) for safe
30
+ `docker` invocation.
31
+ - **`/sandbox` command** — `status` (detected toolchain, backend, recipe),
32
+ `prepare` (pull image / build Dockerfile / install deps), `run <cmd>` (one-off).
33
+ A `.phi/sandbox.json` overrides everything (image, setup, test, backend, memory,
34
+ cpus, network).
35
+
36
+ 50 new tests (1425 total, green). The `/debug` and `/build` phase instructions now
37
+ route every oracle run through `sandbox_run` and downgrade to `BLOCKED` when the
38
+ sandbox is unavailable.
39
+
40
+ ### Still honest
41
+
42
+ The sandbox makes the thesis **measurable** — it is the missing piece that let the
43
+ model self-grade before. It does not by itself prove `/debug` beats a single shot;
44
+ that is now a runnable measurement (containerized SWE-bench-lite with `sandbox_run`
45
+ as the oracle), and it is the next step, not a claim made here.
46
+
47
+ ## [0.89.0] - 2026-07-11
48
+
49
+ ### Added
50
+
51
+ - **Two new execution-grounded commands, `/debug` and `/build`**, implementing
52
+ the design in `docs/design/plan-debug-build.md`. They exist because the
53
+ SWE-bench-lite head-to-head measured that `/plan`'s TEST/REVIEW phases approve
54
+ wrong code — they grade the model's own reconstruction, not a real run. These
55
+ modes move the burden of proof from opinion to **execution**.
56
+ - **`/debug <failing state>`** — turn a REAL failure green through
57
+ `REPRODUCE → LOCALIZE → FIX → VERIFY`. It refuses to guess: if the failure
58
+ does not reproduce on the current code, or cannot be run at all, it stops
59
+ with `BLOCKED` instead of fabricating a fix. `VERIFY` requires two real runs
60
+ (the reproduction now passes AND the suite does not regress) before it will
61
+ ever say `FIXED`. Each phase routes to a model that does not share the
62
+ coder's blind spot (`routing.json`).
63
+ - **`/build <spec>`** — `/plan`'s decomposition (`EXPLORE → PLAN → CODE`) plus
64
+ an execution-grounded `BUILD-VERIFY`: run the recipe, check acceptance
65
+ criteria derived from the spec, run an **executable** red-team against the
66
+ input regime the change touched (a runnable test that goes red, never a
67
+ critique), and route every real failure to the `/debug` protocol. It reports
68
+ `SUCCESS` only when a real run meets the acceptance criteria, otherwise an
69
+ honest `PARTIAL` that lists what still fails.
70
+ - **Six pure, unit-tested decision cores** backing the above (no live model, no
71
+ fs at the boundary): `execution.ts` (the run oracle: `runCommand`/`passed`),
72
+ `acceptance.ts` (executable acceptance criteria; manual criteria are never
73
+ counted as passing), `debug-contract.ts` (`decideReproduce`/`decideVerify` —
74
+ BLOCKED rather than least-bad), `triage.ts` (adaptive depth — pay the pipeline
75
+ cost only when earned), `redteam.ts` (a break is recorded only when a test
76
+ actually ran red), and `build-loop.ts` (`decideBuildRound` — SUCCESS /
77
+ CONTINUE-to-/debug / honest PARTIAL). 73 new tests, including an end-to-end
78
+ integration test of the `/debug` and `/build` chain against a simulated Pi
79
+ runtime.
80
+
81
+ ### Changed
82
+
83
+ - The orchestrator now carries an `orchestrationMode`. `/debug` and `/build` run
84
+ on a **separate linear driver** and never engage `/plan`'s review-fix cycle or
85
+ its five-phase bookkeeping, so the existing `/plan` path is byte-for-byte
86
+ unchanged (its integration suite still passes).
87
+
88
+ ### Honest caveat
89
+
90
+ - These modes are the protocol + scaffolding. Their value depends on a real
91
+ executable environment; on a host that cannot run the target they downgrade to
92
+ `BLOCKED` rather than a fabricated pass. They are **not yet benchmarked** to
93
+ beat a single shot — per the design doc, that measurement (which needs a
94
+ per-project execution sandbox) is the next step, and no claim is made here that
95
+ they do.
96
+
3
97
  ## [0.88.0] - 2026-07-10
4
98
 
5
99
  ### Changed
@@ -0,0 +1,283 @@
1
+ # Design: /plan, /debug, /build — composable, execution-grounded modes
2
+
3
+ Status: proposed (2026-07-11)
4
+ Informed by: the SWE-bench-lite head-to-head (ADR 0001/0002 and evals/), which
5
+ measured that a 5-phase, deliberation-heavy /plan ties or loses to a single shot
6
+ at 6–14× the cost on bug-fixing, and — decisively — that its TEST/REVIEW phases
7
+ approve wrong code because they grade the model's own reconstruction, not a real
8
+ run. Multi-model diversity did NOT fix this: a different family (deepseek)
9
+ independently endorsed the same wrong guard, because the error was a *shared
10
+ plausible misconception*, not a model-specific blind spot.
11
+
12
+ The one thing that would have caught it was running the real test. So this design
13
+ moves the burden of proof from the model to **execution**, and splits the work
14
+ into three composable commands.
15
+
16
+ ## Principles (each is a measured lesson, not a preference)
17
+
18
+ 1. **Execution is the only oracle.** Every accept/reject decision must be backed
19
+ by running real code — the existing test suite, a reproduction, or the built
20
+ app — not by a model reviewing its own output. Opinion (even adversarial,
21
+ even multi-model) shares misconceptions; a red test run does not.
22
+ 2. **Adversarial means executable.** "Challenge the fix" must produce a *failing
23
+ run*, never a critique. A run is objective; prose is not.
24
+ 3. **Pay complexity only when earned.** A one-line change must not trigger a
25
+ five-phase pipeline. Depth adapts to task size/type, decided by cheap signals.
26
+ 4. **Diversity proposes, the oracle disposes.** Multiple models are useful for
27
+ generating *candidate fixes*, filtered by real execution — not for casting
28
+ *review votes*, which correlate.
29
+ 5. **/plan is for building; /debug is for fixing.** They are different jobs with
30
+ different oracles. Do not conflate them; compose them.
31
+
32
+ ## The three commands
33
+
34
+ | Command | Job | Oracle | Standalone use |
35
+ |---|---|---|---|
36
+ | `/plan <spec>` | Build from a description (decompose → implement) | Acceptance criteria derived from the spec + a smoke run | "Scaffold / build this feature" |
37
+ | `/debug <failing-state>` | Turn a real failure green | The failing state + the existing test suite, re-run | "This test/trace is broken, fix it" |
38
+ | `/build <spec>` | Build AND make it actually work | The full loop: run → red-team → debug → re-run | "Build this and don't stop until it runs" |
39
+
40
+ `/build` is the outer loop that composes the two primitives with **execution
41
+ between them**. All three remain usable alone.
42
+
43
+ ---
44
+
45
+ ## /plan — build from a spec
46
+
47
+ Phases (each may route to its own model via routing.json):
48
+
49
+ 1. **EXPLORE** — map the existing code, conventions, entry points. Read-only.
50
+ 2. **PLAN** — architecture + an ordered task list. Each task is a self-contained
51
+ prompt (the prompt-architect skill: `[CONTEXT] → [TASK] → [FORMAT] →
52
+ [CONSTRAINTS]`). This decomposition is /plan's core value on large,
53
+ under-specified builds — the part a single shot cannot hold coherently.
54
+ 3. **CODE** — implement the tasks in dependency order.
55
+ 4. **SELF-CHECK** — the cheapest real signal available: it compiles / typechecks
56
+ / lints, and a smoke run of the entry point does not crash. NOT a substitute
57
+ for verification — just a floor.
58
+
59
+ **Input:** a natural-language spec.
60
+ **Output contract (machine-readable, consumed by /build):**
61
+ ```
62
+ {
63
+ runRecipe: { build?: string, run: string, test?: string, readySignal?: string },
64
+ acceptance: string[], // testable criteria derived from the SPEC (not the code)
65
+ changedFiles: string[],
66
+ selfCheck: "pass" | "fail" | "skipped"
67
+ }
68
+ ```
69
+ The `acceptance` list is the contract: crisp, checkable statements traced to the
70
+ request ("POST /login returns 200 + a JWT for valid creds; 401 otherwise").
71
+ Deriving these from the *spec* — before and independent of the implementation —
72
+ is what keeps verification from becoming circular.
73
+
74
+ Deliberately NOT in /plan: heavy TEST and REVIEW. Verification belongs to the
75
+ execution loop (/build), where it can run for real. Standalone /plan stops at
76
+ SELF-CHECK and hands its output to the user (or to /build).
77
+
78
+ ---
79
+
80
+ ## /debug — turn a real failure green
81
+
82
+ The heart of the redesign. /debug never guesses what is wrong; it is *given* a
83
+ failure and reproduces it.
84
+
85
+ **Input contract — a concrete failing state (at least one):**
86
+ ```
87
+ {
88
+ failingTest?: string, // e.g. "pytest tests/x.py::test_y"
89
+ trace?: string, // a stack trace / error output
90
+ reproCommand?: string, // a command that exhibits the bug
91
+ expected?: string, // what should happen instead (from the user/spec)
92
+ cwd: string
93
+ }
94
+ ```
95
+ If none is runnable, /debug does not fabricate one from imagination — it asks for
96
+ one, or emits `BLOCKED: no reproducible failing state`.
97
+
98
+ Phases:
99
+
100
+ 1. **REPRODUCE** — run the failing state, confirm it fails, capture the exact
101
+ symptom. If it does NOT fail on the current code, stop: `BLOCKED: cannot
102
+ reproduce`. (This alone kills the class of "fixes" for non-bugs.)
103
+ 2. **LOCALIZE** — drive from the real symptom: read the traceback, grep the
104
+ changed/nearby symbols, narrow to the fault site. Localization, not more
105
+ review, is the underrated lever.
106
+ 3. **FIX (generate N candidates)** — produce several minimal candidate patches
107
+ (different models and/or temperatures). Prefer the smallest; every added
108
+ guard/condition is a liability (the 3362 failure was an over-clever guard).
109
+ 4. **VERIFY (the oracle)** — for EACH candidate, run: (a) the reproduced failure
110
+ → must now pass, and (b) the existing test suite → must not regress. Select
111
+ the **minimal candidate that passes both** via `candidate-select.ts`. If none
112
+ passes, emit `BLOCKED` with the closest diagnostic — do NOT ship the least-bad.
113
+
114
+ **Output contract:**
115
+ ```
116
+ { verdict: "FIXED" | "BLOCKED",
117
+ patch?: string, // minimal, verified
118
+ evidence: { reproBefore: "fail", reproAfter: "pass", suite: "green" },
119
+ reason?: string } // when BLOCKED
120
+ ```
121
+ `FIXED` is only ever emitted with a real before/after run pasted. There is no
122
+ path to a green verdict without execution.
123
+
124
+ ---
125
+
126
+ ## /build — build until it runs
127
+
128
+ `/build` = `/plan` then a bounded loop that closes the build→run→fix cycle with
129
+ execution as the glue.
130
+
131
+ ```
132
+ plan = /plan(spec) # code + runRecipe + acceptance[]
133
+ for round in 1..MAX_ROUNDS (budgeted):
134
+ run = execute(plan.runRecipe) # ORACLE #1: does it run + meet acceptance?
135
+ unmet = acceptance criteria not satisfied by `run`
136
+ redTeam = red_team(plan, acceptance) # ORACLE #2: can an adversary break it? (below)
137
+ failures = unmet ∪ redTeam.breakingCases
138
+ if failures is empty: return SUCCESS(plan)
139
+ /debug({ from each failure: failingTest/trace/reproCommand, expected }) # fix REAL failures
140
+ # /debug mutates the working tree; loop re-runs from the top
141
+ return PARTIAL(plan, remaining failures) # honest: what still fails, not a false PASS
142
+ ```
143
+
144
+ Key properties:
145
+ - The `execute` step is the oracle that /plan alone lacked. A criterion is met
146
+ only if the running program demonstrates it.
147
+ - Each `/debug` call is grounded in a **real** failure (a failed acceptance run
148
+ or a red-team breaking case), never in self-review.
149
+ - The loop is **budgeted** and returns `PARTIAL` honestly when it cannot close —
150
+ listing the still-failing criteria — instead of a confident-wrong `PASS`.
151
+
152
+ ---
153
+
154
+ ## The executable red-team protocol
155
+
156
+ This replaces the current REVIEW phase (which we measured as a rubber stamp). The
157
+ adversary's deliverable is a **failing run**, not an opinion.
158
+
159
+ ```
160
+ red_team(code, acceptance, changedFiles):
161
+ dry = 0
162
+ while dry < K and within budget:
163
+ # An adversary agent (ideally a different model family than the coder —
164
+ # different break intuitions) targets the BOUNDARY the change touches.
165
+ attempt = adversary.write_breaking_case(
166
+ focus = changedFiles, # not the whole app
167
+ regimes = enumerate_input_regimes(changedFiles), # e.g. buffered vs streaming,
168
+ # empty, null, boundary, wrong-type
169
+ goal = "make an acceptance criterion or an invariant FALSE, as a runnable test")
170
+ result = execute(attempt) # RUN it — this is the whole point
171
+ if result.green:
172
+ dry += 1 # could not break it this round
173
+ else:
174
+ dry = 0
175
+ record breakingCase(attempt, result.symptom) # a concrete red run
176
+ return breakingCases
177
+ ```
178
+
179
+ Why this works where adversarial *prose* failed (3362): the guard survived a
180
+ different model's careful written review because the reviewer shared the
181
+ misconception. It would NOT have survived `r.raw = io.BytesIO(...);
182
+ assert all(isinstance(c, str) for c in r.iter_content(decode_unicode=True))`
183
+ being *run* — that assertion is red regardless of what any model believes.
184
+ The rule that turns "adversarial" from theatre into signal: **the adversary
185
+ must attack the specific input regime the diff changed, and must express the
186
+ attack as an executed test, not a claim.**
187
+
188
+ ---
189
+
190
+ ## Execution grounding (the non-negotiable prerequisite)
191
+
192
+ Every oracle above requires running real code with the project's real
193
+ dependencies. On a host that cannot run the target (e.g. an old library under a
194
+ too-new Python — exactly what defeated the 3362 measurement), the modes must:
195
+ - run inside a sandbox/container that has the project's real environment, or
196
+ - when no such environment is available, DOWNGRADE honestly: /debug and /build
197
+ emit `BLOCKED: no executable environment` (or a low-confidence draft clearly
198
+ labelled unverified) — never a fabricated-reconstruction `PASS`.
199
+
200
+ Reusing phi's `run` / `verify` skills for local projects is the first target;
201
+ a per-project container is the general solution.
202
+
203
+ **Status (2026-07-11): implemented.** `providers/sandbox-plan.ts` (pure: toolchain
204
+ detection → recipe → backend decision → `docker run` argv) and `providers/sandbox.ts`
205
+ (the `Sandbox` IO shell: `docker` | `local` | `unavailable`) provide the guaranteed
206
+ environment. The `sandbox_run` tool exposes it to the /debug and /build phase agents
207
+ — the reproduction, the suite, and acceptance/red-team checks now run in a real
208
+ container and return its true exit code, so a PASS cannot be asserted, only earned.
209
+ When Docker is absent and nothing is containerizable, the backend is `unavailable`
210
+ and every `sandbox_run` returns `SANDBOX UNAVAILABLE`, which the phase instructions
211
+ turn into `BLOCKED` — never a fabricated pass. The `/sandbox` command
212
+ (`status` | `prepare` | `run`) inspects and provisions it. A `.phi/sandbox.json`
213
+ overrides detection (image, setup, test, backend, resource caps).
214
+
215
+ ---
216
+
217
+ ## Triage / adaptive depth (cost discipline)
218
+
219
+ Cheap up-front classification decides the mode and the depth:
220
+
221
+ | Signal | Route |
222
+ |---|---|
223
+ | Small edit, a known failing test/trace | `/debug` (skip planning entirely) |
224
+ | Small self-contained feature, single shot + verify passes | ship the single shot; skip the loop |
225
+ | Large, multi-file, under-specified build | `/build` (full loop) |
226
+ | A single shot's output already meets acceptance on a real run | done — do not deliberate further |
227
+
228
+ Measured rationale: the 6–14× overhead is only worth paying when a single shot
229
+ *fails the real oracle*. Otherwise the cheapest passing candidate wins.
230
+
231
+ ---
232
+
233
+ ## Model routing
234
+
235
+ Per-phase model assignment stays (routing.json). Corrected use, per the
236
+ measurements:
237
+ - **Candidate generation** (CODE, /debug FIX): diversity helps — different
238
+ models/temperatures produce different candidates, and the oracle filters.
239
+ - **Red-team adversary**: a different family than the coder helps (different
240
+ break intuitions) — but its output is a *run*, so it is robust even to shared
241
+ misconceptions.
242
+ - **Review-by-opinion**: removed. We measured it as correlated and unreliable;
243
+ execution replaces it.
244
+
245
+ ---
246
+
247
+ ## What is reused vs new
248
+
249
+ Reused from today's orchestrator: the phase engine, per-phase model switching,
250
+ the pure phase state machine (`phase-machine.ts`), the structured `phase_result`
251
+ contract, the fix-cycle, `candidate-select.ts` (minimality selection).
252
+
253
+ New work: (1) extract the fix-cycle into a standalone `/debug` with the input
254
+ contract above; (2) make `acceptance[]` + `runRecipe` first-class /plan outputs;
255
+ (3) the `/build` loop; (4) the executable red-team; (5) the execution sandbox and
256
+ the real-run verify path. Items 1–4 are prompt/orchestration changes on existing
257
+ machinery; item 5 is the real infrastructure investment.
258
+
259
+ Status: (1)–(4) shipped in 0.89.0; (5) the execution sandbox shipped in 0.90.0
260
+ (`providers/sandbox*.ts`, the `sandbox_run` tool, `/sandbox`). What remains is the
261
+ *measurement*: running /debug against a containerized SWE-bench-lite with the
262
+ sandbox as the oracle, to get the number.
263
+
264
+ ## How we will know it worked (this must be measured, not shipped on faith)
265
+
266
+ - `/debug`: on SWE-bench-lite (bug-fixing), with a real executable environment,
267
+ resolved-rate vs the single-shot baseline. The hypothesis is that real-run
268
+ verification + candidate selection beats single-shot; if it does not, the
269
+ premise is wrong and we stop.
270
+ - `/build`: on a *build* task set (multi-file features with runnable acceptance
271
+ criteria — the eval /plan actually deserves, which SWE-bench does not provide),
272
+ scored on acceptance-met + does-it-run + coherence, single-shot vs /build.
273
+
274
+ No mode ships to npm before its number beats the baseline it claims to improve.
275
+
276
+ ## Non-goals / honest caveats
277
+
278
+ - This does not add "more specialized agents" for its own sake. The measured
279
+ lesson is the opposite: minimal + execution beats elaborate + deliberation.
280
+ - It does not claim /build will beat single-shot — it makes /build *measurable*
281
+ and gives it the one ingredient (execution) that could make it win.
282
+ - Without the execution sandbox, this is just a reorganization; the sandbox is
283
+ where the value is, and it is real work.