20 KiB
Changelog
All notable changes to AIPass will be documented in this file.
The format is based on Keep a Changelog,
and this project uses Calendar Versioning in the format
YYYY.WNN (year and ISO week number).
[2026.W23] - 2026-06-02
Added
- Cross-OS end-to-end WIRING test (
tests/e2e/,e2e-wheel.yml) — the first CI gate that proves real AIPass wiring (not units-with-mocks) by building the wheel, installing it into a clean venv, and asserting a 4-tier ladder: package install + console scripts (T0),aipass initscaffolding (T1), a hook actually firing via the bridge with an observableengine.jsonlrecord (T2a), anddroneresolving + subprocess-executing a real branch (T3). Runs on a 3-OS matrix (ubuntu/windows/macos,fail-fast: false). Ran red-first on Windows by design and immediately earned its keep — it caught two real, previously uncovered Windows wiring bugs (aipass initpreflight +dronestdout encoding, both fixed below). Notably the layers we most feared — clean-wheel install (T0) and hook firing (T2a) — passed on Windows. (DPLAN-0194 / FPLAN-0239) drone rm— provider-agnostic safe delete — a contained recursive delete that lets agents clean up scratch dirs without tripping therm -rfblock. Deletes are confined to the project root and the system temp dirs (/tmpand$TMPDIR), refusing anything outside (home,/etc,/, etc.). Even inside those roots it hard-refuses protected internals —.git,.trinity/,.aipass/,.codex/,.agents/, and sibling-branch worktrees — mirroring the filesystem boundary an OS-sandboxed agent (e.g. Codex) enforces, so behavior is consistent across CLIs. Pure-Python (shutil.rmtree), with a red-team test suite for containment escapes (symlinks, traversal, sibling branches). (#630)rm_gatehook — block raw recursiverm, teach the safe path — a PreToolUse gate (mirroringgit_gate) that blocks rawrm -r/-rf/-fr/--recursiveand redirects the agent todrone rm. Provider-agnostic (runs in the hook engine, not tied to Claude Code permission rules), conservative (unparseable targets are blocked, not allowed), and skipsdrone rmitself. This makes the safe-delete path discoverable at the moment of friction. (#630)- Hook engine logs
agent_type/agent_idper fire — the engine now records which agent triggered each hook (e.g.agent=mainvsagent=Explore) in bothengine.jsonland the prax monitor stream. Previously the payload flowed into handlers but was never logged, leaving no way to tell an internal main-turn fire from a real sub-agent fire. Pure visibility; no behavior change. Groundwork for #606. (#606) - OpenSSF Best Practices passing badge — AIPass earned the OpenSSF Best
Practices (CII) passing badge (100% of criteria), added to the README badge
cluster. Self-certified across all six categories — basics, change control,
reporting, quality, security, and analysis. Complements the existing OpenSSF
Scorecard, lifting the
CII-Best-Practicescheck from 0. (DPLAN-0193)
Changed
-
Standards floor raised to genuine 100% across all 13 branches — completed the campaign that lifted the seedgo gate threshold from 80 to 100. Rather than bypass failing files, two check flaws were fixed at the root: (1) the file-size / architecture check is now advisory (warn-only for 700–1500 line files with no docstring nudge, hard-fail only above 1500) — large files are a smell, not a defect; (2) readme-freshness now compares against git history, not file mtime —
git checkout/mergereset mtimes without any semantic change, so the old check false-positived (flow + prax shared an identical mtime from one git event, not real edits). It now diffs the README's "Last Updated" against the last commit that touched.py. Genuine content fixes where warranted (aipass requirements template + handler routing; honest README content refreshes on flow, prax, devpulse). The readme-freshness failure message now teaches the right fix ("update README content, then set the date — don't just bump it"). Also optimized the devpulse watchdog poll cadence (2s → 5s; the loop is cheap, so the tighter interval was wasted CPU). (#631) -
Retired the blanket
rmdeny from provider settings —setup.shandaipass initno longer shipBash(rm -rf*)/Bash(rm -r *)deny rules (they were mis-filed among git rules, blocked all/tmpcleanup, and gave a bare "permission denied" with no guidance). Therm_gatehook +drone rmnow own this — cross-provider, path-aware, and they teach.aipass doctordetects the stale rules on existing installs andaipass doctor --fixremoves them (idempotent, preserves all other rules). Claude Code still natively circuit-breaksrm -rf /andrm -rf ~. (#630)
Fixed
- Two latent Windows portability bugs caught by the new e2e harness — both
were always present in the code; they only surfaced now because this is the
first CI to run
aipass initscaffolding and real-branchdronerouting on Windows (the old Windows CI ran an editable install,aipass-less, and only routed to in-process modules, so both paths had zero Windows coverage). Pure portability fixes — Linux/macOS behaviour is unchanged.aipass initcrashed on Windows (surfaced as a misleading "Unknown command: init"). Init scaffolded the project correctly, then crashed printing its✓ Project initializedbanner — Rich wrote the ✓/box glyphs through a cp1252 stdout, raisingUnicodeEncodeError ('charmap'); the error handler's✗message hit the same wall, bubbling up to the command router which mislabeled it. Theaipassentry point now reconfigures stdout/stderr to UTF-8 in place on Windows. (The init preflight ancestor-walk was also hardened to skip un-enumerable Windows drive-root entries — defensive, not the trigger.)drone @branchcrashed on Windows with the sameUnicodeEncodeError ('charmap').droneresolved + subprocessed the branch correctly, then crashed printing the captured output through cp1252 stdout. The existingPYTHONUTF8guard only affected child interpreters, not the live process streams —drone's entry point now alsoreconfigure()s stdout/stderr to UTF-8 in place.- CI unit lane no longer runs the e2e wheel tests.
ci.yml'spytest --rootdir=.swept intests/e2e/(which build a wheel per the dedicatede2e-wheel.yml), failing the unit lane; it now--ignores them. (DPLAN-0194)
- A release merge can no longer destroy the
devbranch —drone @git mergepassed--delete-branchtogh pr mergeunconditionally, so merging adev→mainPR deleted the persistentdevbranch on the remote and stranded the working tree onmain(the next commit silently landing on main). Merge now looks up the PR's head ref and only deletes non-protected branches —devandmainare never deleted, and an undeterminable head ref fails safe (no delete). After a merge it returns the working tree todev(loud warning if it can't).drone @git branchesnow runsfetch --prunebefore listing so it reflects the live remote instead of stale cached refs, and a newdrone @git prune-tempcleans up merged temp PR branches. (#625) drone @git status/diffshow their scope — when scoped to a branch (no--all), output now appends "(showing scope — use --all for full repo)", so an empty scoped view is no longer mistaken for a clean repo. (#623)- External projects can call AIPass branches via drone —
drone @api ...(and anydrone @X) now resolves from a non-AIPass project CWD instead of being blocked with "path escapes project root." The resolver was validating a branch's path against the primary registry root even when the branch was found via theAIPASS_HOMEfallback, so any external project (Vera Studio, Daemon) hit a false security block.resolve_branch()now validates containment against the registry the branch was actually found in. Security is unchanged — each branch is still contained within its own declaring registry's root; genuine path escapes remain blocked. (#618) aipass <command>runs instead of printing an introspection banner —aipassis a user-facing binary, soaipass doctor(and every other command) must execute, not describe itself. All 7 modules (doctor,doctor_fix,doctor_wire,handoff,help_chat,init_flow,profile) previously hit a no-args→introspection gate (a standard meant fordrone @branch <module>discovery) and showed a banner on bare invocation. Now bare invocation runs the command or shows usage; the introspection banner moved to--info. The seedgo introspection standard is bypassed for these binary-invoked modules (documented).- Dashboard plan counts no longer zeroed on refresh — a branch's
active_planswas reset to0by everydrone @prax dashboard refresh, becausePLANS.central.jsononly held Flow's own plans (location==FLOW_ROOTfilter). The central file is now comprehensive: all plans grouped per-branch, so refresh reports each branch's real count (e.g. devpulse now shows its 12 open plans instead of 0).
Security
- Pinned the
requestsfloor to a non-vulnerable version — raisedrequeststo>=2.34.2inpyproject.tomland the API branch'srequirements.project.txt(which previously listed it unconstrained). This clears six OSV advisories the OpenSSF Scorecard flagged against the dependency (PYSEC-2014-13, PYSEC-2014-14, PYSEC-2018-28, GHSA-9wx4-h78v-vm56, GHSA-9hjg-9r4m-mvj7, GHSA-gc5v-m9x4-r6x2) — the oldest surfaced only because the dependency was declared without a version bound. No runtime change (the AIPass venv already ran a fixed release). (DPLAN-0193) - Pinned the test container base image by digest —
Dockerfile.testnow pinsubuntu:24.04to its registry digest (sha256:786a8b55…) so the test image is reproducible and tamper-evident, clearing the ScorecardcontainerImage not pinned by hashfinding. (DPLAN-0193)
[2026.W22] - 2026-05-30
Added
drone @hooks status— read-only viewer for a project's hook config: master switch, every hook's enabled state per event group, matchers, and an enabled/total summary. Resolves the project's.aipass/hooks.jsonby walking up from CWD. (DPLAN-0190 Phase B)- Hooks activate in every project —
aipass initnow writes.aipass/hooks.json, so new projects fire the hook engine out of the box (previously: no config shipped, 0 hooks fired).aipass init updateunion-merges the template, preserving any per-hook on/off choices the user made.aipass doctornow checks for the config's presence. Dead hook-script shipping (_ship_hooks) removed. (DPLAN-0190 Phase A) - README logo — centered logo image replaces plain
# AIPassheader. Newassets/logo.pngadded to the repo. - OpenSSF Scorecard —
.github/workflows/scorecard.ymlruns the official OSSF Scorecard action on push tomainand weekly. Publishes a public security health score at scorecard.dev with a README badge. Actions pinned by SHA. - GitHub Releases —
publish.ymlnow cuts a GitHub Release on eachv*tag, with notes pulled from the top CHANGELOG section and the built dist attached. PyPI publish + GitHub Release now fire from the same tag. - Registry descriptions — all 13 branches now have one-liner descriptions
in
AIPASS_REGISTRY.json.drone systemsshows what each agent does instead of blank lines. Closes #607.
Changed
- Security gates fully project-aware — both the edit gate and the
subagent stop gate now derive the package name dynamically from CWD instead
of hardcoding
src/aipass/. Cross-branch write protection and branch detection work for anysrc/<package>/<branch>/project; previously the subagent gate silently no-opped outside AIPass. 9 new external-project tests. Closes #605. - Hooks branch promoted to service — registry profile changed from
"AIPass Workshop" to "library" so it appears in
drone systemsalongside the other 12 services. - Hooks branch hardened to 100% seedgo — the @hooks citizen took full
ownership of its branch: every handler verified wired + firing, README
rewritten (two-tier provider/project model, dynamic-dispatch design, event
table), 2 stale tests resolved (253 pass). Dead-code/unused-function flags
documented as architectural bypasses — the 15 handlers are invoked
dynamically via
importlibfromhooks.jsonpaths, never statically imported. (DPLAN-0191)
Release
- Version 2.5.0 published to PyPI. Trusted publishing via GitHub Actions
(
publish.ymltriggers onv*tags — no manual twine upload needed). The same tag now also cuts a GitHub Release with these notes attached.
Removed
- Gemini CLI full removal — deleted
.gemini/directory (5 files) andGEMINI.md. Stripped all references fromsetup.sh(~50 lines),README.md,bug-report.yml,aipass init(bootstrap/scaffold/test), hooks (README/prompt/passport), and prax monitoring (~300 lines). 21 files changed, -927 lines. Closes #608.
[2026.W21] - 2026-05-25
First weekly release. AIPass now follows a Sunday release cadence: changes
accumulate on dev throughout the week and merge to main as a single
versioned release with notes.
Added
- Hook engine — a new centralized dispatch system for all hook execution. A thin bridge receives events from the AI provider (Claude, Codex, etc.) and routes them through a single Python engine that reads per-project configuration, executes the appropriate handlers, and logs every invocation. Replaces 14 standalone shell/Python scripts with native handler modules organized by domain: prompt injection, security enforcement, lifecycle management, and notifications.
- Per-project hook configuration via
.aipass/hooks.json. Each project can enable, disable, or customize individual hooks without touching provider-level settings. Previously hooks fired globally with no per-project control. - Audio feedback on hook events using Piper TTS. All 14 handlers
produce distinct spoken audio cues so operators can monitor sessions
without watching the terminal. A shared sound module
(
hooks/apps/sound.py) providesspeak()andplay()with built-in mute support. Toggle withdrone @hooks hooksound on|off— muting silences all 14 handlers without skipping their functional logic. - Hooks agent — the 13th citizen in the AIPass registry, owning all hook infrastructure: the engine, bridge, handlers, and configuration schema.
- Dashboard plugin for devpulse — aggregates git status, session history, and dispatch state into a single startup view. Wired into the session startup protocol so branch managers see current state immediately.
- External log routing — prax now accepts structured log entries from any branch, not just its own modules. Hook executions, dispatches, and agent activity all flow into the central monitoring log.
Changed
- Provider settings fully migrated to bridge pattern. All hook entries in the Claude provider configuration now call the bridge dispatcher instead of individual scripts. Each hook produces its own system-reminder to the model, preserving prompt injection fidelity (a single merged bridge was found to break prompt delivery due to Claude Code's output persistence threshold).
- setup.sh rewritten to install hooks via the bridge pattern. The old version hardcoded 14 script paths; the new version writes a single bridge call per event type and validates that the bridge module exists.
- Documentation sweep across
.claude/README.md,SECURITY.md, the global prompt, and branch-level docs to reflect the new hook architecture. References to legacy.claude/hooks/scripts replaced with the native handler locations. aipass init updatenow correctly preserves user-customized hook settings during project updates instead of overwriting them.- Seedgo snapshot tests rebuilt — the provider hooks snapshot fixture and extraction logic were structurally broken (silently passing with zero results). Both the fixture format and the test assertions have been corrected.
- Test suite updated for hook migration —
test_git_gate.pyimports from the new handler module;test_bootstrap.pyno longer asserts that project initialization ships standalone hook scripts (it no longer does).
Fixed
- Settings merge on project update —
aipass init updatewas clobbering user hook configurations. The merge logic now layers AIPass defaults under existing user settings. - Python 3.10 test collision — a module/function name collision caused mock patch targets to fail on Python 3.10. Test targets corrected.
- Dead code removal — removed an unused CLI
__main__.pyentrypoint and cleaned up.gitignoreentries that were masking tracked files. - Codecov patch threshold lowered to 50% to reflect the project's current coverage baseline and stop false-negative CI failures.
Removed
- 18 standalone hook scripts in
.claude/hooks/disabled (renamed with(disabled)suffix). Their logic now lives in native handler modules undersrc/aipass/hooks/apps/handlers/. The old files remain on disk for reference but are no longer executed. drone hook-soundsplugin disabled. Sound control moved to hooks branch asdrone @hooks hooksound on|offwith full mute support for all 14 handlers (the old plugin only controlled 4).
Infrastructure
- Provider manifest migrated to bridge pattern.
provider_manifest.jsonnow stores bridge commands ($AIPASS_HOME/...bridges/claude.py EventType) instead of standalone script names.doctor_wire.pyauto-wires bridge entries directly — no longer copies scripts to~/.claude/hooks/or generatessys.executablepaths. Doctor checks validate commands exist in provider settings instead of checking for script files on disk. - README v3 — rewritten for external users. Tighter problem/solution framing, collapsible agent details, Gemini CLI removed (untested), user-project perspective throughout.
- Inline handoff (
aipass init runStep 11) — new default stays in the current terminal viaos.execvpinstead of opening a new window. Users choose "stay here" or "new window." Enables single-terminal demo recordings. Closes #610. - Project-aware global prompt — the global prompt loader now detects
whether CWD is inside AIPass or an external project. External projects
receive their own lighter prompt (from
.aipass/aipass_global_prompt.md) instead of the full AIPass-internal playbook. Fixesdrone @praxerrors in new projects. - Project CLAUDE.md template —
aipass initnow generates a project-specific CLAUDE.md from.aipass/project_CLAUDE.mdinstead of copying the AIPass-internal one. Removes the startup protocol reference todrone @prax dashboard refreshwhich doesn't exist in external projects. - Gemini CLI removed from
aipass initCLI choices and handoff options. GEMINI.md no longer created for new projects. Gemini CLI is being retired upstream. - Demo GIF added to
assets/demo.gifand referenced in README.
This is the first CHANGELOG entry. Prior work is documented in the repository's commit history and branch session logs.