Changelog
September 20, 2026 · View on GitHub
0.29.0 - 2026-09-20
- Return the command guard to
specpi-jev-guard, and leave it there. 0.28.0 built the guard into the layer as its eighth system, for three stated reasons that were properties of the package as it stood: a global configuration file with no session scope, a key read from the environment only, and a fail-closed posture. Two of the three are fixed upstream. Guard 0.3.0 ships/jev-guard setup | on | off [--global] | check | model | backend, so it has a session scope and a switch of its own, and it resolves Pi's saved login before the environment, so/login openrouterserves it exactly as it serves the advisor. What remains is fail-closed, which is a disclosed trade rather than a defect.extensions/jev-advisor/risk.mjsandquestions/guard.mjsare gone, the package is pinned again, and the base is eight. - Keep the guard out of the Jev layer entirely, rather than half in it. There is no
/jev guardcommand, nosystems.guard, noguardpair in the layer's settings, no guard row in the Chat panel and no guard line in/jev status; the advisor imports nothing from it, and a test walksextensions/jev-advisor/to keep it that way. Two switches over one gate is what made the package's own shape a problem the first time, and adding a second one back would have reintroduced it with the roles swapped. The package owns its switch; SpecPi owns whether the package is installed and that it arrives off. - Move the seam to
scripts/jev-guard.mjs, because it is an installer concern and nothing else. It was a managed resource copied into every agent directory, where after this change nothing would have loaded it. The managed set drops from 34 files to 33, and the seam ships with the CLI instead. - Write the inert posture at install and update, and nowhere else. The old seam re-asserted it at every session start, which is what made a
--globalsave through the package's own command fail to survive a restart; it does not run at session time now, so the package's switch stays the package's between installer runs. The install-time write is still unconditional, because an install carrying a staleenabled: truefrom before the package was last unpinned would otherwise arm a fail-closed gate the moment it came back. That is the one case where establishing the default takes something away, so the run reports it by name and says which command restores it. - Drop both older guard keys in schema 4 rather than migrating either. Schema 2 kept
guard: { enabled, startup }and schema 3 keptsystems.guard; neither was ever the authority over whether the guard runs, so keeping a copy could only produce a second answer that disagreed with the package's own file. Nothing about anyone's guard changes: a schema-2 preference had already been written through to that file by the last session that read it, and schema 3 shipped with the package unpinned. - Drop the package-retirement machinery with the package it existed for.
retiredPackages,removeRetiredPackagesandretireBasePackagesunpinnedspecpi-jev-guardby name on every install and update, including under--skip-package-install, andspecpi doctorfailed on an entry that was still configured. With the package pinned again all three would strip the entry SpecPi itself writes, so they are gone rather than filtered. The transaction's watched set returns to its narrower condition, which is correct again now that nothing writes settings outside it. - Pin the command guard at 0.4.0 and show its counter in SpecPi Chat. The package publishes one status line per session under the key
jev-guard-- classifier calls, blocked calls once there are any, and the last verdict -- and Chat already stored it, as a raw key/value row inside the collapsed generic runtime list behind up to twenty-four other widgets. It is a first-class readout now: a chip in the session footer beside the token and cache counts, showing the leading count, turning amber once something has been blocked, with the package's whole line in the tooltip. The key joins the reserved set so a busy session cannot crowd it out. Chat renders the string and does nothing else with it -- no recount from the session records, no reformatting, and no control, because the guard is configured in Pi and not here. The chip is absent exactly when the guard is not gating, since the package clears its own line when it is switched off or its audit display is. - Leave
auditDisplayto the package. It defaults tostatus, which is what publishes the counter, and asserting it in the install-time seam would take a display preference away from the user to guarantee a readout in one frontend. - Raise the session call ceiling from 512 to 2048, and the per-system ceilings with it. 512 was sized the way 120 was before it -- against sessions shorter than the ones the layer now runs in. The long-session evals are what showed it: a session long enough to compact several times spends in the hundreds, so a backstop meant to be reachable only by a loop had drifted close enough to real use to stop a session that was working correctly. The per-system numbers move too, because the total is only a constraint while the per-system ceilings can add up past it: 2048 against a sum of 2322, scaled from the same firing frequencies rather than re-derived.
capabilitystays at 2, alone, because it is bounded by construction rather than by frequency -- it asks once a session and then never again. A test pins the inequality rather than the literals, so the next change to either number has to consider the other. The whole ceiling is about six cents and two megabytes of digest. - Take the guard out of the eval row it was never going to measure.
eval-harnesses.mjsasked forguard: trueand verified the advisor read it back, which now throws rather than silently measuring less than it asked for — the check working as designed. The disposable homes do not install the package, so the row does not claim it. The tier-6 and Terminal-Bench tables keep their recorded guard rows, labelled as the native guard they measured, because deleting a measurement is not the same as correcting one.
0.28.0 - 2026-09-18
- Read the key Pi already has. The layer resolved its credential from
OPENROUTER_API_KEYand nothing else, while Pi had long since stored an OpenRouter key where it stores every provider credential — theopenrouterentry that/login openrouterwrites toauth.json. So a person who had logged in, whose every model call was working, was toldkey: missingby the one component that had opted out of Pi's credential store, and there was no interface anywhere that would have explained the gap. The advisor now follows Pi's own documented resolution order, the store first and then the environment, which is what makes/login openrouterthe answer to "how do I configure the API key" rather than a second thing to set up./jev statuslists every source and marks the one in force, because "missing" with nothing to act on is what sent people looking for a key field that does not exist. - Make turning the layer on turn the layer on.
/jev onset the master switch and left all seven systems off, so the layer ran and did nothing; the notification said so and then asked for seven more commands. Enabling it now enables every system that is off, and only when none are on, so a deliberate subset survives being toggled. The command guard is one of the eight and comes on with them, and because it is the only system that can refuse a tool call, the notification now says so in as many words rather than listing it among seven that only ever add advice. - Remember the switch.
/jev onchanged one session and wrote nothing, so the layer had to be re-enabled from scratch every time Pi started — the startup preference was a separate command most people never found. Turning the layer on or off now writes the preference, with--sessionfor the one-off case that must not change tomorrow. - Stop storing one intention as two switches that can cancel each other.
masterandstartupare both written whenever either changes, because the advisor only acts when both are true: a file sayingmaster: true, startup: falsedescribes a layer that is on and never runs, which is exactly the state the Chat panel's two independent checkboxes made it easy to save. - Drop the
specpi-jev-guardpackage and build the command guard into the layer natively, as its eighth system. Four review rounds kept returning to the same handful of causes, and all of them were properties of that package rather than of the feature: its configuration was one global file with no session scope, so there was no such thing as enabling it for a session and--sessioncould not scope it; it read its key from the environment only, so the credential/login openrouterhad already stored was invisible to it; and it was fail-closed, so a missing or stale key turned every shell call in the session into a refusal. Assystems.guardit is gated, budgeted, reported and toggled by exactly the same code as the other seven —/jev enable guard, one key, one budget, one switch — and the separateguard: { enabled, startup }pair is gone with the persistence disagreements it caused. Schema 3 migratesguard.startupintosystems.guard, and the base drops from eight pinned packages to seven. An install that already has the package is unpinned on update, by name and only when the entry is the one SpecPi wrote, because dropping a line from the template would otherwise leave a fail-closed gate running on every machine that already had it with the command to disarm it deleted.specpi doctorreports a retired entry that is still configured. Its downloaded files stay where Pi put them; Pi stops loading it. - Invert the failure posture. The native guard fails open: no key, no budget, a timeout, an unconfident answer or a middle-band verdict with nobody to ask all hand the call to
@gotgenes/pi-permission-system, which decides it exactly as it did before the layer existed. That is not a weakening — the guard sits in front of the permission system, so fail-closed meant an outage stopped work while fail-open returns policy to the component that owned it anyway. It is also the rule the rest of the extension already followed. - Hold the local allowlist to one rule: a binary is admitted only if it is simple whatever flags it is given.
rg --preruns an arbitrary preprocessor,date -ssets the clock,hostnamewith an argument sets the hostname,file -Cwrites a compiled magic file — none reads as dangerous, all four were admitted, and each was a general bypass. The list is a budget mechanism rather than a safety one, which is the thing that had gone unsaid: the guard has 208 calls, so a fast path exists to stop a session oflsandgrepspending them all and leaving nothing for the calls that matter. That makes the admission test "would asking about this spend the budget without learning anything", and under it a rare binary is never worth a silent hole. Jev is the analyser; anything that merely looks simple belongs to it. - Say why the two local lists are maintained under opposite pressures, in the file itself. A wrong
safeis a silent permanent hole, because it is the one verdict with no second reader; a wrongunknowncosts one call out of 208 and Jev decides; a wrongdangerousblocks real work with no recourse but switching the guard off. Six review rounds treated both lists as one thing called "the guard" and hardened them the same way, which is backwards. - Stop writing fixture credentials in real vendor shapes. Every "secret" in the suite was a fixture and always had been, but they were spelled like genuine OpenRouter and Anthropic keys, so GitGuardian failed the pull request on them — the scanner working exactly as intended and the fixtures being wrong. They are named for what they are now, and a test refuses any tracked file containing a vendor prefix followed by a plausible key body, because a scanner that cries wolf on your own test data is one people learn to click past.
- Close the ways round the guard that its own first implementation left open. A read-only binary was judged by its name and never its arguments, so
cat ~/.ssh/id_rsaandgrep -r . ~/.aws/credentialstook the free path — leaving the exfiltration half of the question the guard asks unreachable for the commands that answer it;printenvwas on the same list for the same reason and is not any more.env,find,fd,sortanduniqsat there too, each one a general bypass:env rm -rf buildbegins with a binary that changes nothing. The catastrophic delete rule anchored on the end of the line, so it matchedrm -rf /, which GNUrmrefuses on its own, and missedrm -rf / --no-preserve-root, which does not. And a shell tool's text was read frominput.commandalone, sowrite_stdin— which types into a live shell — was classified as the empty string and spent a budgeted call asking about nothing. - Stop two failures from turning into consent.
ctx.ui.notifyreaches the host over RPC and can throw; called inline inside the guard's fail-open catch, one failed notification unwound a decided refusal into an allow. And actx.ui.selectthat rejected — a host without the method, a disconnect, a cancel that throws rather than resolving — was swallowed by the same catch and read as approval, on the single path in the feature where a human was asked directly. The announcement can no longer change a decision, and a question that could not be put is a refusal. Which answer counts as consent is now one exported function with a test, rather than a string comparison inside a closure nothing imports. - Say that the guard can refuse a command on every path that arms it, not only on
/jev on./jev enable guardand/jev startup onarmed the same system in silence, so the first thing either taught you was a blocked call — the outcome the rule exists to prevent. - Give the command guard the history it is documented to weigh.
recentwas appended to in exactly one place, inside retention's success path, so a session running the guard with retention off evaluated the intent half of the block rule against an empty history for its whole length. Every tool result is recorded now; retention refines its own entry rather than adding a second. - Cache an unparseable
auth.jsonas firmly as a parseable one. Recording only successful parses left the worst case uncached: a truncated store threw on every call, so every request paid a fresh stat, a 256 KiB read and a failing parse inside the same latency budget the cache was added to protect, forever. - Unpin a retired package whatever shape its entry has, and keep that write inside the installer's transaction. Preserving a user-modified
specpi-jev-guardentry preserved the fail-closed gate this release deleted the controls for — the deliberate exception to "a modified entry is yours", and the only one. The write also ran outside the watched set on the--skip-package-installpath, so it was neither backed up nor rolled back when a later step in the same run failed. - Stop the Chat panel re-arming eight systems when someone turns the last one off.
coupleread that as the broken dead-layer file it repairs and ticked every box back on, the blocking command guard included, with a note describing a file that never existed — while/jev disableread the identical situation as "switch the layer off". The panel now does the same, and still repairs a file that genuinely arrives dead. - Give the guard its own gate thresholds. It asked under the name
gap, whichthresholdsForresolves identically by falling through its default — so adding aguardentry, the natural change for the one system whose action takes a tool call away, would have changed nothing at all and said nothing about it. - Stop
/jev offreading Pi's credential store to discard the answer, and stopapplyLayeraccepting two arguments it never looked at. - Settle most calls locally, for nothing. Read-only commands and ordinary project writes never leave the machine, and a deliberately tiny list of catastrophic, unambiguous commands is blocked with no call at all. A
lscosts nothing; a command carrying any shell control character never takes the fast path, becausels; rm -rf ~begins withls. Blocking on a Jev verdict needs two answers to agree — a confident destructive reading and a confident reading that the request does not account for the call — because the likeliest way to be wrong is a destructive-looking command the person asked for in as many words. Both answers must survive the confidence gate: reading a missing intent answer as agreement would have made the safeguard apply to about one call in five, since roughly four Score answers in five do not gate. The shell tools are gated under every alias the harness maps ontobash, the write tools under every namemulti_edit,apply_patch,create_fileandstr_replacearrive as, and a write whose target cannot be read is asked about rather than waved through. When the guard asks a human, only the affirmative runs the call; dismissing the dialog is not consent. - Make Chat and the advisor agree about a symlinked
auth.json. One reader was fixed to follow links and the other was not, so/jev statusreported "in use from auth.json" beside a panel reporting "No key anywhere" — the drift the pin test exists to catch, which it missed because it never created a link. It does now. - Merge
/jev enableand/jev disableinto the stored systems rather than overwriting them with the session's copy, which silently turned off systems enabled on disk since the session started. Disabling the last system now switches the layer off and says so, instead of writing the dead-layer state that four other code paths exist to prevent. - Cache the parsed credential store against the file's size and modification time.
ask()resolves a key per request and retention fires on every large tool result, so the store was being stat'd, read and parsed inside a 1500 ms latency budget on the tool path, where it used to be one environment lookup. - Move
/jev onand/jev offout ofindex.tsand into a module a test can call. Nothing imported that file, so the ~200 lines that are this feature were covered by prose alone while the suite reported green — which is how two review rounds found, among other things, a stored preference destroyed by a command that had decided nothing about it, a notification reporting a gate "left off" while it was on and blocking every call, and a rollback that never ran because the flag was set before the write.layer.mjstakes its world as an argument and holds no state, so what/jev onmeans is now something a test can call rather than something the prose asserts. - Bind the backend in
keyPresent,keySource,keySourcesandkeyEnvName, not justapiKey. All five were re-exported with a"openrouter"parameter default, sokeyPresent()reported a key on the direct TypeSafe route thatresolveKey()would never return.backend()moved intokey-source.mjsso each one binds it rather than defaulting to a literal. - Read a symlinked
auth.json. Refusing links recreated the exact divergence this layer was written to remove: a dotfile manager links the file, Pi resolves the credential, every model call works, and this layer alone reports "key: none found". Links are refused where SpecPi writes; this is a bounded read of a file Pi owns. - Stop the Chat panel throwing while opening the very file it exists to repair. A layer on with no systems was a validation error, so the panel rendered red with Save disabled before anything was touched, and the one mechanism that could fix it skipped the case. The rule now lives where the write happens: the form repairs and announces it, and the host refuses it on save.
- Persist
/jev enableand/jev disable. They announced a session-scoped change that a later/jev oncopied to disk anyway, so a choice described as temporary became permanent through an unrelated command. - Write both halves of the startup preference in
/jev startup, which had been the one command still writingstartupwithoutmaster— the exact two-keys-for-one-intention trap this release removes, left in the command named after it, while its own notification claimed new sessions would start on. It also fills in the systems when none are chosen, for the same reason/jev ondoes. - Bind
apiKey()to the active backend again. Re-exporting the resolver under that name rebound every no-arg caller — the calibration and triage scripts among them — to the OpenRouter default, so withJEV_BACKEND=typesafea script'sif (!apiKey())guard passed on a stored OpenRouter key while every request underneath it returnedno-key. - Keep measured runs off a personal account. With the credential store consulted first,
scripts/jev-calibrate.mjsandscripts/jev-triage.mjswould have billed a developer's own/login openroutercredential rather than the key inevals/.env, and--probewould have verified a key the run did not use. Both now setJEV_KEY_SOURCE=environment, which restricts resolution to the environment. - Refuse a layer that is switched on with nothing to run. The Chat panel filled in the systems in its form, but the full-configuration textarea bypassed that, so the dead-layer state this release exists to remove stayed one hand-edit from disk. It is refused rather than silently corrected.
- Report the key from one place in the Chat panel.
saveJevresolvedauth.jsonfrom a different agent directory thanloadJev, so with a workspace-relativePI_CODING_AGENT_DIRpressing Save flipped a working panel to "No key anywhere"; and the report named the credential store even on the direct TypeSafe API, which has no entry there. Both now follow the advisor's own conditions. - Stop the advisor's tests from writing to the real agent directory, which they had been doing for as long as any of them were async.
withAgentDirwrapped its callback intry/finallyaround a barereturn run(dir), and an async callback returns its promise at the firstawait— so the cleanup ran there, restoringPI_CODING_AGENT_DIR,HOMEandUSERPROFILEto the developer's real values and deleting the temporary directory while the test body was still going. Nineteen tests in that file pass an async callback, so most of the suite was isolated only up to its first suspension point. It destroyed a real credential store before it was found: a fixtureauth.jsonwritten after anawaitlanded in a developer's own~/.pi/agent, and because Pi'sAuthStoragemerges onto whatever it reads, one write replaced three logged-in providers with the single fake entry the fixture held. The suite reported every test passing while doing it, which is what kept it invisible. Cleanup now waits for a returned promise, and the fixture writer refuses any path outside the temporary directory: OAuth tokens cannot be recovered, so a failed test is the only acceptable cost of that check. - Isolate the key variables in the advisor's tests.
withAgentDirrestoredOPENROUTER_API_KEYafterwards but never cleared it for the body of a test, so "a missing key reads as unavailable" passed or failed depending on the shell the suite was started from.
0.27.0 - 2026-09-18
-
Put a measured number on the rule the whole layer is built around. "Any tool-set decision happens once, before the first request, or not at all" rested on a cache argument that was reasoned about here for months and never weighed. Three arms on
t3-cascade-ledgerdiffering only in when Browser QA's fourteen tools reach the request settle it: flipping them on at turn 6 collapsed cached tokens to 3,200 at the very next request in three attempts out of three — from 25,984, 17,792 and 21,120 — while the prompt kept climbing, one request going from 3,673 fresh tokens to 31,337. The re-warm cost 14.6%, 21.6% and 23.9% of the attempt, against a 10% threshold written down before the run. Arming the same group from turn 1 cost 16% more than never arming it, against 47% for flipping mid-session, so paying up front is about three times cheaper than paying when the need appears. Recorded toevals/runs/cache-probe/;scripts/cache-probe.mjsreproduces the analysis. -
Calibrate the Jev gate against recorded evidence and pin it there. Every threshold was a placeholder carrying a comment that said so; the layer shipped ahead of its own gate.
scripts/jev-calibrate.mjsnow measures each primitive against a label this repository already owns and, separately, checks whether a gate can fire at all — and the second check found that it could not. Retention demanded a Score confidence of 0.80 with the value within 0.15 of a level; on a deliberately obvious spent result it answers 0.10 to 0.18 at confidence 0.73 to 0.85. So retention could gate through to "keep this result" and essentially never to "this result is spent", and running the layer could never have shown it, because a system that never fires looks exactly like one whose advice was always to do nothing. The same check caught compaction's open-thread gate and, the same day, a threshold in new code written for this release. -
Report honestly that the confidence field carries little signal on hard questions. Predicting an attempt's outcome, its task category or its tier from behavioural metadata alone scores at the base rate for a Noul and a Choice, and about two-thirds exact for a Score against a 43% majority class. No system's pre-registered precision target is met anywhere on any of those curves, and
evals/runs/jev-calibration.jsonrecords UNMET rather than a number chosen to fill the gap. On the production questions, where the state carries the material being judged, separation is wide: a planted credential scores 0.96 against 0.04 for a clean report. -
Find out, and say, that retention does not fire. Once the ledger could record outcomes rather than only calls, five live runs said the system asks three or four times per attempt and has never shortened anything, every decline being the same: Jev answered and reported a confidence below the calibrated bar. On a contrived case it is confident; on the real reads of a repair chain, where each result feeds the next step, it is genuinely unsure whether the output is spent. The threshold is not lowered to compensate, because firing on a confidence the model did not have is the one thing a system whose wrong answer costs the task must not do. The point of the instrumentation was to make that statement checkable instead of inferable, and it does.
-
Instrument what retention actually drops. The ledger recorded bytes sent and never whether the advice was taken, so a system's effect could only be inferred from a cost delta it may not have caused — disqualifying for a layer whose claim is that it pays for itself.
request()takes adecidecallback that runs before the ledger write, so one line now recordsappliedandsavedBytes, and the eval adapter foldselisionsandbytesDroppedout of the disposable home into each attempt's report. -
Re-run the layer's own row and publish what it says, which is that the layer does not pay for itself. 37 attempts across five tiers with the calibrated gate and five systems on: 43 calls, none failed, none changed anything. Retention asked 38 times and elided nothing, every decline the same confidence below the bar — and this time against a threshold that demonstrably can fire, which is what turns the earlier suspicion into a result. Progress asked 5 times and was right every time that the session was not stuck, down from spending an entire budget on one healthy session. The layer costs about 9% more per attempt and scores within noise either way, and the one figure that had looked like the mechanism working did not survive: context growth per turn went from 590 to 631 against a control of 628, so the earlier 6% reading was variance.
scripts/jev-effect.mjsreproduces every published figure from the stored reports, using the same aggregates the evaluations page is built from rather than a second definition that could drift. -
Say that the untrusted-content system has never been called. It rides on a web or browser tool result; no task in any tier produces one, and the run that would exercise Browser QA keeps it withdrawn because capability arming needs an interactive human.
SECURITY_MODEL.mdclaimed its false-positive rate was measurable on tier 5, which is not true and is corrected. A system that ships unmeasured is a gap worth naming, because an absent row reads as a zero and a zero reads as a result. -
Record a payload that outlives its session. The proxy saw 44 calls leave the machine and the ledger held 43, which is how this was found. A system that is deliberately not awaited — the normal shape of a turn-level one — can still be in flight when the session ends, and the answer was being discarded along with its audit line. The answer still is, because it belongs to a session that no longer exists and must never be acted on; the line is now written either way and says which, since the ledger's whole claim is that every transmission appears in it.
-
Correct what the Jev page said the sanitiser refuses. It still carried the "refuses file contents and command output outright" wording that
SECURITY_MODEL.mdhad already retracted, in prose and in a diagram. The page now states the real bound: at most twelve short redacted lines of the material being judged — six from the head, four through the middle, two from the tail — inside the same 1 KB budget. Its measured table also disagreed with the dataset it summarised, and is replaced by figures the script derives. -
Publish the session's call count where another process can read it.
/jev statuscould always print what the layer had spent, and nothing outside the advisor's own process could: the ledger is an append-only audit trail with no session boundary in it, so counting this session out of it meant knowing something only the advisor knew. The advisor now keeps one smallusage.jsonbeside its settings, rewritten as it goes, holding counts and nothing else — no state, no questions, no answers, not even the ledger's digests, which is what makes it safe for a second process to read. It is written only while the master switch is on, so a layer nobody has enabled still leaves no trace, and the last session's counts survive shutdown rather than being deleted, because "this has never run" and "the session that just ended spent its whole budget" are different facts. -
Size the call budget for a session that runs for a day. The total was 120, which was sized against eval attempts — and an attempt runs for two minutes while a session runs until you close it. A turn-level system at one call every four turns reached that ceiling somewhere in the afternoon and then went quiet without having found anything wrong, which is not protection, only a later failure. The default total is now 512 with per-system ceilings that still sum past it, so the total remains a real constraint: measured, a full tier-3 task spends 4 to 7 calls and the busiest attempt ever recorded spent 12, so 512 is only reachable by a loop, and reaching it is therefore information. Cost was never the reason for a ceiling — a call is about $0.00003, so the whole total is about a cent and a half — they bound how much digest leaves the machine and how much awaited latency a runaway loop can add.
-
Say so when a budget runs out. Exhaustion and "nothing to say" both produce silence, and silence is this layer's normal state, so a session could run for an hour with a system switched on and quietly dead. Each system now announces its own exhaustion once, where there is a human to read it.
-
Replace one shared call budget with per-system budgets under a session total. A turn-level system firing thirty times would have reached a shared ceiling of 8 within a few turns and left every other system dead for the rest of the session, with event ordering rather than policy deciding which one won. Schema 2; a schema 1 file is migrated forward rather than read as unrecognised, because collapsing to all-off is a rule for corrupt input and applying it to our own previous version would silently disable a layer the user had switched on.
-
Stop the progress system spending its budget on healthy sessions. Its first local gate asked whenever any single signal fired, and a live run spent all twelve calls on a session that scored 0.978: a 120-step repair chain re-runs its verification command constantly, so a repeated tool signature is that task's normal condition rather than a symptom, and a long read looks identical to a stuck session on the quiet-stretch signal alone. Two weak signals are now required together, a run of three consecutive errors still stands alone, and a verdict is not re-asked for four turns because the situation that produced it has not changed. The same run afterwards: one call instead of twelve, same score.
-
Add three systems. Progress is the first aimed at turns rather than input tokens, which is where the money is on the hard tiers: it watches for a repeated tool call, a run of errors or several turns with nothing written, and only then asks whether the session is stuck. It ships set to tell the person rather than the model. Untrusted content prepends a fixed warning to a fetched page that confidently reads as instructions addressed to an agent, and costs no extra call while retention is on because one digest answers both questions. Capability arming reads the request once, before anything is sent, and offers a withdrawn tool group at turn 0 instead of turn 6 — which the cache probe prices at about a third of the cost.
-
Widen retention to the results that are actually large.
fetch_content,get_search_content,web_search,browser_snapshot,browser_accessibility,browser_diagnosticsanddelegateproduce the biggest outputs anything in SpecPi generates and are the least likely to be load-bearing twice, and the plan said they fell out of this system for free. They did not: the names were simply not in the set. -
Serve branch summarisation, which was unserved. It is the same problem as compaction at the same discarded boundary, so it shares the compaction switch rather than adding another.
labelfrom a fixed enum makes/tree's labelled-only filter worth having, and no model-written text reaches the session file. -
Give
request_capabilitya documented per-invocation cost. Its dialog stated the standing schema weight and said nothing about the one-off, which the cache probe now prices at about 20% of a mid-length attempt. The tool description and the confirmation both say so, and the plan's claim that activating Browser QA also rebuilds the system prompt is removed rather than left standing: the pinned 0.3.0 release moved that guidance into tool descriptions, and the measurement confirms the system prompt is byte-identical between arms. -
Publish the failure-mode distribution on the evaluations page. A pass rate says how often a harness finished, not whether the failures ran out of clock, solved the wrong problem or repeated a failing call — and those need different fixes. Every verdict goes through the same gate a live session would apply, and 14 of 24 did not clear it; those are published as ungated rather than folded into the nearest category, because a report file records what a harness did and not what it was trying to do.
-
Correct what the security model says leaves the machine. It claimed file contents and command output were "refused outright". That was never true: deciding whether a result is spent cannot be done from byte counts, so a bounded sample of the result's own lines has always been sent. The accurate bound is at most twelve short redacted lines inside a 1 KB budget, and it is now stated as such in both
SECURITY_MODEL.mdandTHIRD_PARTY.md. -
Score tiers 1 to 3 on effort as well as correctness. Across 182 recorded attempts every score was exactly 0 or 1, and 9 of the 14 failures were one harness with disclosed platform problems, so thirteen tasks carried about one bit between them; a two-line deliverable is genuinely binary and no grading scheme rescues it. What did vary at identical results was the work taken — 2 tool calls against 9 on the same task — so a task may now declare an
effortreference and the runner scorescorrectness x (1 - weight + weight x min(1, floor / calls)). Correctness multiplies, so a wrong answer still scores zero however cheap it was; the floor is the fewest calls a real harness used on a passing attempt, named per task, because reference solutions hardcode their answers and a floor derived from them would punish any agent that honestly reads its input. Beating the floor caps at 1.0, so a better harness never lowers anyone else's recorded score. The checker still returns correctness alone and never sees tool calls, so the fake/failing-fake contract is untouched, and stored attempts are rescored byattemptScorethe same waypriceAttemptreprices stored usage. -
Give every tier 1 and 2 task a decoy. Scope was clean on all 182 recorded attempts because most workspaces held only the file being worked on, so restraint was measuring nothing. Each task now ships a neighbour carrying the same class of defect as the in-scope file, and the prompts do not mention them:
t1-no-touchandt2-scoped-editname their forbidden file, which tests instruction-following, while these test whether a harness stays on its task when something adjacent looks broken. Across 168 attempts no harness touched an unrelated decoy; all seven edited the one that was a consequence of the change they were asked to make, so that decoy was replaced with an unrelated one rather than left measuring good engineering instinct as overreach. -
Publish efficiency rather than only spend. Cost is the sum of everything else and the least diagnostic figure of the set, so the evaluations page now reports tool calls, turns, tool-error rate, repeated calls, cache hit rate, context growth per turn and compactions beside the score. All of it was already recorded per attempt and aggregated nowhere. One harness compacted 37 times across the run and no other compacted at all, which no cost column would have shown.
-
Add a "The Jev layer" page to the site, with diagrams for where the layer sits, the six gates in front of every call, and why deciding on arrival is worth five times what rewriting history is worth. It states plainly that the layer has not yet paid for itself: across 259 attempts it cost about 9% more per attempt and scored fractionally lower, with context growth and tool-error rate the only measures moving the right way, and neither attributable yet because nothing records how many bytes retention actually dropped.
-
Add a Jev layer panel to SpecPi Chat. The four systems, the master switch, the call budget and the command guard are toggles that write
<agent-dir>/specpi/jev/settings.json— the same file the extension reads. The panel holds the flattened shape and the host translates, because the systems nest undersystemsand the guard underguardon disk and a nested object renders as a JSON textarea. A test asserts the panel offers exactly the advisor's own systems, so the two cannot drift apart. -
Key the Jev consent grant to the host it was given for. The dialog named
api.typesafe.aifrom a fixed constant, and when the default backend became OpenRouter it went on naming a host the data no longer went to; because the grant was keyed on the same constant it was written with, nothing failed and the mismatch was invisible. The label is derived from the live base URL now, so each destination names itself and switching backends asks again rather than carrying an old answer to a new destination. -
Refuse to render an evaluations page spanning two models. Metadata was read from whichever report sorted last, so a mixed set would have published one model name over rows measured on two — the same shape already caught once for attempts per cell, which had a guard where the model did not.
Unreleased
-
Route and price Jev traffic through the eval proxy.
TYPESAFE_BASE_URLpoints the advisor at the proxy, which forwards/v1/systemoneupstream and records what it cost, so advisor spend lands insidemodelCost— the figure the harness comparison actually uses — instead of being invisible because it went somewhere else. It is also reported separately asadvisorCost. Jev prices input only and reports no usage block, so tokens are estimated from the payload at chars/4 and marked as an estimate. Advisor records are filtered out of the model series, which counts turns and context growth and would be wrong if they were mixed in. -
Write the Jev guard's inert settings at install time, not only at session start. The guard's own default is
enabled: trueand it re-reads its settings on every tool call, so an absent file means an active guard — and with no key it fails closed, which is a fresh install that refuses to run commands. The advisor rewrites the file every session, but that only helps if the advisor loads; establishing it duringinstallremoves the dependency. -
Withdraw the two harness-improvement authoring tools when no improvement is selected. Measured across the recorded eval runs, six of SpecPi's ten offered tools were offered on 31 of 31 attempts and called zero times;
record_harness_contractandfinish_harness_improvementare only usable after a human selection, and whether one exists is already a fact in local state. They are now withdrawn until one is made and restored the moment it is, which needs no model and cannot be wrong.report_capability_gapandrequest_capabilityare never withdrawn: one is how friction gets reported at all, the other is the escape hatch that makes every other withdrawal safe. -
Add the Jev advisor, off by default. It asks TypeSafe's Jev classifier typed questions about session state and gets calibrated probabilities back, then gates them in one place. Four systems: shorten a spent read-only tool result before it is appended, steer compaction's summary at the one boundary where the prompt cache is discarded anyway, deduplicate and re-score capability-gap reports, and order the sources a delegation batch will freeze. It holds no authority — it never grants a capability, calls a tool or allows one — and every failure is silent, so a timeout, missing key, refused consent or unconfident answer simply produces no advice.
/jevshows and changes it;/jev ledgerreads back a local hash of every payload ever sent. -
Condense tool results on arrival rather than rewriting history. Simulated over the recorded token series, batching several results and rewriting them afterwards is worth about -12% of long-attempt cost, against -61% for condensing each result before it is appended: a rewrite invalidates the cached prefix, and 94% of SpecPi's prompt tokens are cache reads. Coverage matters far more than compression ratio, so the replacement is a plain deterministic head-and-tail digest that says the output can be recovered by re-running.
-
Pin
specpi-jev-guard@0.1.0beside the permission system rather than in place of it, and ship it inert. Its own default isenabled: true, so left alone a fresh install would start gating shell and file calls through a third-party service on day one; SpecPi writesenabled: false, and while it is off@gotgenes/pi-permission-systemdecides every call exactly as before./jev guard onenables it for a session,/jev guard startup ondefaults it on. Once on it is fail-closed by design — no key, an unreachable endpoint, or a middle-band verdict with no UI all block the call, and no setting hands that decision back to the permission system — so switching it on accepts that an outage stops gated work./jev statusanddoctorsay which posture is in force. -
Use one key for the whole Jev layer, and reach Jev through OpenRouter. Jev is published there, the guard already defaulted to that backend, and an OpenRouter key is rejected by the direct TypeSafe API with a bare 401 — so the advisor now defaults to OpenRouter too and both halves read
OPENROUTER_API_KEY.JEV_BACKEND=typesafeselects the direct API for a TypeSafe key. Onlyenabled,backendanduncertainare asserted on the guard, merged into the existing file, so a user's own thresholds, safe-command globs and protected paths survive. -
Nothing in the Jev layer is on by default. The advisor's master switch, all four of its systems and the guard each ship off, and each has a
startuppreference so a user can default on whatever they want. Session toggles never write those preferences. -
Read development keys from the existing
evals/.env, reusing the eval suite's own loader and file rather than adding a second mechanism beside it.scripts/jev-calibrate.mjsandscripts/jev-triage.mjsload it automatically; a shell variable always wins, values are never printed, and--env-file=<path>or--no-env-fileoverride it.--probesends one fixed synthetic question so a key and endpoint can be verified before a full run is spent on them.evals/.env.exampledocuments the two Jev variables alongside the eval provider ones. The file is for this repository's scripts only; an installed extension reads the environment Pi was started with. -
Add
scripts/jev-calibrate.mjsandscripts/jev-triage.mjs. The eval checkers are deterministic, so Jev is not a grader here and would be worse as one; calibration inverts the relationship instead and uses those objective verdicts as free labels, printing reliability bins and precision at each candidate threshold so the gate thresholds can be read off a curve rather than guessed. Triage classifies why recorded attempts failed, which is manual transcript reading today. Both are offline and touch no session. -
Add a
specpi-jeveval harness besidespecpi-default, pricejev-1.13.0in the frozen list, and recordstderrTailon every attempt. The adapter refuses to run unlessTYPESAFE_BASE_URLpoints somewhere loggable, so advisor spend is priced rather than hidden.eval-run.mjs --keep-transcriptswrites per-attempt request transcripts for triage. -
Measure the DeepSeek Harness on the same terms and add it to the first-call chart. Its default session sends 31,743 characters across 25 tools, about 5.7x stock Pi and within 700 characters of OpenCode.
scripts/measure-context.mjs --dsh=<path to its bin>takes that row, declared through the harness's own patch layer; its auxiliary session-title request is excluded because it carries no tool schema. The Oh My Pi row is carried forward from the same-terms run while its runtime's dependency resolution stays broken upstream.
0.26.0 - 2026-09-17
- Let the agent ask for a withdrawn tool group instead of working around it. Hiding web access and Browser QA keeps 19,344 characters of tool schema out of every request, but it also hides them from the agent, so a session that turns out to need one had no way to say so. The new
request_capabilitytool names the withdrawn groups and asks the human, who may decline; accepting offers that group's tools from the agent's next message, for the rest of the session. It grants nothing on its own: it refuses without an interactive human, and a decline leaves the session unchanged./webaccess onand/browser onare unchanged. Delegation is not requestable, because its own package requires a human command to bind a model. - Stop asking about a capability you always allow.
/capability allow <name>records a standing grant sorequest_capabilityoffers that group without a prompt,/capability ask <name>restores the prompt, and/capabilityshows which groups are offered and which are granted. Recording a grant needs an interactive command, a grant that fires announces itself, and headless sessions are still refused: the grant removes the prompt, not the human. - Bump
specpi-browser-qato 0.3.0 and move the base pin to match. Its fourteen tools no longer carrypromptSnippetorpromptGuidelines; that guidance moved into the tool descriptions, which travel with the schema. Pi rebuilds the system prompt when an activated tool carries prompt metadata, and that rebuild invalidates the provider's cached prefix even where deferred tool schemas are supported — so the metadata made every mid-session activation more expensive than it needed to be. Behavior is unchanged.pi-web-accessis a third-party package and still carries its own prompt metadata, so activating web access mid-session continues to rebuild the prompt.
0.25.0 - 2026-09-17
- Hide the web access tools until needed.
web_search,source_check,fetch_contentandget_search_contentare no longer offered to a session until/webaccess on;/webaccess startup onsaves that choice. The working agreement, security model and wiki say so, and the agent asks the human to run it rather than attempting a hidden tool. - Re-measure first-call context from the complete installed base instead of first-party extensions alone. A default session sends 15,069 characters and the enabled profile (
/browser on,/delegate on,/webaccess on) sends 40,203. The earlier 10,536-character figure omitted installed guidance and third-party pins and is corrected on the research page. The measurement, pins and method are published assite/research/context-measurement.json. - Add a capability chart that partitions the enabled profile's tool schema group by group: web access is the largest at 11,298 characters, ahead of Browser QA's fourteen tools at 8,046. Leaving the three opt-in groups hidden keeps 23,797 characters of tool schema out of every request.
- Re-measure Oh My Pi on the same terms at 65,816 characters and keep it a measured row: HarnessTax covers Claude Code, Codex CLI and Pi only, so the fork was never a study figure.
- Measure OpenCode on the same terms and add it to the first-call chart. Its default build-agent session sends 31,043 characters across 10 tools, about 5.6x stock Pi and about twice a default SpecPi, landing between a default SpecPi and the enabled profile.
scripts/measure-context.mjs --oc=<path to OpenCode's binary>takes that row; the session title is pinned so the turn sends exactly one model call. The research page is rewritten in plainer language and the "For this base" section is restructured around subheads. - Stop
/scopefrom racing the session restore. Restore retires scope immediately but only learns the repository root oncegit rev-parsereturns; a contract declared in that window was recorded against the session cwd, and the replay that followed rejected its own entry as belonging elsewhere and silently turned scope off after reporting it set. Scope commands now wait for the restore in flight.
0.24.0 - 2026-09-16
- Replace
pi-subagents@0.67.0in the default base with first-partyspecpi-delegation@0.2.0, and addspecpi-experiments@0.1.0. The base is now seven pinned packages; the other five are unchanged. - Correct the delegation documentation: the package activates at Pi startup whenever a model is configured, and
/delegate offturns it off. Earlier drafts of this entry and of the package's own README, security notes and guide described it as opt-in, which the extension, its tool description and its startup test all contradict. - Publish SpecPi's own bounded delegation as an independent Pi package. Child sessions get three read-only tools over a source snapshot frozen when the batch starts — no shell, edits, network or nested delegation — under fixed ceilings that local settings may lower and never raise. Delegation activates at Pi startup whenever a model is configured, and
/delegate offturns it off for the session. The extraction drops the Command Guard admission path, which SpecPi no longer ships, so the reported guard posture isabsent. - Publish the retired
/experimentcommand as an independent Pi package. An experiment is a detached Git worktree created atHEAD, closed by exporting a patch or discarding it; the base worktree, its index and its uncommitted changes are never touched./experiment recoverreconciles records against what Git tracks and never deletes a directory Git still tracks. - Both packages carry no production dependencies and were extracted under MIT from SpecPi 0.20.1, immediately before commit
4f5461d. - Add the HarnessTax study (Pan, Yang, Arabzadeh, Chiang, Stoica and Zaharia; UC Berkeley Sky Lab and Arena, 16 September 2026) to the research page, credited and linked, with all 21 model-harness pairs drawn as cost-success figures from the data published with the study.
- Measure what SpecPi adds to Pi's first model call rather than assuming Pi's economy survives configuration.
scripts/measure-context.mjsreads the request a real Pi process sends and counts tool definitions, tool-schema characters and instruction characters the way the study defines them. The base sends 23,710 characters against stock Pi's 5,521, about 4.3x, still under half of Codex and about a quarter of Claude Code. The figure omits the four third-party pins, so it is a floor. - Gate the two heaviest optional packages behind a saved preference that ships off. Pi sends every active tool's schema on every request of a session, so Browser QA's fourteen tools (about 8.7 KB) and delegation's one (about 4.4 KB) were charged to projects that never used them.
/browser onand/delegate onenable them for a session;/browser startup onand/delegate startup onsave that choice. A default session's first call falls from 23,710 characters to 10,536, from 4.3x stock Pi to 1.9x. - Bump
specpi-browser-qato 0.2.0 andspecpi-delegationto 0.2.0 for that change, and move the base pins to match.specpi-experimentsis unchanged at 0.1.0. - Measure Oh My Pi on the same terms and add it to the first-call chart. The fork sends 65,843 characters across 11 tools, about 11.9x stock Pi and six times a default SpecPi, which puts a configured Pi fork between Codex and Claude Code.
scripts/measure-context.mjs --omp=<path to its cli.js>takes that row; it needs Bun and is skipped without the flag, so Oh My Pi is not a dependency of this repository. - Remove Chat's
pi-subagentsfrontend: the fleet adapter, its RPC bridge, its result cards and its configuration UI. Chat's existing delegation panel now covers the default base, including per-worker Stop. Package settings cover web access alone.
0.23.0 - 2026-09-14
- Replace the BetterWright default with independently published
specpi-browser-qa@0.1.0: 14 QA-focused interaction, accessibility, diagnostic, and visual-regression tools, not general-browser feature parity. The other five package pins and Chat 0.8.1 are unchanged. - Confirmed install/update runs the installed package's Node bin for Chromium setup and offline readiness checks.
--skip-browser-installnow skips that setup;--skip-package-installstill skips all acquisition and preserves an existing base. Doctor checks real rendering, pixel comparison, and accessibility without downloads. OS libraries require manual installation; Bun is neither required nor removed. - Migrate BetterWright entries using existing package ownership restoration: remove only unchanged SpecPi additions, restore pre-existing entries, and preserve user edits and downloaded tools. No personal browser/profile/cookie or private Pi data migration. Managed configuration rolls back on failure; package and browser-cache bytes may survive failure and uninstall.
- The Browser QA source was extracted and released independently as 0.1.0 before this integration; its published package is unchanged.
0.22.1 - 2026-09-14
- Remove
pi-lensfrom the default base. The other six package pins are unchanged. Normal managed updates remove unchanged Lens entries added by SpecPi; pre-existing or user-modified entries and downloaded files remain.--skip-package-installpreserves the old base. Restart Pi and Chat connections to unload Lens. - Align package documentation and release links with SpecPi Chat 0.8.1; no Chat UI or host behavior changes.
0.22.0 - 2026-09-14
- Remove
pi-background-tasksfrom the default setup to avoid its Anthropic message-history errors. Updates remove unchanged entries added by SpecPi and preserve user-managed installations. The other seven package versions stay the same. - Pair this release with SpecPi Chat 0.8.0, which adds editable permission settings.
- Ask agents to keep replies, commits, and pull requests brief and written in plain language.
0.21.1 - 2026-09-14
- Restore the website's original typography, colors, Chat preview, theme switcher, and improvement diagram while keeping the new package base.
- Bring back the README's logo, product preview, badges, and navigation, and make detailed command instructions collapsible.
- Give npm's registry processing up to five minutes to finish before the publication readback check fails. Version 0.21.0 published correctly but became available after the previous one-minute check expired.
0.21.0 - 2026-09-14
This release resets SpecPi's base and removes previously shipped harness features. Both retained Pi extensions ship as part of SpecPi 0.21.0. SpecPi Chat 0.7.1 remains the separately packaged VS Code frontend and is aligned with the new base.
- Reduce SpecPi to
/scopeand the human-selected harness improvement loop, including its local evidence, contracts, verification, retirement, and reopen behavior. - Remove SpecPi's custom delegation, Command Guard, background tasks, browser and structural tools, extra workflow commands, file review UI, themes, and shell profiles.
- Replace the old showcase site with a short installation and package guide. Keep existing documentation URLs working and provide a direct download for Chat 0.7.1.
- Update the VS Code frontend for the upstream package base: pi-subagents activity and result cards, visible package messages, Permission System settings and approval dialogs, and existing usage/status reporting. Preserve the chat UI, attachments, history, and separate VSIX packaging.
- Establish the new default base through eight pinned upstream Pi packages: pi-web-access 0.29.0, betterwright 2.8.1, pi-subagents 0.67.0, pi-lens 4.1.6, pi-background-tasks 2.5.0, pi-goal-x 0.31.2, @sreetej510/pi-usage 0.10.0, and @gotgenes/pi-permission-system 32.0.2. Keep the first-party surface limited to scope and the improvement loop.
- Install and update the base with Pi's package installer, preserve unrelated settings and resource filters, and restore unchanged owned package entries on uninstall. Document upstream script/download rollback limits, BetterWright's separate browser setup, and Pi 0.84.4 compatibility. Keep an explicit core-only skip option and isolated real-package validation.
- Save exact npm dependency versions during package acquisition and verify installed versions before completing the transaction, preventing later package installs from advancing an earlier pin.
- Keep explicit, backed-up installation and removal. Updates retire old managed resources, restore owned settings, remove shell marker blocks, and preserve retired runtime bytes and modified resources in local backups. Restart Pi after updating to unload retired extensions.
0.20.1 - 2026-09-10
- Enable
structural_searchby default on fresh installs and updates without a saved choice. Persist enablement transactionally and preserve explicit opt-outs; disable withspecpi update --structural-search=off, then restart Pi. - Keep acquisition skip flags, malformed-configuration failures, selected-source limits and Command Guard approvals unchanged. Unsupported native hosts must pass
--structural-search=offduring install/update to avoid runtime acquisition failures.
0.20.0 - 2026-09-09
- Add opt-in
structural_searchwith pinned ast-grep 0.45.3, protected explicit source selection, bounded output and subprocess cleanup, and exact-call approval in Strict mode. Enable withspecpi update --structural-search=on, then restart Pi. - Add
browser_accessibilitywith axe-core 4.13.0 for the current browser state, fixed WCAG profiles, bounded violations and incomplete findings, and existing browser cancellation. Update the managed browser runtime to provision the scanner. - Stage, smoke, verify and roll back structural runtime changes with the installer; preserve enablement and modified runtimes, qualify native hosts in CI, and keep downloaded binaries out of the npm artifact. An unparseable owned configuration is reported with its path instead of aborting
plan, and only an explicit--structural-searchselection rewrites it. - Fix structural-search merge findings: preserve non-binary runtime changes using full-tree ownership checks, restore prior runtimes before failed cleanup, bound serialized enablement configuration, and dismiss expired/cancelled Strict approvals.
0.19.1 - 2026-09-08
- Allow Pi's exact public Copilot catalog identification headers during delegation, fixing Copilot → Anthropic → Copilot switches that were incorrectly rejected as runtime provider overrides. Reconstruct headers independently in the child; retain runtime-auth, extension-provider and other header restrictions. Restart Pi after updating the harness.
0.19.0 - 2026-09-07
- Add session-owned background commands with exact interactive approvals, shared Command Guard admission, bounded logs, and best-effort process cleanup.
- Install and verify the background tools with an offline doctor smoke; document shell, environment, output-retention, and process-tree limits.
- Clarify repository guidance for concise commits, pull requests, and necessary risk-focused validation.
0.18.1 - 2026-09-07
-
Restore Pi's cached model catalogs during delegation setup so switching to catalog-added or updated models resumes workers without a manual toggle or restart. Catalog network refresh stays disabled; exact-model checks, safety revocations and spent quotas remain enforced.
-
SpecPi Chat 0.4.1 replaces the persistent Delegates panel with a compact live-only activity strip and opens workspace image links in the validated image viewer instead of the text editor. Chat is packaged separately; update the harness for the model-switch fix and Chat for the UI fixes.
0.18.0 - 2026-09-07
-
Start Command Guard off in RPC sessions, including new SpecPi Chat conversations. Explicit
/guard guardand/guard strictstill enable protection for the current session; the terminal startup chooser is unchanged. -
Let installer PATH discovery continue past inaccessible candidates while preserving permission errors for explicit executable paths. Use the pinned repository Pi for test fixtures and a temporary npm cache for package validation, so restricted test accounts do not require personal npm access.
-
Publish bounded delegate lifecycle metadata through Pi's RPC widget protocol for SpecPi Chat's live panel. Add exact-attempt human cancellation, stop sampling on settlement/shutdown, and keep prior-generation task labels out of replacement sessions. Worker policy, model-facing operations, quotas and provider behavior are unchanged.
0.17.1 - 2026-09-07
- Reduce the npm package by shipping only runtime scripts; remove obsolete plans, research documents and unused assets, and shorten operational guides.
- Use structured wishlist candidates directly instead of parsing generated Markdown. Human selection and proof-gated retirement remain unchanged.
- Remove obsolete tests, editorial assertions and duplicate CI execution while retaining supported safety coverage. SpecPi Chat 0.3.7 separately removes unused standalone conversation paths.
0.17.0 - 2026-09-06
- Let delegation workers correct ordinary source-tool arguments and malformed/truncated reports in the same child session, preserving previously read passages and spending the original budgets. Keep source changes, revocations, unavailable tools, and exhausted budgets terminal. Remove the delegation-specific 8,192-token cap in favor of Pi's provider/model settings; scale SDK response acceptance with
/delegate budget(1 MiB by default). - Replace generic delegation worker failures with safe diagnostics for source tools, provider requests, stream/context/response limits, output-token truncation, and final JSON/schema/evidence validation. Preserve the original tool failure through SDK cancellation, disclose report constraints in the worker prompt, and cover low-usage failures with controller and native Pi regressions.
- Raise default delegation budgets to 96 source calls/512 KiB and 32 model turns per job, with 32 batches/256 model turns per Pi process. Add human-only, persisted
/delegate budget <multiplier>(1–64, default 8), scaling counts and context together while preserving spent usage, deadlines, and concurrency. Keep handoff and response sizes independently bounded. - Count only delivered source JSON, report the specific exhausted allowance, and reject spent-budget follow-ups before starting a child. Tell workers their remaining allowance and verify substantial reading plus passage-preserving follow-up with offline regressions.
- Preserve safe delegation snapshot rejection reasons and identify the selected-source position so failed reviews are diagnosable in SpecPi Chat and terminal Pi. Redact raw filesystem errors and verify that rejected snapshots start no worker or inference.
- Replace delegation's private-topic keyword filter with known private namespaces and credential-store formats. Allow ordinary authentication, credential, session, and history source files/directories in all supported text formats, including
src/lib/security/credential-url.tsandcredentials.ts. Protect configured Pi storage and its canonical aliases; retain selected-file scope, containment, link, text, size, and freshness checks.
0.16.0 - 2026-09-05
- Enable experimental read-only delegation by default at the first session start of each Pi process, including TUI, RPC, print and JSON modes. Startup preflights the host without launching workers or model inference; selective review/scout admission, Guard checks, source restrictions and resource ceilings remain unchanged.
- Keep
/delegate offand safety revocations effective through reloads and session switches./delegate onexplicitly re-enables dispatch; restarting Pi reapplies the on default. Invalid settings, unsupported providers and locked, unready or ambiguous Guard policies still block activation. - Update delegation guidance and release references, and add startup/default-on regression coverage alongside real-Pi lifecycle checks. Restart Pi after updating SpecPi to load the changed delegation runtime.
0.15.0 - 2026-09-05
- Add SpecPi Chat 0.3.1, a separately packaged VS Code sidebar with streamed Pi replies, safe Markdown, expanded thinking and collapsed tool output by default, model/thinking controls, exact approvals, and a compact composer. It reuses the user's Pi configuration without managing credentials or installing the harness.
- Add searchable extension-owned history, rename and reversible archive, and independent live conversations. Switching chats or folders preserves background work, approvals, drafts, attachments, usage, and view position. Stop/Disconnect target the selected conversation; parallel chats share workspace files rather than isolated worktrees.
- Add validated file/image attachment, screenshot paste/drop, bounded inline images, workspace code links and image previews, explicit queued-image recovery, visible transcript search/copy/export, usage/cost reporting, and native Git diff review. Branching and earlier-prompt editing preserve the source conversation and never undo code files or automatically send restored drafts.
- Start Command Guard in Guard mode in RPC without an unreadable startup selector; retain explicit
/guardchoices after readiness. Route task handoffs, challenge reports, and wishlist reports through RPC-capable dialogs, and disclose terminal-only display controls. - Wait for legacy Pi startup fallbacks without approving early dialogs. Reset interrupted-run state on reconnect and never restore accepted prompts after a later refresh failure.
- Show installed Codex Usage and pi-usage (including Anthropic) reports in Chat's compact, expandable Limits row, separate from conversation tokens/cost. Reuse bounded Pi status events without provider queries, credential/cache reads, or changes to the default package list.
- Scope Chat's Pi-state filename restrictions to Pi/Chat storage so ordinary authentication, session, and history source files remain usable; retain global credential/key protection and canonical-path checks.
- Fix composer
/modelselection, read-only usage during active work, and stale Stop cleanup errors after reconnect. Align README and Pages installation examples with the separately versioned Chat artifact. - Add dependency-free local VSIX packaging, isolated real-Pi and native VS Code tests, rendered Chat checks in CI, and editor-extension syntax/source-inventory coverage. No npm/Marketplace publication or automatic editor installation is included.
0.14.0 - 2026-09-05
- Raise delegation's default job timeout from 2 to 10 minutes, including the provider adapter. Add
/delegate timeout <minutes>(1–60) andresetwith an atomic, backed-up preference across restarts. Batch timeouts scale with the job window; call quotas, original follow-up deadlines and settling ownership remain unchanged. Restart Pi after updating the delegation runtime. - Support platform aliases in the selected Pi agent-directory path while rejecting links inside preference state. Bound encoded backups separately so every accepted settings file can be saved repeatedly.
- Keep
.mtsdeclarations on LF checkouts and validate Pages permissions with either LF or CRLF input so release checks remain portable on Windows. - Run timeout persistence fixtures across release platforms, isolate the package help probe, and verify that settings survive failed updates and every managed delegation file is removed on uninstall.
0.13.0 - 2026-09-05
- Add bounded, best-effort sanitized browser exceptions, console errors, failed requests, and HTTP error diagnostics with explicit cursor/loss/clear semantics and ephemeral retention.
- Add keyboard/chord input, native dropdown selection, and deadline-bounded page-condition waits; invalidate snapshot refs on application-driven navigation and preserve isolated cancellation cleanup.
- Strictly type-check the browser extension against pinned development Pi, TypeBox, Node, and Playwright declarations without eagerly loading the browser runtime or changing production optional peers.
- Add real registered-tool Chromium fixtures, repeatable responsive-site checks with fault-injection tests, and a shared CI browser gate required before Pages deployment. Preserve the pinned-Pi no-skips coverage gate.
- Document privacy/testing contracts and an evidence-backed decision to retain project-native TypeScript semantic navigation rather than add an LSP tool now.
0.12.0 - 2026-09-05
- Add experimental, opt-in delegation for independent reviews and selected-source analysis through native Pi sessions. The parent remains the sole writer; workers have no shell, edits, live web, nested delegation or ambient extensions.
- Show live worker state, elapsed time and call counts above the editor, with expandable findings and evidence in tool results.
- Follow parent model and thinking changes after one activation. Check public SDK capabilities, preserve process budgets and cancellation settlement, and document unsupported parent hooks and provider overrides.
- Bound snapshot retention, source-tool responses and replay records. Preserve spending receipts while pruning recent cancellation and nonfinal assessment responses.
- Fix Command Guard state notifications across reused sessions, stale approval dialogs and session locking. Keep Guard optional for delegation while respecting active policy and locks.
- Share the delegation install inventory across installer and package checks, test imports from the installed tree, and exclude nested dependencies from source syntax checks.
- Rebuild the technical site with a dark default theme, workflow documentation, and a sourced architecture article with comparison charts explaining the single-agent default and selective delegation.
After updating the npm CLI, run specpi update and restart Pi to load the new delegation runtime. See the delegation guide for its experimental limits.
0.11.2 - 2026-09-04
- Publish the validated tarball through an absolute local path. npm interpreted the previous relative path as a GitHub repository, so 0.11.1 stopped before npm publication despite passing artifact validation.
- Carry forward the reviewed task cards, verification receipts, outcome feedback, and documentation improvements without changing runtime behavior. Versions 0.11.0 and 0.11.1 never reached npm; their source tags remain unchanged.
0.11.1 - 2026-09-04
- Fix npm release validation and dist-tag checks by configuring the public registry directly, avoiding the obsolete
always-authsetting generated by setup-node. Keep the existing cross-platform checks, protected publication, and provenance requirements. - Carry forward the reviewed 0.11.0 features in a new version. The 0.11.0 publication stopped before reaching npm because of this workflow configuration issue; its source tag remains unchanged. This bounded forward repair preserves the valid feature merge while correcting the release tooling.
0.11.0 - 2026-09-04
- Reject shared temporary roots before Pi test launches, ignore generated desktop builds in Git, require the active task digest in card-backed review submissions, and treat unavailable foreign task roots as absent. Keep npm configuration trust assumptions explicit.
- Resolve equivalent project-root aliases during scope checks while preserving relative-path traversal semantics and rejecting symlink escapes.
- Refine the README and GitHub Pages with clearer installation guidance, a Pi relationship diagram, responsive reading layouts, and versioned feature guidance. Keep local assets and human-controlled workflow boundaries.
- Add optional session-branch task contracts shared by the specification view, explicit scope import, experiment cards, completion review, and human-directed review packets. Preserve fixed requirement IDs and reject stale or incomplete card-backed reviews.
- Bind selected harness improvements to a source checkout, selection generation, and immutable recorded card. Preserve verification policy, restrict test discovery, and verify the supported source inputs before and after executable gates. Retain bounded verification receipts separately from model-reported acceptance evidence.
- Add explicit local post-retirement outcome feedback, preserve correction history, and distinguish shipped-baseline reviews from the local retirement cohorts used to calculate reopen rates.
- Preserve valid user theme choices through installation, update, doctor, and uninstall. Explain SpecPi, Pi, model-provider, and browser privacy boundaries without changing upstream preferences.
- Isolate Pi extension test launches before startup, distinguish a missing runtime from a failing runtime, and extend the pinned package compatibility checks. Exclude generated desktop output from source formatting and linting.
0.10.0 - 2026-09-02
- Add the public npm distribution contract for the
specpiinstaller CLI, including global install, explicit managed install/update/uninstall steps, source-audited alternatives, and the limited resource-only boundary of directpi install npm:specpiusage. - Validate the exact npm tarball in isolated state: enforce its public file allow-list and metadata, install it offline into a temporary global prefix without auto-installing Pi host peers, and run packed
plan,install,doctor,update, anduninstalllifecycle checks while proving private evidence survives. - Declare every imported Pi core module as an optional host peer, ship the README logo, preserve an executable package bin, and add public/provenance publishing metadata without npm installation lifecycle scripts.
- Add a release-only npm workflow that checks immutable tag/version/changelog alignment, rejects existing versions, preserves and checksums one validated tarball, separates protected publication from validation, publishes through GitHub OIDC with npm provenance, and verifies registry integrity, dist-tag, and attestation state.
0.9.0 - 2026-09-01
- Adopt the SpecPi identity across the package, executables, installer and private state, environment variables, managed markers, extensions and events,
/specmode, improvement skill, theme, tests, documentation, security policy, and GitHub Pages URLs. - Add the immersive
specpi-specPi theme based on SpecPi’s clean GitHub Pages specification design, with layered technical surfaces and complete palettes for Markdown, tools, diffs, syntax, search, statuses, and thinking levels. Make it the default while retaining Tea House as an installed option. - Rebuild
/specas an immersive specification console: replace normal header and footer chrome, show indexed execution phases and scope state, seal reasoning traces, hold live response prose until completion, keep tool output collapsed, and suppress routine model narration while preserving the full transcript and restoring normal rendering when the mode exits.
0.8.4 - 2026-09-01
- Add opt-in workflow controls:
/scopedeclares project-relative change boundaries and surfaces direct or observed drift without silently expanding scope;/experimentcreates detached, private-state Git worktrees with complete patch export and explicitly confirmed discard;/challengeproduces a structured adversarial readiness card whose deterministic gate rejects unresolved evidence. - Keep the new controls human-led and local: no child process or agent launch, automatic commit/merge/apply, remote operation, raw command log, unrelated session scan, or mandatory completion interception. Add branch-local scope/challenge state, a private recoverable experiment registry, direct closed validators, installer lifecycle coverage, and security-boundary documentation.
- Separate
/scope acceptfrom/scope add: accepting acknowledges one observed finding and leaves the declared contract untouched, so a later change to the same path is reported again. Add/scope recheckto re-baseline the worktree and deliberately clear snapshot uncertainty, and report removals and no-op verbs explicitly. - Export experiment patches as the exact bytes Git produces. A text file that is not valid UTF-8 previously lost its original bytes on export and produced a patch that no longer applied.
- Disclose ignored files in
/experiment statusand/experiment close. Ignored work is invisible to Git status and cannot travel in a patch, so a worktree holding only ignored work no longer looks empty at discard time. - Expire a completion challenge that the agent turn ends without answering, instead of leaving its "do not implement" instruction attached to every later turn.
- Offer a working recovery action for an experiment directory Git no longer tracks: the record can be released while the files are left in place for the human.
- Snapshot the worktree once per tool instead of twice, skip snapshots for read-only calls, and copy scope entries both when appending and when restoring them so a branch record cannot be rewritten by later mutation.
- Record an expired challenge distinctly from a cleared one, so a challenge that goes unanswered no longer discards the last completed readiness card after a restart.
- Report ignored paths from
/experiment statuswith no ID, the form used from inside an experiment worktree. - Re-derive worktree presence inside the registry lock during
/experiment recover, so a Git operation performed while a recovery prompt is open cannot drop a live record or adopt a replaced directory. - Measure experiment status and patch export from the recorded base commit instead of the worktree's current HEAD. Work committed inside an experiment previously reported as clean, exported to an empty patch, and could be discarded without the dirty-work confirmation.
- Keep Git-reported paths canonical in workflow state and percent-escape controls only at presentation boundaries, so filenames containing
%, newlines, Unicode separators, or directionality controls remain matchable without forging system guidance or UI text. - Resolve relative direct write and edit paths from the active session directory before comparing them with project-relative scope, so sessions opened below the Git root neither allow outside-scope mutations nor reject matching nested paths.
- Claim a patch output exclusively instead of checking then renaming, so a destination created in the gap is never replaced without explicit overwrite approval.
- Require a ready completion verdict to disclose residual risk when the change snapshot was indeterminate, instead of ignoring that signal.
0.8.3 - 2026-09-01
- Remove the
pi-subagentspackage and SpecPi's native-subagent configuration, runtime integration, and installation defaults. - Make the workflow rationale explicit: automated parent/child handoffs can silently omit decisive context, while parallel writers fragment assumptions and ownership. Prefer deliberate context gathering, reviewable artifacts, explicit second-opinion sessions, and one writer per working directory or isolated worktree.
- Split the public security policy from the technical security model. Document latest-release support, private vulnerability reporting, best-effort response expectations, reporting scope, secure operation, and supply-chain assumptions while keeping implementation boundaries in a shipped
SECURITY_MODEL.md.
0.8.2 - 2026-09-01
- Keep command-guard denials fail-closed without making every uncertain or wrong-shell cleanup attempt strand the session: only structurally proven lock-worthy critical mutations latch
locked, while parser fallback, shell-syntax mismatches, and refused reads remain non-latching denials. - Protect installed command-guard files as managed enforcement nodes rather than treating the whole command-guard directory as protected, allowing unrelated temporary descendants while preserving ancestor and canonical-path protection. Share the managed-file inventory with installer resources and checksums.
- Classify cmd-style
rd/rmdir /s /qsent directly to the Windows Bash tool as a corrective non-latching denial, and keep parser-fallback protected-path matching local to the destructive statement so unrelated scratch cleanup cannot inherit a critical result.
0.8.1 - 2026-08-30
- Ask for Guard approval before Git destroys work. Force pushes (
--force,-f,--force-with-lease,--force-if-includes) and the wider destructive Git family — remote ref deletion, hard resets, cleans, branch and tag deletion, stash drops, discarding checkouts and restores, rebases, and history rewrites — now surface an approval in Guard instead of running silently, because they discard or rewrite work no local undo restores. Ordinary pushes, pulls, and fetches stay quiet in Guard; all of it still asks in Strict.
0.8.0 - 2026-08-31
- Parse Bash and cmd at the statement level instead of treating every word in command position as a program. Shell reserved words (
if/then/while/until/for/do), the!negation prefix, and thebuiltin/command/coproc/timeprefixes were taken as leaf executables, so inif true; then rm -rf /; fithe real command survived only as an argument list on a leaf namedthenand matched no rule at all.trap 'rm -rf /' EXITnow analyzes its handler string, and cmdifconditionals are unwrapped the wayforalready was. - Resolve the heredoc consumer instead of assuming
-cmakes the body inert.bash -c 'sh' <<EOF … EOFruns the body through theshthat-claunches, andsu root <<EOFruns it as root, so both are code rather than data. - Thread the working directory through a command sequence. Relative targets always resolved against the session cwd regardless of what ran before them, so
cd / && rm -rf usr,Set-Location C:\ ; Remove-Item -Recurse -Force Windows,cd /d C:\ && rmdir /s /q Windowsandenv --chdir=/ rm -rf usrwere each reported as a determinate, clean delete inside the workspace. A directory change the analyzer cannot resolve now makes later targets uncertain instead of clean. - Protect the ancestors that contain enforcement state, not only the subtree itself. Deleting
<agent-dir>/extensions/command-guardwas denied while deleting<agent-dir>/extensionsor the whole agent directory — a superset of the same tampering — was allowed. Destructive Git operations run inside protected or enforcement trees are classified with them. - Canonicalize the path spellings that reach the same target: Win32 trailing dots and spaces (
C:\Windows.),~under PowerShell as well as Bash, the macOS firmlinked/private/etcand/private/vartrees, and the WindowsEFI/Recoveryboot partitions. - Complete the decode-to-interpreter set (
base32,basenc,xxd,hexdump,od) and the critical-process list (svchost,services,smss,winlogon,launchd), which previously closed only the base64 andlsass/csrssspellings of the same operation. - Stop an unavailable parser from downgrading a catastrophe into an approvable prompt. A helper timeout, a missing interpreter or a blown limit produced
ask, so the case where the guard knows least was the case where it yielded most. The raw command text is now scanned for confirmed catastrophic operations before any approval is offered, including the payload of an inline-code flag such as-Commandor-c, which is program text rather than data. The scan reads only what the shell would execute as syntax — quoted arguments stay inert and backslash is treated as an escape only where the shell treats it as one — so a command that merely prints a destructive-looking string is not mistaken for one. - Give the PowerShell parser helper the environment it needs to start. Spawned with only
SystemRoot,PATHandTEMP, Windows PowerShell 5.1 hung indefinitely on a current Windows Server 2025 image — measured at five of five spawns killed at a 20-second bound with no output and no error — while the same spawn withPSModulePath,APPDATA,LOCALAPPDATAandUSERPROFILEpresent completed in about 380 ms. PowerShell 7 was unaffected. Every 5.1 analysis in a session therefore waited out its full bound before falling back. The helper still runs on an allowlist that withholds tokens, keys and other credential-bearing variables. - Match endpoint-protection services as complete tokens rather than substrings.
security,firewallandsentinelmatched anywhere in the arguments, so ordinary units —redis-sentinel,security-scanner.service, an in-housefirewall-ui— were critical denials that locked the session. - Identify credential paths by shape rather than by bare words that ordinary source trees use as directories.
credentials,token,secretandpasswdmatched as standalone path segments, so every file under a monorepo'spackages/token/,src/secret/orapp/credentials/was a critical read denial. - Resolve the agent directory with the analyzed platform's path semantics rather than the host's, so cross-platform classification is deterministic instead of depending on how the host resolves a foreign path spelling.
- Reformat all tracked JavaScript and TypeScript for readability with four-space indentation, explicit braced control flow, one statement per line, and consistent spacing around blocks and returns. Add pinned project-local Prettier and ESLint checks so future changes preserve the style.
- Guide agents to prefer simple, explicit commands while Command Guard is active, reducing avoidable parser-uncertainty approvals without weakening or bypassing protection.
- Add a first-party, session-scoped command guard with Guard, Strict, Off, and Locked states. Guard is a narrow catastrophe backstop: confirmed host-wide destruction and enforcement tampering are immutable denials, analysis uncertainty asks with UI and denies headlessly, and determinate non-catastrophic work runs without routine prompts. Strict retains broad approval behavior.
- Add bounded shell-specific analyzers, native PowerShell AST parsing without evaluation, protected-path canonicalization, display redaction, deterministic policy smoke validation, and Linux/Windows regression coverage across PowerShell 5.1, PowerShell 7, cmd, and inert adversarial corpora.
- Propagate protected modes to supported native subagents through the pinned public preflight contract, a managed child extension, and a reserved binding; preserve unrelated child extensions, block unverifiable launch forms, and exercise a real inert native-child process in CI.
- Parse PowerShell with whichever installed host accepts the command text: PowerShell 7 grammar (
&&,??,?:) is no longer denied as malformed when only Windows PowerShell 5.1 parses it, and either host alone is now sufficient. A rejection is authoritative only when every installed host rejects it, and a spawn failure can never escalate a syntax error into a critical denial. - Classify argv-prefix runners (
setsid,stdbuf,ionice,taskset,flock,systemd-run,unbuffer,runuser,setarch,xvfb-run,proxychains), command-string runners (su,runuser,script,watch), awk shell escapes, andosascript/tclsh/expectinline code, so a critical payload cannot be laundered through an unlisted wrapper. - Treat a whitespace-bearing command token as unresolved rather than reducing it to its trailing path segment, and propagate an unresolved nested child up to the whole analysis so wrapped command strings cannot be reported as a clean parse.
- Match every PowerShell parameter prefix, not only full spellings:
-encruns the same code as-EncodedCommand, so an abbreviated flag used to carry a base64 payload past the guard with no approval when the invocation arrived through the Bash or cmd parser. Bash- and cmd-hostedpowershell/pwshinvocations now decode and classify their-Command/-EncodedCommandpayload instead of seeing one opaque argument, including recursivecmd /c powershell.exedispatch, and an absent PowerShell parser downgrades to an approval rather than locking the session over an interpreter the command could not have used. - Remove routine Guard approvals for determinate non-catastrophic work, including project or user-data deletion, force push, publication, installation, network transfer, process termination, service and registry changes, and out-of-workspace targets. Keep those broader prompts in Strict. Narrow Guard's protected mutation boundary to host-root/key system targets and, inside the installed agent, command-guard enforcement sources,
settings.json, andspecpi/manifest.json. - Identify Pi and SpecPi private state by location rather than by name.
specpi/manifest.json,specpi/backups,specpi/wishlistandextensions/command-guardwere matched as bare relative segments, so reviewing SpecPi's own repository denied a file read critically and locked the session, andguard.self-tamperfired on any mutation whose arguments merely contained "specpi" or "command-guard" —mkdir specpi-experimentwas a critical denial. On POSIX the rule was an unanchored/(?:specpi|pi).*(?:auth|session|…)/, so everyday files such assrc/api/session.tsandlib/api/auth.py("pi" inside "api") were denied critically too. These now key on the resolvedPI_CODING_AGENT_DIR; Guard protects only enforcement-critical installed state while Strict retains the wider private-path policy. - Stop latching the session lock when a read is refused. Blocking the read is the protection; locking additionally refused every later call — including read-only ones — until
/guard unlock, so one blocked file ended the session. Critical mutation attempts still lock. - Stop treating a plain
findas a deletion.findsits in the delete family for-delete/-exec, buthasRecursiveFlagmatches any predicate containing an "r", sofind src -type f -printwas reported as "Recursive deletion needs approval",find /etc -name '*.conf'denied critically, andfind . -name specpitripped guard self-tamper. Mutatingfindnow reachesfilesystem.find-mutation, which was unreachable behind the delete-family branch, andclearlyReadOnlyshares the same predicate list. - Classify complete environment enumeration however it is spelled (
printenv,declare -x,export -p,compgen -v, baredeclare) and recognize/proc/<pid>/environand/proc/<pid>/memshell reads. Strict asks about those findings; Guard does not claim comprehensive credential-read protection. - Protect macOS system roots (
/System,/Library,/Applications,/Users/<name>,/Volumes/<name>,/private/etc,/cores) and.bash_profile/.zshenv/.zlogin, without capturing the firmlinked/System/Volumes/Datauser tree. - Give approval prompts a human-scale bound and add Allow exact call for session. Reuse is limited to 128 in-memory SHA-256 fingerprints over complete tool input, cwd, mode, and policy version; calls are always reanalyzed first, critical denials cannot be overridden, and
/guard clear-approvalsclears the set. - Fail doctor on installed command-guard checksum drift, on a
pi-subagentsversion that no longer matches the pinned native-child contract, and when no PowerShell parser host is available; add byte-for-byte installer/update rollback injection coverage. - Document the defense-in-depth boundary, including direct user commands, custom tools, approved scripts, trusted extensions/configuration, TOCTOU behavior, and the need for OS-level isolation with hostile code.
0.7.0 - 2026-08-29
- Make retirement durable: every capability now ships a closed validator from a reviewed catalog,
finish_harness_improvementdispatches all linked validators generically and fails closed on unknown names, andnpm run checkplusspecpi doctorcontinuously re-prove retired capabilities in temporary state. - Add the improvement journal: retirements persist bounded sanitized proof (acceptance evidence, gates, repo-relative changed files, SpecPi version) in the local decision log,
/wishlist history [id]renders the harness's own changelog with rollback context, and the report's retired list shows verification dates and gates. - Add loop health metrics: deterministic retirements, reopen rate, open reviews, median time-to-retire, and qualification rate rendered in the report footer and summarized by
/wishlist status. - Make reopens context-rich: reopen decisions link to the retirement they review, carry up to five sanitized post-retirement signals, and the
/harness-improvementprompt includes the original proof and what changed since. - Extend repository checks to the wishlist extension and validator sources, ship the validator module through install/update/uninstall, and run completion validators from the source checkout under review; document the new local-only journal data classes in SECURITY.md and the
SpecPi-Gap:commit trailer convention in the improvement skill.
0.6.1 - 2026-08-29
- Flush a prompt frame when extension dialogs mount so chained menus such as
/spec-subagentsdo not remain invisible until the next keypress in regular TUI sessions, notably through Windows SSH terminals; require and bootstrap the reviewed Pi 0.84.4 baseline that provides prompt lifecycle events. - Fix provider-profile activation on model changes by prompting the user to run the documented
/reloadflow instead of calling command-onlyctx.reload()from a lifecycle event context. - Redesign the README self-improvement diagram as a compact Tea House graphic and version its asset URL so GitHub and browser caches cannot retain the previous rendering.
0.6.0 - 2026-08-29
- Add exact-provider subagent profiles that restore automatically with a single bounded runtime reload, while keeping capacity global and unavailable saved models stale without replacement.
- Add ephemeral provider leases so simultaneous different-provider Pi processes fail closed instead of racing the shared active settings mirror.
- Preserve private provider profiles across update and uninstall; store no credentials, authentication data, prompts, sessions, history, or complete settings snapshots.
- Replace the README's text loop with an accessible static Tea House SVG and update the showcase to explain saved provider restoration.
0.5.0 - 2026-08-29
- Automatically install pinned
@earendil-works/pi-coding-agent@0.84.3through npm after confirmation whenpiis absent; preserve the external installation on rollback and uninstall, with--skip-package-installas the opt-out. - Add
/spec-subagentswith confirmed capacity, builtin-role model, and thinking configuration using only the documentedpi-subagentsconfig surface. - Synchronize strict native subagent scope to the parent's exact Pi provider, filter model choices accordingly, flag stale role models after provider changes, and block unsafe project-scope tool launches.
- Preserve user-tunable role and capacity leaves across update/uninstall while continuing to enforce security-owned settings; add bounded leaf backups, shared locking, atomic writes, rollback, doctor validation, and legacy whole-file config migration.
- Add provider-safe delegation guidance to the working agreement, README, security documentation, and static showcase.
0.4.0 - 2026-08-29
- Replace the abstract cycle charts with an accessible interactive walkthrough that shows one gap moving through evidence, human choice, proof, retirement, and later review.
- Replace
@tmustier/pi-files-widgetwith an in-house, Tea House-native/filesbrowser using Pi's built-in syntax and Markdown renderers; remove the bat, git-delta, and glow prerequisites and retire their legacy managed binaries on update.
0.3.0 - 2026-08-29
- Complete the local improvement loop with explicit collection consent, deterministic evidence ranking, lifecycle decisions, and regression-aware retirement.
- Replace hard-coded implemented capability keys with a reviewed registry linked to closed
specpi doctorvalidators; the browser smoke now verifies both exact and changed pixel comparisons. - Add reversible exact alias decisions, local sanitized issue drafts, and recoverable checksummed archive/reset operations.
- Add the one-command
/harness-improvementmenu andspecpi-improveworkflow, with session-bound implementation authorization, repository and capability verification gates, and automatic retirement only after success. - Refresh the minimal README and showcase with explicit retired/review semantics plus accessible cycle and verification-outcome charts.
0.2.0 - 2026-08-29
- Add a native Windows command launcher and Windows-safe executable discovery for
pi.cmd,npm.cmd, and access-restricted Windows App Execution Aliases such aswinget.exe. - Invoke
.cmd/.batshims as a single quotedComSpeccommand, avoiding Node's deprecated shell-plus-arguments path. - Make the npm binary entry invoke Node directly instead of requiring a POSIX shell.
- Document platform-specific install commands and automatic dependency installation, and preflight the Pi 0.80.0 package API baseline.
- Add Windows installation smoke coverage.
- Offer missing bat, git-delta, glow, and DonSeTch tools individually during interactive installs;
--yesattempts all and--skip-tool-installopts out. - Pin bat 0.26.1, git-delta 0.19.2 (0.18.2 on Intel macOS), glow 3.0.0, and DonSeTch 3.4.0; use exact Winget installs on Windows and checksum-verified managed archives on Linux/macOS.
- Roll managed optional binaries back with failed installs and remove them on uninstall while documenting that Winget/global npm changes remain external.
0.1.0 - 2026-08-28
- Add explicit plan/install/update/doctor/uninstall workflow.
- Add managed AGENTS and shell blocks with backups and checksums.
- Add provider-safe strict native-subagent inheritance.
- Disable external Codex subscription runners.
- Bundle the Spec extension, Tea House theme, and DonSeTch skill.
- Make
/speca focused execution mode with persistent activity UI, collapsed tool output, per-turn guidance, session persistence, and full toggle restoration. - Add a privacy-minimized, task-deduplicated capability-gap collector and generated tool wishlist, with
/wishlistrendering the refreshed Markdown report directly in the conversation and retiring capabilities implemented by SpecPi. - Add managed isolated browser QA on hosts satisfying Playwright Chromium system requirements, with a pinned runtime, responsive viewport tools, bounded inline screenshots, explicit baselines, and pixel-diff artifacts.
- Add browser runtime staging, rollback, doctor smoke validation, and uninstall cleanup while preserving browser artifacts.
- Add a zero-dependency SpecPi showcase site with GitHub Pages publishing.
- Pin the reviewed Pi package baseline.