Repository navigation
[2026-10-27] — v2.8.29: the new-project system after Vera-Studio's day (AGENTS.md managed, the gitignore template and merge, updateignore, doctor's shadow and coverage rows), the watchdog per seat, a manager writes in its citizens, Codex wake lanes and parity
Added
-
aipass:
aipass init,newandadoptseed.aipass/.gitignore(a whitelist, so the scaffold manifest and.aipass/.backup/are never committed) and a root.updateignoreholding only its header, the owner's file for what update must never touch. -
aipass:
aipass init updatemerges a project's.gitignoreline by line: template lines the file lacks are appended under one dated comment, no owner line is removed or reordered, and--dry-runprints the lines. It seeds a missing.aipass/.gitignoreand.updateignorewithout rewriting existing ones (Vera-Studio's dry run: four lines to add). -
aipass: a new project's
.claude/settings.local.jsoncarriesautoCompactWindow: 350000, the manager-seat default that needed a hand edit through the settings gate; existing files are never given it. -
aipass:
aipass doctorStructure rowgitignore coverage, read-only: runtime files on disk that git tracks (.chroma,docs.local,dropbox,*_json,.spawn, the registry json) get one FAIL per folder withdrone @git untrackas the fix; this is the check that would have caught Vera-Studio's 144 tracked files. -
aipass:
aipass doctorStructure rowinstruction shadowFAILs a liveCLAUDE.md,.claude/CLAUDE.mdorCLAUDE.local.mdbeside anAGENTS.md(Claude Code reads AGENTS.md only when no CLAUDE file sits in cwd or above, so the project's AGENTS.md was never read: Vera-Studio and four other scaffolded projects, all passing doctor), says whether the two texts match, and--fixrenames it to(disabled): never a copy, delete or overwrite, never under$HOME, never through a.claudesymlinked outside the root (structure_scanner.py1.2.0,doctor.py1.10.0,docs/doctor.md; ten tests, 1411 green). -
hooks: a project's manager writes inside its own citizens' branches (
modules/write_ownership.py2.5.0,docs/edit_gate.md; the owner's ruling 2026-10-26, the same shape as #808 for git). @vera was refusedCross-branch write blocked: vera cannot write to writeron both lanes because the manager exception covered only files outside every branch row. A non-AIPass project's manager row now writes its whole project; a citizen into a sibling citizen's row is still refused, crossings between projects are still refused, and inside AIPass the verified admin seat stays the only exception. One new test, two pins reversed, 3/3 mutants red, 2781 green. -
hooks:
navmap_cap(prompt/navmap.handle_edit, PostToolUse) says at edit time when.aipass/tier1_navmap.mdis over its 8,000-char cap, with the count, instead of the injection cutting it silently near 10,000. -
hooks: the project template (
.aipass/project_hooks.json) carriesculture,session_pointer,settings_gate,settings_backupandnavmap_cap, so an external seat gets the culture file, a resume pointer for BAUD, the provider-settings gate and the navmap warning.compass_recall,auto_process,comment_budgetandpresence_gatestay host-only, reasons insrc/aipass/hooks/docs/project_config.md. Existing projects take the entries throughaipass init update. -
drone:
drone @git push: a project's manager pushes its own repo's checked-out branch to origin, never forced, no arguments. In AIPass it refuses and namesdev-pr;push --repostays the admin door. -
drone:
drone @git issue create|comment --repo AIOSAI/AIPassfrom any seat in any project, the body signed with the seat and project (--bodyonly;--body-file,--editorand--webrefuse there). Issues are the contribution channel; close, edit and delete stay owner-tier. -
drone: git writes are fenced by the session, not the cwd: owner and ci-cure verbs refuse when the running Claude Code session started outside the project (its transcript sits under the launch directory, which a
cdcannot move), unless the seat is admin. Gate and door logs carry the session id and where it started beside the seat. -
hooks:
drone @hooks parity [--branch @canary|<path>] [--lane A,..,F] [--provider claude,codex] [--json]runs the same scripted prompts headless on Claude Code and on Codex from one branch's cwd and compares what the hooks did, row by row: the twelve UserPromptSubmit handlers, SessionStart, the four gates (cross-branch write, raw git, raw rm, new test file), PostToolUse, Stop and SubagentStop, the over-cap.trinityedit. The oracle islogs/engine.jsonl: every per-hook line now carries the payload's session id, so a session's whole hook record is read back by the id each CLI prints. Accepted differences (culture native on Claude, the sub-agent mechanism) are marked, never counted; exit 1 on a real difference or a session that left no record, so the merge train can gate on it. First live run on canary: 32 rows, all same or accepted.docs/provider_parity.md. -
daemon: a job's
wakeblock names the provider next to the model:"wake": {"fresh": true, "model": "gpt-6-sol", "provider": "codex"}. Absent is claude, wake.py's own default, never a second copy; a value that is notclaudeorcodexis refused at discovery by owner, job id and value. The scheduler pins both, the owner's ruling (discovery.py2.6.0,run.py1.9.0,rotation.py1.4.0,job_reference.py1.1.0,docs/schedule_contract.md; six pins in existing test files, six mutants red; proven live by a one-off canary job that woke on Codex, register row 371e953d). -
aipass: the handoff's end choice is Claude Code or Codex, and the provider does not matter: choosing codex launches
codex --dangerously-bypass-approvals-and-sandbox(the posture the rooms, the/codexskill and dispatch use, hooks being the gates) and the way back iscodex resume --last; Claude's launch and breadcrumb are unchanged.aipass doctorgains four read-only Codex rows aftercodex hooks: the binary and its version,codex login status, the configured model checked againstcodex debug modelsfor this login, and the four config pins (memories off, plan mode off three ways), each missing pin's fix being the exact TOML line to add by hand;--fixnever touchesconfig.toml, and a pin proves its bytes unchanged (handoff_platform1.1.0,_doctor_wire.py1.4.0,provider_wire.py1.7.0,doctor.py1.8.0,docs/two_clis.md; 1382 green, eight mutants red). -
aipass, hooks: Codex hooks move to the user level, so a Codex seat in any project runs under the same 24 hooks as inside AIPass. @hooks measured Codex 0.164:
$CODEX_HOME/hooks.json(else~/.codex/hooks.json) fires from every cwd; the repo-level.codex/hooks.jsonloads beside it with no dedupe, so both files together fire every hook twice; trust is keyed<file>:<event>:<group>:<index>on content with no cwd, so one trust covers every project; untrusted rows skip silently. The writer now targets the user-level file and merges rather than replaces: AIPass rows (the ones running the Codex bridge) are replaced wherever they sit and placed first in each event, every foreign row and top-level key is kept behind them, backup and receipt as before. The tracked repo.codex/hooks.jsonleaves the tree. Doctor gainscodex trust(atrusted_hashunder each AIPass key in config.toml, else WARN naming the one key to press) andcodex double-fire(FAIL while a repo-level file still holds bridge rows;--fixleaves the project's file). The bridge is unchanged (provider_wire.py1.8.0,_doctor_wire.py1.5.0,tests/test_provider_wire.py1.9.0 withCODEX_HOMEpinned to tmp for every test,docs/two_clis.md,docs/doctor.md; 1387 green, eight mutants red). -
devpulse: the watchdog sees a Codex session. A branch whose session pointer names provider
codexis watched through its one rollout under~/.codex/sessions, re-read from the pointer on each refresh so a/codexswitch is followed; a Codex turn is in flight until the rollout's last line is anevent_msgof typetask_completeorturn_aborted(Codex writes no session-end record), an unparseable tail counts as in flight. The wire and the sign-in take this seat's session id fromCLAUDE_CODE_SESSION_ID, else from a Codex pointer's thread id (agent.py1.6.0,dispatches.py2.1.0seat_session_id,wire.py2.5.0,watchdog.py2.2.0,docs/watchdog.md; seven pins in the existing test files, one mutant red). ai_mail'sdoor_session_pointer1.1.0 exportscodex_thread_file,CLAUDE,CODEXfor it. -
seedgo:
host_portabilityarm G flags a stdlib import newer than pyproject'srequires-pythonfloor (tomllibon a 3.10 floor;zoneinfo,graphlibon older ones) unless the sametry/iffalls back to the backport (import tomli as tomllib); asys.version_infogate alone is not an acquittal, it moves the degraded answer to the floor's lane without running it. The floor is read from the nearestpyproject.toml, none means the arm is off. It is the species behind PR #811's 3.10-only red: fourteen aipass Codex tests whose degraded except branch no newer seat ever ran. Three pins red-first on the provider_wire reduction, five mutants red, fleet 0 live rows after seedgo's own cure (host_portability_check.py1.5.0,host_portability_content.py1.4.0,host_portability.md,docs/aipass_standards.md; FPLAN-0707). -
seedgo:
host_coupled_oracle, a tests-only standard (branch level): a test that passes its ownos.getpid()/getppid()to an args/cmdline/title reader, or pins a literal runnercall_count == Non a function that both forks on platform and runs a subprocess, is host-coupled and fails. Born of PR #807's three clone-only reds (pytest-xdist retitles its workers; macOS reads args throughps). Two pins on reductions of the real tests, seven mutants red, fleet 0 hits after hooks' cures.
Changed
- aipass: a project's
AGENTS.mdis managed by manifest hash like the tier files (current / update / kept-local with the.aipass-newsidecar when the owner edited it), with an.updateignorerow as the opt-out; it was a seed written once and never revisited, so tonight's template fix reached no existing project. A manifest from the seed era carries no trusted hash, so the first update never overwrites an edit an older update adopted (scaffold_manifest.py1.1.0,bootstrap.py2.5.0). - aipass: a new project gets a project navmap stub (its name, its citizens from the registry, the drone basics) in place of the host's roster map, which described AIPass's citizens in every external project. An unedited stub follows the roster on update; a host map or the project's own map is kept and the stub offered as
.aipass-new. - spawn:
drone @spawn updatereads the project-root.updateignoreas well as the branch's, matching root patterns against the project-relative path on the gitignore model (src/vera_studio/*/docs.local/protects every citizen, a slashless name matches at any depth), so one file at a project's root protects all its citizens; the branch-local file stays as an optional extra and either one protects. A wildcard directory pattern now also withholds the bare directory row; an unreadable root file refuses before any write (update_ops.py2.5.0,update_ignore.py1.2.0,docs/update_engine.md; five new tests in the existing file, 3/3 mutants red, 1127 green). - prompts:
.aipass/tier1_navmap.mdtrimmed under its 8,000-char cap (it was 9,422, cut silently at injection); the "Finding your way" section went, the kernel already says it; the settings line names the Codex files beside Claude's. - README: the install line says Codex as a choice (a second CLI, each agent runs on either, per task), no longer "optional", matching aipass.ai.
- ai_mail: managers wake on Codex in the same lanes as on Claude: headless in the scheduled and admin lanes, and through a tmux session running
codex --dangerously-bypass-approvals-and-sandboxon a @daemon self-wake. The Claude-only refusal of--provider codexfor acitizen_class: manageris gone; the owner's ruling, after Vera's first Codex release post needed a hand-opened pane (wake.py3.8.0,dispatch.py3.7.0,docs/wake_pipeline.md,tests/test_wake.py1.5.0: one refusal pin becomes four cases, two mutants red).
Fixed
- seedgo: the provider hooks snapshot fixture (
tests/fixtures/provider_hooks_snapshot.json) carries the ClaudeSessionStart:culturerow the provider manifest gained, so the sixtest_hooks_snapshotreds are green. - hooks:
README.mdback under seedgo's 10,000-char README cap (was 10,099, the ratchet fleet walk red): seven Documentation-table descriptions shortened, wording only. - devpulse: the watchdog wire's identity primitives are public names (
stdout_target,cmdline,looks_like_ours,session_dir_ofinwire.py2.7.1): the tests that prove them against a real process reached them as private names, seedgo'sthrough_the_commandrow on the pre-push audit; no behaviour change,test_watchdog_wire.py2.4.1 follows. - aipass: the Codex config reads (
doctor's model and pin rows,set_codex_pinsbehindsetup.shanddoctor --fix) fall back to thetomlipackage wheretomllibis absent, the structure scanner's own shape, so Python 3.10 reads and pinsconfig.tomllike 3.11+; before, 3.10 answered "needs Python 3.11+" on every row and skipped the pins, and the fourteen Codex tests were red on CI's 3.10 lane (PR #811 run 40477098528). Without either parser the rows still say so, naming tomli now (provider_wire.py1.9.1). - seedgo: the test inventory reads
pyproject.tomlon Python 3.10 throughtomliinstead of falling back to pytest's defaults on asys.version_infogate; without either parser the note names both (test_inventory/collection.py1.0.3, arm G's one fleet hit). - ci: the standards gate's expected count moves 83 → 84 for
host_coupled_oracle, which landed without movingEXPECTED_STANDARDSin.github/scripts/seedgo_audit.py; the tripwire fired on the first branch of the local pre-push run and would have been the standards job's red. - aipass: the project
.gitignoretemplate covers the runtime state spawn and the services write (.chroma/,docs.local/,dropbox/,artifacts/,tools/with a negation example,**/*_json/,.spawn/,.backup/, the registry and roots json,.env, local plans, WAL sidecars,.claude/worktrees/); Vera-Studio had 144 of one installation's files tracked on the old template (scaffold_content.py1.4.0). - aipass:
aipass init updateretires a liveCLAUDE.md,.claude/CLAUDE.mdorCLAUDE.local.mdbesideAGENTS.mdto(disabled)with doctor's shadow fix and its refusals, and retires the pre-2.8.20.claude/hooks/*.pyscripts the manifest recorded while they are unedited. - aipass:
aipass init <target> --dry-runtook the flag as the project name and scaffolded a--DRY-RUN_REGISTRY.json; flags are refused before anything is written. - devpulse: arming the watchdog wire takes over only this seat's older wires; a wire registered by another seat (a project manager's,
metadata.seat) is left alive and named on stderr, where before every live wire in the registry was SIGTERMed and the other manager's completions went unreported. Entries without a seat (pre-2.6.0) are taken over as before (wire.py2.6.0). - devpulse: the watchdog's seat is the sealed owner of the caller's project, read through
AIPASS_CALLER_CWD, where before drone's cwd switch made every seat@devpulse: @vera's wire delivered devpulse's completions, took devpulse's wire over as a same-seat arm, andwatchdog statusfrom her seat counted devpulse's dispatches as hers (#810). Each seat now drains the feed through its own cursor files (devpulse_json/<seat>.feed_cursor.json, same for the dead cursor), so one manager's drain never marks another's completions as seen;statuslists the caller's seat's dispatches only and names the seat on each wire row (dispatches.py,feed.py,presenter.py,wire.py2.7.0). - memory:
drone @memory config get|set @branchresolves citizens of declared roots (@vera), so BAUD's settings panel saves for a project citizen;process-plansvectorizes every visible project's.backup/processed_plans(host, resident projects, declared roots), tags each chunk with its project, andsearchshows the project under the hit. - flow: a closed plan archives into its own project's
.backup/processed_plans, never the host's (an external's plan history had landed under$AIPASS_HOME/.backup); restore looks there too. - flow:
drone @flow listshows the caller's project's plans;list allfrom the host seat still lists every project. Plan numbers stay global. - aipass: the project
AGENTS.mdtemplate (.aipass/project_AGENTS.md) and its generator follow the host startup protocol: memories read one file per call, no README read at startup, dashboard refresh and read, and the todo pad rules. It had drifted; a project scaffolded from it inherited the old startup.AGENTS.mdis a seed file, soinit updatenever rewrites an existing copy; onlyinit,newandadoptwrite the new text. - hooks: Claude Code seats in external projects get the AIPass culture file at SessionStart (the Claude manifest has the
SessionStart:culturerow now); insideAIPASS_HOMEClaude reads.claude/AGENTS.mdnatively and the hook stays silent, so nothing is injected twice. - drone:
drone @git commit --allno longer dies with E902 in a project with no roottests/, and no longer rewrites files before refusing: the lint gate runs first over the dirs that exist, thenruff check --fixandruff formattouch only the changed files and the result names every file they rewrote. - aipass: projects get the provider switch.
init,newandadoptwrite/codex(.claude/commands/codex.md) and$claude(.codex/skills/claude/SKILL.md) as managed scaffold files,init updateadds them to older projects and keeps edited copies, and doctor'sprovider switchrow names a missing one, which--fixwrites. Before this the pair lived only in the AIPass tree, so no external project could switch CLI. - commands, skills: the switch files (
/codexin.claude/commands/codex.md,$claudein.codex/skills/claude/SKILL.md) point their prep step at.agents/skills/prep/SKILL.md, where prep lives since the shared skills folder; they named.claude/commands/prep.mdand.codex/skills/prep/SKILL.md, neither of which exists. - aipass, setup.sh: a fresh install with
codexon PATH installs the Codex hooks again.setup.shhad looked for the repo's.codex/hooks.json, retired, and skipped the Codex step, leaving a new Codex seat ungated; the block it would have run also rewrote~/.codex/config.tomlwholesale with deprecated keys and a default model. Nowsetup.shrunsprovider_wire.refresh_codex(), the same planaipass doctor --fixapplies:cli.codexmerged into the user-level$CODEX_HOME/hooks.jsonwith the owner's own rows kept, then the four Codex pins (memories,collaboration_modes,include_collaboration_mode_instructions,tools.experimental_request_user_input.enabled, all false) set one line each inconfig.toml, every other line kept, after a backup and with a receipt. A config.toml the line edit cannot set safely (a dotted key, an inline table) is refused and left untouched.refresh_provider_hooks()is Claude-only now. The install summary and the README name the user-level file and the one-time trust step. - hooks:
drone @hooks verifyreads Codex's hook rows from the user-level$CODEX_HOME/hooks.json(~/.codex/hooks.json), the only place they live since the repo.codex/hooks.jsonretired; it had kept reading the repo path and reported the Codex-only culture handler as unwired. A missing user-level file is now an ERROR naming the cure (aipass doctor --fix, then trust the hooks once incodex), not a skipped check, since a Codex seat without it runs ungated. One home rule incontext_window.py(codex_home()) serves bothcodex_config_path()and the newcodex_hooks_path(). The settings gate keeps protecting the retired repo path so the file never returns unnoticed beside the user-level one. - aipass:
aipass doctorrun from an external project's root no longer reportsAIPASS_HOME splitagainst that project. The terminal's tree is the registry root when that root is an AIPass tree (it holdssrc/aipass); in an external project it is the tree the project's.claude/settings.local.json(thensettings.json) names inenv.AIPASS_HOME. Two AIPass trees still fail the row (doctor.py1.7.1,tests/test_doctor_home.py1.2.0, three pins, two mutants red). Found by Vera-Studio's update to 2.8.28, the first run of the release ritual. - memory: rollover's trim re-reads the memory file before writing and removes only the archived entries, so an edit made while vectors were being stored (a new todo, a meta bump) is no longer overwritten by the image read at extraction. Found at 12:16 on devpulse's own pad: one Edit added a todo and pushed sessions to 11, the rollover it fired had read the file three seconds earlier, and its trimmed write put that older image back (
extractor.py0.9.0,orchestrator.py1.4.1,tests/test_orchestrator_exec.py1.1.0 pins the real sequence, two mutants red;docs/rollover_pipeline.md). - hooks: on Codex, an
apply_patchthat names no file is denied with that reason and logged at WARNING; it is a malformed call, not a broken engine, so it no longer raisesHOOKS BROKEN. PostToolUse on such a patch runs nothing (bridges/codex.py2.5.0,tests/test_codex_bridge.py2.5.0, four mutants red).