Releases: cdnwetzel/pxx
Release list
pxx 2.5.4
Added
pxx doctorflags self-review — the reviewer defaults to the author model.
Settings.effective_review_modelfalls back to the codermodelwhen no[roles.review]
overlay is set, so the shipped default puts the same model that wrote the diff in front
of it as the judge — whilepxx loop --reviewrunsBLOCKINGby default. A model cannot
independently review its own output: it carries the blind spot that produced the defect,
so the gate reports a pass it was never able to withhold, and that pass is then recorded
as evidence. This is the vacuous-gate failure mode, and it is worse than having no gate.
The newreview:independencecheck reports three states: a distinct reviewer model passes;
the same model on the same endpoint warns asSELF-review; the same model on a different
endpoint still warns, because separate hardware is not separate judgement — identical
weights carry identical blind spots. Warning-grade, never a hard failure: a single-model box
is a legitimate if weaker posture, and doctor reserves hard failures for the runtime itself.
The check comparessettings.model— whatSessionactually constructs — rather than the
authorlane, which resolves in config but is not yet wired to the runtime; reading it
would report an independence the loop does not have. Negative control per repo practice,
stated as the mutation matrix actually measured rather than as a summary: against a mutant
that always returnsokwith a plausible pass detail, the three negative-path tests
fail — including the one asserting the check is registered inrun_doctor, since a check
nobody calls is the vacuous case for the check itself — while the distinct-reviewer test
survives, because it is the positive control, not a negative one. The inverse mutant
(always warn) is caught by exactly that distinct-reviewer test and by nothing else. So the
four tests pin the check in both directions, with no single test doing both jobs. (A weaker
always-okmutant carrying a placeholder detail string fails all four, which is why the
first pass of this note over-claimed a 4-of-4 kill; CodeRabbit caught it on review.)
Verified on hardware both ways — a cleanHOMEwith no config fires
the warning out of the box; a configured two-box coder/judge split passes.
Docs:docs/CONFIG.md§[roles.review]gains an Independence note.
Known limit: doctor reports this, the loop does not — a run that never invokes
pxx doctorstill gets a blocking gate it cannot fail. MakingBLOCKINGmode itself
refuse, or require an explicitallow_self_reviewopt-in, is carded and not in this release.
pxx 2.5.3
Changed
- Greenfield gate soundness locked with a regression guard. A clone-from-docs probe
raised a concern that the regression-relative test gate might vacuously pass broken
greenfield code (a node appeared to reachCOMPLETEDon a module with aSyntaxError).
A deterministic reproduce disproves it:run_loopon a suite that fails from round 1
terminatesLOOP_DETECTED/ a non-success code, neverCOMPLETED
(tests/test_loop.py::test_greenfield_failing_baseline_never_completes) — the apparent
pass was a confound (lost per-round log + a sandbox whose state did not match the recorded
outcome), the same failure mode as the earlier "planner fails greenfield" report. The gate
is sound; a regression guard now locks that in and the roadmap claim is withdrawn. No
behaviour change — a test + doc correction that hold the gate to its negative-control
discipline (reproduce cleanly before carding a gap).
pxx 2.5.2
Added
- Goal planner runs on the
roles.planlane.pxx goal's read-only planner now
resolvesSettings.effective_role("plan")(via a testable_planner_settingshelper),
so a reasoning/planning model can decompose the goal into the task DAG while the coder
builds it — the second consumer of the 2.5 role-lane map, matching a separate PLAN role
(reasoning brain ≠ builder). Falls back to the coder model when[roles.plan]is unset
(byte-identical to before). Surfaced by the clone-from-docs probe. NOTE (honesty): the
probe's initial "planner fails greenfield with OUT_OF_SCOPE" report was a harness
artifact — auv run --directoryinvocation ran the planner in the wrong repository; a
clean reproduce showspxx goalworks greenfield end-to-end. So this is an architectural
enhancement (reasoning-planner support), not a bug-fix. +2 tests.
pxx 2.5.1
Fixed
- Embedding-space versioning for observation memory (fail-closed on embedder swap).
Stored vectors carried no record of which embedder produced them, so changing the memory
embedder (or theroles.embedmodel) silently madesearch()compute cosine across
incomparable spaces — confident garbage with no error, and worst in observation memory,
which accumulates for months and is never reindexed. The store now stamps its
embedding-space identity in ametatable, andset_embedderfails closed with
EmbeddingSpaceErrorwhen a different identity is attached over existing vectors —
targeting the dangerous same-dimension / different-model case (a plain dimension
mismatch already threw). Recovery:MemoryStore.reset_embedding_space()clears the
vectors + stamp and detaches the embedder (re-add observations to re-embed under the new
embedder — it does not auto-reindex), or repoint to the original embedder. Empty stores
adopt a new embedder freely; pre-versioning
stores with vectors assume the current embedder (logged warning, not a hard fail).
Embedders now expose anidentity(HashEmbedder→hash:dim=N,OllamaEmbedder→
ollama:<model>). This is the retrieval analogue of the content-truthfulness gate —
refusing to answer from a corrupt index is refusing to fabricate, not judging. +10 tests
including a same-dim/different-model negative control. Prerequisite for the forthcoming
repo code index; flagged by PSAIOS in coordination.
pxx 2.5.0
Added
- Generalised per-role model lanes —
[roles.<name>]. The reviewer overlay
([roles.review], shipped 2.2.0) is widened to a closed, validated role-lane
map:author,reviewer,plan,fast,verify,embed. Each lane names a
(provider, model, base_url, api_key)quadruple resolved late against the coder
model, so different roles can run on different families/endpoints — the driver
is family independence on the judgment axis (the reviewer/verifier should not
share the author's lineage).Settings.effective_role(name)is the one resolver
role consumers call. New per-lane env parityPXX_<ROLE>_{MODEL,PROVIDER,BASE_URL, API_KEY}(reviewer keepsPXX_REVIEW_*).reviewis a back-compat alias for
reviewer;review_model/[roles.review]/PXX_REVIEW_*/--review-*are
unchanged and the reviewer path is byte-identical (proven by the existing review
suite staying green). An unconfigured box is byte-identical to before — every
unset lane resolves to the codermodel. The closed set is fail-closed on an
unknown role; opening it later is non-breaking, narrowing is not. - Egress guard covers every lane. Role routing is a data-egress surface for all
lanes (the diff for the reviewer, source chunks forembed, the prompt for any
lane), so the entire[roles]table is honoured only from user config, env, or
CLI — a repo-localpxx.tomlcan redirect no lane (ignored with a warning).
Every lane inherits the reviewer's exfil protection by construction. pxx owns
role→model name; endpoint/node placement stays a pluggable adapter (never
a second placement authority) — advancing the ROADMAP "model-backed boundary
roles" item.embedis reserved for the forthcoming repo code index.
Fixed
- User-config symlink trust check now uses the repo root, not just cwd. The
symlink-into-project guard (which downgrades a~/.config/pxx/config.toml
symlinked to repo-editable content to untrusted) compared the target against
cwd. From a nested working directory, a symlink targeting a repo file that
is an ancestor of cwd escaped the check and was trusted — letting a repo file
smuggleallow_ungated_shell/memory_capture_successes/ hooks / role-lane
routing past the A0b gate. Containment is now checked against the repository root
(_repo_boundary, nearest.gitancestor), closing the bypass; regression test
added. Found pre-flight by CodeRabbit while reviewing the role-lane map (whose
new lanes widened this guard's responsibility).
pxx 2.4.4
Changed
- Review prompt: severity discipline to stop small reviewers over-flagging. The review gate's
system prompt (pxx/prompts/review.md) told the model toAPPROVEonly with "no high or medium
findings" but never that style, comments, renames, test-only edits, import order, and refactors
that keep tests green are not defects — so smaller judges rated acceptable-change nitpicks as
mediumand votedREVISE. Added an explicit rule reservinghigh/mediumfor real correctness
or safety defects; minor/stylistic observations arelowat most and never a reason to REVISE.
Measured on the calibration corpus attemperature:0:qwen2.5-coder:32bfalse-positive rate
0.429 → 0.143 (now passescalibration ok) andqwen2.5:14b-instruct0.429 → 0.286,
with recall unchanged at 0.857 (precision up, no loss of critical-bug detection). This makes
a fast, GPU-resident local model a viable blocking reviewer. NOTE: the calibration corpus is
small (14 cases) — treat the magnitude as indicative pending held-out validation on real diffs.
pxx 2.4.3
Fixed
- Reviewer/judge now decodes greedily (
temperature: 0) — the review gate is reproducible.
The model-backed reviewer's request omittedtemperature, so against an Ollama endpoint the
server default (0.8) made the judge nondeterministic: the same diff produced different
verdicts run-to-run, andpxx calibrateswung across itsMIN_AGREEMENT/MIN_RECALL
thresholds — so a calibrated reviewer couldn't be trusted to stay calibrated, and a single
calibration near a threshold was a coin-flip.pxx/review.pynow sendstemperature: 0, pinning
the judge to greedy decoding. Surfaced by areachable → unparseable → calibrate → replace
reviewer diagnostic that exposed the swing. Test asserts the reviewer payload carries
temperature == 0.
pxx 2.4.2
Added
- Content-truthfulness gate (advisory) — a new axis, separate from permission. Scope /
R-014 governs what the agent may touch; it does not catch a model that stays fully
in-scope and still reports something false about the code (quoting a comment that isn't
there, presenting invented code as real). The objective gates (lint/tests/diff-cap) catch
broken edits, not confident-but-wrong claims. This ships the first increment: deterministic
quote-grounding (pxx/truthfulness.py). Every non-trivial code span the model quotes in its
final narration must appear in content it actually read or wrote — the union of read
tool-results and edit-tool args accumulated across the run. Grounding is checked
per-source (a quote must sit inside a single source, so it can't be assembled from two
unrelated reads) and only edit-tool args count as "written" (a non-edit tool arg is
model-supplied, not read content, so it can't launder a fabricated quote). Fenced blocks are
checked line-by-line (a model that quotes a function but elides its docstring is being
terse, not fabricating; a single invented line is what flags), tolerating spaced info strings
and CRLF; inline spans are checked only when they look like code (whitespace-normalized, no
model, fully deterministic). - Wired into the native loop's COMPLETED path as an advisory, non-blocking, fail-safe check:
an ungrounded quote emits a metadata-onlycontent_truthfulnessevent (count + a kind
breakdown, never the quoted code — the audit stream carries no file contents) plus a
warning log, but never changes a run's outcome — thetry/exceptswallows any checker
error so an advisory can't break a run. Advisory-first is deliberate: the false-positive rate
is measured on real runs (like the reviewer's calibration) before the gate is ever
promotable to a heal trigger. Prove-before-you-call-it. - Negative control (shipped in the tests): a fabricated quote MUST flag and a real quote
MUST pass — a check that cannot go red is not a check.content_truthfulnessregistered in
theEVENT_KINDSallowlist.
pxx 2.4.1
Added
--review-model/--review-base-urlrun flags — the per-role reviewer/judge
overlay ([roles.review]/PXX_REVIEW_*, shipped 2.2.0) is now settable per-run on
the command line, no config file needed. Available on every command that runs the
reviewer: the run/loop commands (alongside--model/--base-url) and the reviewer
commandspxx review/pxx calibrate. The flags layer as the highest-precedence
[roles.review]source (user TOML → env → flags, per field), sopxx loop --review --model <coder> --base-url <coder-url> --review-model <judge> --review-base-url <judge-url>runs the coder and the judge on different endpoints in one invocation.
Absent the flags a run is byte-identical to before. Tests: CLI→overlay mapping (run +
review + calibrate), flag-over-env precedence (model and base_url), and no-flags no-op.
The Kimi K3 Swarm audit — an independent architecture + quality audit
(2.8T-parameter frontier model, high-effort) of the repo at v2.3.7 — landed as
three waves: validated bug/security fixes, then the loop-closing feature, then
the learning-loop completion. Every item rode the normal gate (verified in a real
venv → PR → CI + CodeRabbit). Notably, the Wave 1 memory-capture fix was authored
by pxx fixing its own bug on local hardware (receipt R-034).
Added
memory_retrieval_limitsetting + stable settings overlay (Wave 2 — closes
the improvement loop): theimprove/plane (candidates, promotion guards,
shadow/canary, autopromote) never changed a production run.memory_retrieval_limit
is now a realSettingsfield (default8== the historical_SEARCH_HITS, so
an unconfigured box is byte-identical; strict positive-int TOML parse) consumed
by memory injection.apply_stable_overlay()applies the STABLE channel's
settings-class candidate at session start — re-validated (content-hash tamper
check), budgets tighten-only against the current budgets, CLI-pinned keys
always win, fail-closed but never bricking (a broken/tampered/absent artifact →
base settings + a warning). This unblocks live (model-scored) eval arms.- Opt-in success-exemplar capture
memory_capture_successes(Wave 3, default
off, byte-identical when off): a COMPLETED run writes exactly one compact
session_outcomeexemplar of bounded shape metadata only (files-changed /
tool-call counts — the raw task prompt is not persisted, since this durable row
later becomes prompt context),contamination_riskbelow auto-quarantine,
provenance from the completed-run ladder, deduped so identical verified shapes
growseen_count— the recurrence signal the graduation ladder consumes.
Enabling it turns on persistent memory writes, so the key is honoured only from
a trusted source (user config /PXX_MEMORY_CAPTURE_SUCCESSES/ CLI — never a
repo-localpxx.toml, A0b). Preserves the Phase 20.5 "no silent
success-to-knowledge" default. Also:MemoryStorededup now upgrades an
observation's provenance label (not only its numeric confidence) when a
stronger-evidence recurrence arrives.
Fixed
- Memory capture read the wrong event key (Wave 1, R-034): the
tool_result
branch readresult/output, but the tool bus emitsresult_preview— so
every real tool-result observation was silently dropped. Now reads
result_previewfirst (legacy keys kept as fallbacks); failed tool calls
(error=True) are captured at low confidence so they stay distinguishable. - Unbounded
git worktree add(Wave 1):improve/channels.pyand
improve/scheduler.pyranworktree addwith notimeout=— the two sites the
2.3.6/R-030 git-bounding missed. Both nowtimeout=30and degrade to the copy
fallback onTimeoutExpired(R-030's boundary amended to name the gap).
Security
- Fail-closed secrets gate on auto-commit (Wave 1):
commit_session_worknow
scans the staged delta (governance.scan_staged, already fail-closed) before an
auto-commit; any finding or an unrunnable scan → no commit, work left staged. - The PR-time governance scan is now armed (Wave 1): same-repo PRs/pushes run
pxx check --all-files --require-denylist(fork PRs stay unarmed with a loud
warning), closing the 1.3.x silent-green hole where an empty denylist passed
silently. Arming immediately surfaced — and this release fixes — fleet host
names leaked into the public receipts corpus;docs/RECEIPTS.mdnow describes
hardware by capability, not by hostname/IP.
pxx 2.4.0
The Kimi K3 Swarm audit — an independent architecture + quality audit
(2.8T-parameter frontier model, high-effort) of the repo at v2.3.7 — landed as
three waves: validated bug/security fixes, then the loop-closing feature, then
the learning-loop completion. Every item rode the normal gate (verified in a real
venv → PR → CI + CodeRabbit). Notably, the Wave 1 memory-capture fix was authored
by pxx fixing its own bug on local hardware (receipt R-034).
Added
memory_retrieval_limitsetting + stable settings overlay (Wave 2 — closes
the improvement loop): theimprove/plane (candidates, promotion guards,
shadow/canary, autopromote) never changed a production run.memory_retrieval_limit
is now a realSettingsfield (default8== the historical_SEARCH_HITS, so
an unconfigured box is byte-identical; strict positive-int TOML parse) consumed
by memory injection.apply_stable_overlay()applies the STABLE channel's
settings-class candidate at session start — re-validated (content-hash tamper
check), budgets tighten-only against the current budgets, CLI-pinned keys
always win, fail-closed but never bricking (a broken/tampered/absent artifact →
base settings + a warning). This unblocks live (model-scored) eval arms.- Opt-in success-exemplar capture
memory_capture_successes(Wave 3, default
off, byte-identical when off): a COMPLETED run writes exactly one compact
session_outcomeexemplar of bounded shape metadata only (files-changed /
tool-call counts — the raw task prompt is not persisted, since this durable row
later becomes prompt context),contamination_riskbelow auto-quarantine,
provenance from the completed-run ladder, deduped so identical verified shapes
growseen_count— the recurrence signal the graduation ladder consumes.
Enabling it turns on persistent memory writes, so the key is honoured only from
a trusted source (user config /PXX_MEMORY_CAPTURE_SUCCESSES/ CLI — never a
repo-localpxx.toml, A0b). Preserves the Phase 20.5 "no silent
success-to-knowledge" default. Also:MemoryStorededup now upgrades an
observation's provenance label (not only its numeric confidence) when a
stronger-evidence recurrence arrives.
Fixed
- Memory capture read the wrong event key (Wave 1, R-034): the
tool_result
branch readresult/output, but the tool bus emitsresult_preview— so
every real tool-result observation was silently dropped. Now reads
result_previewfirst (legacy keys kept as fallbacks); failed tool calls
(error=True) are captured at low confidence so they stay distinguishable. - Unbounded
git worktree add(Wave 1):improve/channels.pyand
improve/scheduler.pyranworktree addwith notimeout=— the two sites the
2.3.6/R-030 git-bounding missed. Both nowtimeout=30and degrade to the copy
fallback onTimeoutExpired(R-030's boundary amended to name the gap).
Security
- Fail-closed secrets gate on auto-commit (Wave 1):
commit_session_worknow
scans the staged delta (governance.scan_staged, already fail-closed) before an
auto-commit; any finding or an unrunnable scan → no commit, work left staged. - The PR-time governance scan is now armed (Wave 1): same-repo PRs/pushes run
pxx check --all-files --require-denylist(fork PRs stay unarmed with a loud
warning), closing the 1.3.x silent-green hole where an empty denylist passed
silently. Arming immediately surfaced — and this release fixes — fleet host
names leaked into the public receipts corpus;docs/RECEIPTS.mdnow describes
hardware by capability, not by hostname/IP.