Releases: maskshell/solidforge-pi
Release list
v0.3.1 — HETERO_THINKING
Fleet-wide hetero thinking knob
HETERO_THINKING (project channel .env.solidforge, session channel shell export; off|minimal|low|medium|high|xhigh|max) pins both wrappers' different-family legs. Same-family legs keep inheriting the session's level; hetero legs keep model defaults unless overridden — never hardcoded in shipped profiles.
Requested-level semantics (the adversarial design review's decisive finding): pi silently clamps per model — glm-5.3-flash supports only low/high/max (off/minimal run low, medium→high, xhigh→max); qwen-bailian high→xhigh, max→medium. The sidecar stamps the REQUEST (hetero-leg-start.thinking); the clamp matrix is documented in README and both .env.solidforge.example copies. Wrapper-side fail-fast on invalid values is load-bearing — pi itself silently drops them.
Setting the env also suppresses per-provider MODEL=<id>:<level> suffix tuning (the suffix stays the per-provider tool). pd's wrapper has no sidecar — the knob is unobservable there (documented).
Also fixes a stale pd wiring assertion (pre-dated the 0.2.9 flash bump; the review found pd wiring missing from the gate sweep).
v0.3.0 — execution-process observability (ADR #69, pi re-base)
What reviewers DO, not just that they're alive
ADR #69 absorbed with an adversarial triage review that caught two blockers before any code landed: the CC event gate never fires on the pi wire (silently-empty audit trails — re-based onto tool_execution_start/message_end with an anti-silent-empty probe), and the omitted trace-append would have made execution_trace dead-on-arrival.
New surfaces
- Distilled execution streams: every csr leg leaves
round<k>-<leg>.stream.jsonlin the run dir — the wrapper distills the different-family leg live; the orchestrator persists the same-family leg'sexecution_traceat leg completion. Render:csr_progress.py stream <run-dir> [--tail N | --watch 5]. - All-runs view:
csr_progress.py status runs/— one block per ACTIVE run (no run-end event), concurrent-session safe;runs/LATESTdocumented as single-active-run-only with the state-your-run-dir-at-Frame convention. - Agent narration discipline + optional
execution_traceoutput (doc-reviewer, web-claim-verifier) — one what+why line per step, collected at report time. - roundIndex instruction in the SKILL (fixes a latent audit-trail corruption: unpassed rounds fell back to 1, appending into the wrong stream file).
Minor-version bump: new schema field, new CLI subcommands, new file formats in run dirs.
v0.2.10 — MCP proxy wiring
Agent fixes for the MCP adapter path
- playwright trio:
tools:allowlists now include the adapter's proxy toolmcp— the bodies already documented the two-step form (mcp({search})→mcp({tool, args})), but the missing allowlist entry made it uncallable from subagents. - graphiti-config-generator: properly pi-converted at last — allowlist (
find, grep, read, write, mcp) + instructions rewritten from CC-era forms (capitalized tool names, baremcp__graphiti__*names that do not exist on this substrate) to the proxy form. - README MCP section: adapter-shape explainer — one ~200-token proxy tool vs per-tool schema injection, lazy start; standard
.mcp.json/~/.config/mcp/mcp.jsonwork as-is, host-specific files (Cursor/.cursor/mcp.json, CC user store) import once via/mcp setup.
No engine changes — agents, docs, and allowlists only.
v0.2.9 — 2026-09-07 watch batch absorbed
Upstream absorption batch (all ledger rows closed)
- #67 web-claim-verifier (the fourth seat): agent contract verbatim + pi substrate adaptation (no native web tools → bash/curl fetch, caller-supplied URL reliable path); csr reconcile-moment spawn condition + psv single-claim note + ADR #67; 23 agents.
- #68 csr engine: sidecar round derived from prior-findings (unset --round-index no longer labels every leg round=1);
runs/LATESTatomic stable pointer (substrate-neutral mechanics; CC narration assertions deliberately not absorbed). - #69 profiles: review legs on the flash family (glm-5.3-flash / qwen3.8-flash, all 6 profile copies + README); CC-substrate env shaping not applicable.
- #59 fast_gate scope: project-root gating (out-of-repo scratch = silent no-op).
- #60 breaker inertia: terminal-state loops record but never escalate.
v0.2.8 — #63–#66 security mirror
Security release
The #63–#66 mirror of upstream CC 8a19c2e: seven ad-hoc project-local tool-resolution sites collapsed onto the canonical dt.resolve_tool — PATH-wins, explicit opt-ins (SF_PROJECT_NODE_BIN / SF_PROJECT_VENV_TOOLS), realpath containment. Before this, arch_contract_web/tests/deps, spectral_adapter, arch_contract_python (private unconditional venv resolver) and fast_gate's eslint site executed repo-committed binaries under gate authority, preferred over PATH — our tree was local-first, worse than CC's pre-fix ordering.
Also: the npx --no-install delegation arm is gated (a PATH tool is not PATH resolution); all converted legs execute with the resolved prefix (check/execution agreement); upstream's 26-probe test suite adopted verbatim; outer-ring reviewed (caught + fixed a govulncheck execution-site miss).
Deferred #67-#69 absorption candidates (web-claim-verifier agent, csr engine semantics, profile bumps) remain ledgered, unprioritized.
v0.2.7 — SF_PROJECT_NODE_BIN opt-in + arm-tools argument wiring
Two live-incident fixes (TDD, outer-ring reviewed)
SF_PROJECT_NODE_BIN=1 — project-local node tools, safely
The 0.2.4 PATH-only security fix made arm.py --with-tools-installed node_modules/.bin tools invisible to the gates (live observation: npm add -D eslint ... succeeded, status still absent). New explicit opt-in resolves .bin with a containment hard stop — an entry whose realpath escapes node_modules/ is refused even under the opt-in; PATH always wins; the 0.2.4 venv opt-in semantics unchanged. Connected fix: arm.py tool_present no longer reports tools "present" that the gates would refuse to run (root-threaded: the positional path reports against the target project). Tests: 10 cases incl. the npm in-tree-symlink shape and the escaping-symlink refusal.
arm-tools no longer drops your flags
The template never referenced $ARGUMENTS, so pi substituted invocation flags nowhere — /solidforge:arm-tools --with-tools --scaffold-configs silently armed without either (live incident). The template now wires ${ARGUMENTS:-<none passed>} and instructs parsing from that line; verified against pi's actual renderer; smoke assertion prompt-arguments-wired guards it (smoke now 8 checks).
Also in this release
- Repo armed Layer 2 (dogfood): configs, gate devDeps, blueprint templates, constitution
- CC handoff doc:
docs/outflow/sf-project-node-bin-handoff.md(adoption at the CC maintainers' cadence) - Outer-ring review (fresh context): PASS with I1–I3 follow-ups — all fixed in-tree before release
v0.2.6 — renamed to unscoped solidforge-pi
Rename release
@maskshell/solidforge-pi → solidforge-pi (unscoped, canonical public entry). npm has no rename — this is a clean cutover; the old name stays published as a deprecation pointer. Family strategy: public main entries unscoped (solidforge for DSH, solidforge-pi for Pi); scoped names remain as pointers.
No code changes vs 0.2.5 — identical content under the new name (first manual publish without provenance; provenance resumes with the OIDC path from the next release).
pi install npm:solidforge-piv0.2.5 — gallery preview, #8834 landing detector, e2e probe
Package surface
- Gallery preview (
pi.image→ docs/preview.png): a designed preview faithfully mirroring the real render formats — the hetero live panel (per-provider elapsed/turns/idle/cost/heartbeat), subagent disclosure lines, and the csr run-progress footer strip. - namespace-landing-detector.yml: weekly probe of official pi for
validateNamespaceValue; on landing, opens a deduplicated issue carrying the fork-retirement checklist. First live dispatch verified:result=NOT_LANDED(correct for official 0.84.4). - tools/e2e_probe.sh: the authenticated install-path probe as a script (marker round-trip through package load + extension init + LLM turn) — live PASS.
Security follow-through
- CC reference backported (same day): venv-exec PATH-only + loop_state fail-closed — upstream-watch OUTFLOW row closed.
docs/security-review-target-0.2.4.md: the hetero-leg review target is prepared (scope + already-caught list + hunt targets + verbatim diff). The leg itself remains blocked on credentials — honestly deferred, not dry-run theater.
v0.2.4 — security hardening + namespace-e2e CI + ADR #58
Security release (outer-ring dual review findings)
🔴 Blocker fixed — project-venev binary execution
detect_toolchain.resolve_tool fell back to executing <project>/.venv/bin/<tool> — a hostile repo could commit an executable there and have the gate run it with the user's full privileges (armed exactly when the tool was absent from PATH). Now PATH-only (per-project opt-in: SF_PROJECT_VENV_TOOLS=1). loop_state_path fails closed likewise. The CC reference implementation still carries both — ledgered as a SECURITY OUTFLOW candidate.
🟠 Major fixed — $-splice gate bypass
String.replace interprets $& $
Carve-out reachability + CI
- ADR #58 mapping carve-out now reachable through the pi bridge (ccPayload translates pi edits → CC Edit/MultiEdit shapes)
- New
namespace-e2eCI job: pi.namespace composition proven against the maskshell/pi fork build (7/7) — the #8834 landing detector - sf-hooks: mkdtemp temp hygiene, once-cached absolute ruff (TOCTOU + PATH-swap hardened), hard timeout bounds everywhere, decode-safe paths
Selftest 15/15; all gates + CI green.
v0.2.3 — pre-write lint gate, push/PR CI, pi-internals anchors
Hardening release (post-0.2.2 review follow-up, P0–P4)
sf-hooks: pre-write python lint gate (TDD, 9/9 seam selftest)
A red python edit/write is now denied at tool_call time — before the file is modified (would-be content linted on a temp copy with the file's own ruff config + mode). Timeout/hung-ruff/ruff-absent degrade to allow (hard 5s bound, resolves immediately). The post-write fast_gate message now discloses the edit already applied — the bare BLOCK that read as write-prevented caused a nearly-shipped contaminated commit on 2026-09-03.
ci.yml — the full gate set runs on every push/PR
Previously every gate depended on the author's local in-session hooks. Now: ruff (tools + all skill infra), per-skill lint_self/disconnect_check/plugin_layout, pi real-loader smoke (7 checks), sf-hooks seam selftest, markdownlint (maintained surfaces), npm-pack leak check, and a headless loader-e2e that imports + initializes all 5 extensions through pi's own jiti loader.
Docs & invariants
docs/pi-internals-anchors.md: the pi 0.84.x internal behaviors this package deliberately relies on (manifest-only discovery, barrel-hijack immunity, namespace composition, post-write block semantics) with a re-verify trigger on pi bumps- README Development section: local gate commands + contributor checklist (extension registration, bare agent names, PI_REF discipline)
"type": "module"; markdownlint adopted; plugin_layout gate bidirectional (a stray 23rd agent can no longer ride silently)
Process (dogfooded)
Outer-ring code review (fresh context) returned REQUEST CHANGES: 1 blocker (workflow YAML dead on arrival), 1 major (timeout→deny contract violation, empirically reproduced), plus minors — all fixed with hardened tests (AC6 now exercises the genuine timeout path). CI then caught a one-character SHA typo the local gates could not see. Green end-to-end before release.
Deferred: gallery preview image (no assets in repo; needs design input).