Version bump 2.5.3 -> 2.6.0 (MINOR — ships compass v2, daemon scheduler, @backup
restoration, telegram skill, tiered prompts). Bumped in both pyproject.toml and
src/aipass/__init__.py. CHANGELOG top section enriched into a 2.6.0 release
roll-up so the GitHub Release notes read properly. Rides into PR640.
README Readme standard was at 75%: compass module/handler undocumented and the
test count had drifted. Added compass to the architecture tree + a Compass
command section, bumped tests 236->282, refreshed the date stamp. devpulse audit
back to 100%.
Batch of S243/S244 work held for PR640. Only devpulse has git write, so all
branches' changes (@memory, @prax, @hooks, @aipass, @skills, @flow, @backup,
@seedgo) land through this single commit.
Memory rollover (correctness):
- @hooks rollover hook now delegates to drone @memory rollover check/run — it
had been reading stale .trinity limits (moved to memory.config.json by
DPLAN-0210), falling back to a 600-line check that never fired, so rollover was
silently dead for weeks. compact.py reads the current list schema. Both fail loud.
- @memory removed the v1 line-count/600 fallback entirely — v2-only, fail-loud
(959 tests).
Retire-for-all (DPLAN-0215):
- Legacy global prompt fully removed across every runtime: global_loader.py +
tests deleted, hooks.json/project_hooks.json blocks stripped, bootstrap global
seeding removed, cadence default + bypass cleaned, global .md files archived.
Codex SessionStart + Claude cadence read the same tier files.
Hardcoded-path cleanup (seedgo #37):
- New HARDCODED_PATH checker (#37). @memory symbolic.py + @prax branch_detector.py
home paths -> dynamic/generic. Both 100% Hardcoded_Path.
- seedgo provider_hooks_snapshot.json fixture refreshed to the tiered baseline.
Genericization: patrick -> user across tracked source, docs, templates.
CHANGELOG: 2026-06-19 + 2026-06-23 sections added.
Closes the deployment gap — cadence_config.json + the settings.json bridge are machine-local (gitignored), so fresh clones needed the tiered wiring seeded from committed sources:
- cadence.py DEFAULTS (keystone): adds tier0(period 1) + navmap(period 5) to the code fallback, so a fresh clone with no cadence_config.json gets tiered cadence automatically (was defaulting tier0 to period 5).
- setup.sh: fresh-install bridge seed now emits tier0_kernel + navmap, drops global_prompt.
- provider_manifest.json: doctor update path adds the tiered entries on existing machines, drops global_prompt.
Convergence: the committed hooks.json (global_prompt enabled:false) means even a stale settings.json with a global bridge is skipped by the engine. 615/615 tests (1 new: test_defaults_include_tiered_loaders), seedgo 100%.
Harvested from Claude Code's skill-authoring spec:
- when_to_use frontmatter field (trigger phrases) on all 3 SKILL.md templates + github catalog exemplar; discovery scan surfaces it so agents see triggers without loading the full body.
- Per-step 'Done when:' success criteria in the Steps section.
- 'Use when / Do NOT use when' structure in the When to Use section.
252/252 tests pass.
Replaces the single 8k always-injected global prompt with cadence-tiered injection:
- Tier 0 (.aipass/tier0_kernel.md, ~2k) injects EVERY turn — identity grounding, the drone --help reflex, disaster-preventer rules. Folds DPLAN-0213 C1 (faithful-reporting) + C2 (no-gold-plating sub-agent brief).
- Tier 1 (.aipass/tier1_navmap.md, ~7.7k) injects every 5th turn + session-start + post-compaction — full agent roster, framework, conventions, plus a new Terminology section migrated from the S211 backup.
- Old global_prompt loader retired (disabled in hooks.json, removed from cadence wiring); aipass_global_prompt.md kept as a reference snapshot.
Engine (built by @hooks): per-loader period in cadence.py should_fire() (back-compatible: unset period falls back to global); new tier0_kernel + navmap handlers; bypass.json extended to cover the two new dynamically-dispatched handlers. 614/614 tests pass, seedgo @hooks 100%.
Live-verified: tier0 fires every turn, navmap on turn 0/5/10, turn counter advances once per turn (not per loader), no double-injection. Machine-local wiring (cadence_config.json, ~/.claude/settings.json bridge) updated on this host; fresh-clone seeding of those is a tracked follow-up.
PROMPT_STYLE.md reference updated to point at the tier files as canonical examples.
- PROMPT_STYLE.md: new 'Writing voice' section (file_path:line refs, no-colon-before-tool-call, no emojis, write-for-a-person, three-tier where-detail-lives) — harvested from Claude Code's own prompt
- devpulse local: blast-radius habit before any drone write-op (reversibility + scope)
- global + devpulse-local: S241 whitespace/structure cleanup (readable English restored)
Compass guidance now lives in the public local prompt (compass is public). Recall
-> @memory; decide/fork -> compass query; good/bad decision -> compass add;
Patrick fires /compass. Old gitignored private_prompt injection now redundant.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Patrick fires /compass <rating> <note>; the model composes the decision text from
conversation context and stores via drone @devpulse compass add --source patrick.
The human-triggered answer to the 'noticing' problem. P4 (rate/archive/review)
already covered by P1+P2 — verified live (re-rate, archive-as-avoid-list, review
stamp, archived excluded from query).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thin command layer over the P1 storage core: add/query/stats/rate/archive/review
via drone @devpulse compass. Ratings shown in query output ([GOOD]/[BAD]/...),
--db flag for testing, auto-discovered (no devpulse.py change). 18 cmd tests,
seedgo 29/29, no regressions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add @skills/@daemon/@commons/@backup to global prompt agent list; trim+restyle
devpulse local prompt to PROMPT_STYLE (single # headers, no emphasis, de-dup vs
global); smooth .claude culture doc (kill repeats, drop mechanical overlap).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A PID containing "429" (e.g. 14290) in the monitor's own header line was
substring-matched as an HTTP 429, mislabeling sandbox-abort (-4) bounces as
"API rate limit" and flaking test_sandbox_failure_sends_bounce in push CI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR #640's only failing required check was Code scanning/CodeQL: 3 HIGH
py/clear-text-logging-sensitive-data alerts where get-secret printed raw secret
values to stdout. Research (OWASP, CodeQL rule source, secret-CLI survey)
confirmed a real exposure — acute for AIPass since it runs inside Claude Code,
which captures command stdout into model context, and the telegram skill
shelled out to get-secret and parsed the token from stdout.
@api (P1):
- NEW apps/modules/secrets.py — in-process cross-branch door (get_secret,
list_secrets) wrapping the auth handler; consumers import this, not the CLI.
- get_secret_cmd rewritten: masked summary by default ('slug: set (N chars)'),
--out FILE writes the raw value 0o600 and prints only the path, --list shows
slug names via console.print. All 3 raw-value print() sinks removed.
- bypass.json reasoning + README + help updated.
@skills (P2):
- telegram config._get_secret / list_bot_configs rewired from subprocess+stdout
parse to the in-process aipass.api.apps.modules.secrets API; subprocess/json
imports dropped. Tests + SKILL.md updated.
Also: seedgo test_checkers_batch2.py — comment the synthetic sk-or-v1 fixture
keys as FAKE (not real credentials).
Verified: @api 504 tests + seedgo 100%; telegram 452/452; skills 252/252; no
secret reaches stdout by any path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- telegram skill: register a minimal telethon sys.modules stub in the test
conftest so botfather_client tests (which patch telethon.*) run without the
optional MTProto library installed. Fixes 7 ModuleNotFoundError failures;
telegram suite now 452/452 green.
- pyrightconfig.json: add the root .venv site-packages to extraPaths (has
pytest + project deps) alongside memory's venv, so the @hooks auto_fix
pyright check stops emitting false 'Import pytest could not be resolved' on
every test file. CI does not run pyright; this is local-DX only.
Both CI-safe: telegram tests live under .aipass/ (excluded from umbrella
pytest) and pyright is not a CI gate, so PR #640 stays green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Once the collection-level blockers were fixed, the Windows runner finally ran the
suite and surfaced 7 pre-existing failures — all tests asserting Linux-only
behavior, while the production code already handles non-Linux gracefully:
- api test_secrets: chmod(0o000) can't make a file unreadable to its owner on
Windows (the 'unreadable -> None' precondition is unreachable)
- daemon test_scheduler_cron: patches fcntl.flock; fcntl is None on Windows
(scheduler_cron already skips locking on non-Unix)
- skills test_runner: system_status memory/uptime/processes/summary read Linux
/proc (skill returns a graceful error on Windows; disk test stays, it's portable)
Guard each with @pytest.mark.skipif(sys.platform == 'win32', reason=...). 68
tests pass on Linux, ruff clean, api+daemon audit 100%.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CI Linux seedgo-audit step failed: commons + daemon at 99%, readme 87%
('Directories in tree not found on disk: tools'). Their READMEs document tools/,
a gitignored branch-local runtime dir (like logs/, dropbox/) absent in CI's
tracked-only checkout. The readme-currency skip-list already covered logs/dropbox
but missed tools.
- Add 'tools' to the readme-currency runtime-dir skip set (readme_check.py)
- Test coverage in test_readme_content_checks.py
Verified: commons + daemon readme 100% (was 87%), overall 100% (was 99%);
seedgo self-audit 100%, 1060 seedgo tests pass. Work by @seedgo (FPLAN dispatch).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The custom norecursedirs in pyproject dropped pytest's default '.*' pattern, so
pytest recursed into .aipass/ scaffolding. The telegram skill bundled at
src/aipass/skills/.aipass/skills/telegram/tests/ (with __init__.py) made its
conftest resolve to module 'tests.conftest', colliding with branch tests/
conftest.py -> ImportPathMismatchError aborted collection on BOTH Linux CI and
Windows (the sole remaining green-CI blocker after the guard fix).
- Re-add '.*' to norecursedirs (skips .aipass/.trinity/.seedgo/... scaffolding)
- Verified: full src collection 9540 tests, no ImportPathMismatch; only the
telegram .aipass scaffold tests excluded (the single tracked test dir under
any dot-dir), all real branch tests still collected
Note: the telegram skill's bundled tests (7 failing locally) are out of umbrella
CI; skills owns stabilizing + properly integrating them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cross-branch import guard's same-branch check used a POSIX-only path test,
so on Windows (backslash paths) it failed to recognize a branch importing its
OWN handlers -> ImportError at collection, failing all commons + 2 daemon tests
on the Windows CI runner. (Unmasked once the pathspec fix let collection proceed.)
- commons: '/commons/' substring -> 'commons' in Path(caller_file).parts
- daemon + skills: add .replace('\\','/') before the check (matches the idiom
already used by 15 other branches' guards)
- Convert AIPASS_DEBUG_GUARD debug print() -> sys.stderr.write (cli standard;
avoids import-time logger dependency inside the guard)
Security semantics unchanged: same-branch allowed, cross-branch still blocked
(verified cross-platform). commons+daemon audit 100%, 700 daemon+skills tests
pass, commons 449 tests pass. (skills local 99% = untracked skills_json orphans,
not in CI.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Disabled files (AIPass convention: rename name(disabled).py instead of delete)
are intentionally-parked inert code, but seedgo audited them as live source —
ai_mail's dashboard_sync(disabled).py dragged it to 99%, blocking green CI.
- Add is_disabled_file() + DISABLED_FILE_MARKER to skip_dirs.py (single source
of truth alongside SOURCE_SKIP_DIRS)
- Apply in branch_audit, dead_code, unused_function, test_quality checkers +
test_map function_scanner + checklist directory mode
- New test in test_coverage_audit.py
Verified: ai_mail 99->100%, seedgo self-audit 100%, 1060 seedgo tests pass,
@cli/@flow unchanged at 100%.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The commons craft/trade/capsule subsystem lives at apps/handlers/artifacts/
but the blanket 'artifacts/' ignore (meant for branch-local runtime dirs)
silently excluded it from git. The tracked test_artifacts.py imports it, so
CI hit ImportError at collection while local passed (files present locally).
- Add *.py-scoped negation in .gitignore (keeps logs/ + __pycache__ ignored)
- Track artifact_ops.py, trade_ops.py, capsule_ops.py, __init__.py
- 19 test_artifacts.py tests pass; imports resolve
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add pathspec>=0.12 to root pyproject dependencies (was undeclared, caused
ModuleNotFoundError in CI across all Python versions + Windows)
- Rename drive_test.py -> drive_check.py so pytest stops collecting the module
as a test file; update MODULE_NAME, PRIMARY_COMMAND, help text, README, tests
- 220 backup tests pass, seedgo 100% all 37 standards
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Identity: normalize branch names to lowercase at both write paths so one
branch = one identity regardless of registry casing (registry has historically
mixed BACKUP vs devpulse, splitting the roster into DEVPULSE/devpulse rows).
- identity_ops.get_caller_branch(): _normalize_branch_name() at the single
caller choke point (post/comment author writes + agent registration).
- db._register_branches(): lowercase on the bulk registry seed.
Verified live: post author lands lowercase, no duplicate rows; uppercase-
registry branches (backup) normalize through the caller path too.
Test suite: session-scoped template DB cloned per test (shutil.copy) + fast
PRAGMAs (journal_mode=MEMORY, synchronous=OFF) instead of re-running
schema.sql+FTS5+registry per test. 449 tests now 86s (was >120s gate timeout);
full per-test isolation preserved, initialized_db interface unchanged.
test_identity: assertions updated for the lowercase caller path; monkeypatch
targets retargeted from the commons_identity facade to identity_ops (where
get_caller_branch resolves them).
.daemon/schedule.json: disabled wake-test seed (decentralized daemon contract example).
seedgo 100% (37/37), 449 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
handler.py run() received args as a DICT ({'arg0':'base'} from the skill
runner's _parse_extra_args), but _cmd_* consume a positional LIST -> args[0]
raised KeyError(0) (str '0') -> 'start failed: 0', no-op'd the live bot launch.
Add _normalize_args(): dict->list (arg0..argN -> values; key=value -> key,value),
list passes through. Verified live: 'status base' + 'start' route correctly via
the real drone runner. telethon_auth.py: guard optional 'from telethon import'
with type:ignore (matches botfather_client pattern).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- todos don't auto-roll (active work items, pruned by hand) — so when they pile over the per-branch count limit, edit_gate now emits a NON-BLOCKING advisory ('todos over limit (N/M) — prune completed ones') and still allows the save
- reads the count limit from @memory's rollover config (per_branch override -> defaults -> 10); todos-only, local.json-only; char-cap block still takes priority; config-load failure = silent skip
- 11 new tests (522 hooks total), seedgo 100%; verified live (fired 11/10, silent at 10/10)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- memory.config.json is the single source of truth: char caps (entry_limits, global) + rollover counts (per_branch, materialized from registry); .trinity files stripped to a one-line _usage header
- changed_entries gate now matches by content identity, not array position — prepending a new entry never re-flags unchanged legacy entries (old=old, new=new; no trimming required on a cap change)
- rollover push command + top-level 'drone @memory push' alias; corrected config _note + --help (rollover push surfaced, labeled destructive system-wide reset)
- removed 3 dead functions: seed_per_branch, its orphaned write_config, add_learning + its tests
- spawn birthright/builder + LOCAL/OBSERVATIONS templates aligned to the stripped shape
- 966 tests, seedgo 100%
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LOCAL.template.json: session_number->number, +tags:[], schema_version 3.0.0
OBSERVATIONS.template.json: pattern/source->number+note+tags:[], schema_version 3.0.0
New citizens now born in the unified schema (matches spawn templates from 7276e03).
Live 15-branch .trinity cleanup (drop session_number dup, unify obs->note) applied
+ verified by artifact (0 session_number, counts preserved, 34 backups) — gitignored local state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migration surfaced two consumers that still counted key_learnings as a dict: detector v2 trigger (at-cap list invisible -> fell to v1 line-count) and learnings/manager (used by rollover + symbolic). Made list-aware + dual-mode; +5 regression tests (detector counts a LIST, manager round-trip) — the gap 955 tests missed. rollover check now shows '25/25 key_learnings' (v2), was '609/500 lines'. 960 tests; seedgo 99% (pre-existing unused-function on unwired add_learning, not a regression).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gate now covers Write/Edit/MultiEdit via _resolve_after_text (reconstruct post-edit
text: Edit replace first/all, MultiEdit sequential) + _evaluate_limits +
_check_trinity_change, all reusing @memory changed_entries. Because only NEW/CHANGED
entries are checked, editing an unrelated field in a file full of legacy over-limit
entries is ALLOWED — proven on devpulse's REAL local.json under enforce=true
(unrelated todo edit allowed, over-limit edit blocked, file never written).
Fail-open on old_string-not-found / invalid-JSON / import error / any exception.
+17 tests (39 trinity, 511 hooks total), seedgo 100%, enforce false. @hooks side
complete. Warn-first build (Phases 1-5) done. Part of DPLAN-0205.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Additive gate in edit_gate.handle(): on Write to .trinity/{local,observations}.json,
lazily importlib-imports @memory's load_entry_limits + changed_entries and flags
new/changed over-limit entries. enforce:false → allow (warn); enforce:true → block
with reason. Fail-OPEN on bad JSON / import error / any exception (this hook runs on
every edit system-wide — never block on its own failure). Edit/MultiEdit pass
through (Phase 5). All 4 existing gates (inbox/daemon/cross-branch/edit-while-errors)
intact. +22 tests (494 total, 13 existing edit_gate green), seedgo 100%, enforce
false. Verified by artifact incl. fail-open + rollover-safe + char-not-byte proofs.
Part of DPLAN-0205.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
changed_entries(before,after,limits): pure diff that flags only NEW/CHANGED
over-limit entries, ignoring unchanged legacy fat — so rollover (trims by count,
writes back recent fat entries) is never rejected. Wired into write_memory_file
via _validate_entry_limits (gates only .trinity/{local,observations}.json): warn
mode logs+writes, enforce mode rejects new/changed over-limit only. Validation
wrapped in try/except → a validator bug can never abort a write. +15 tests (917
total), seedgo 100%, enforce stays false. Verified by artifact incl. live proof
of rollover-safety + the defensive guarantee. @memory side (P1-3) complete. The
changed_entries() helper is what @hooks imports next. Part of DPLAN-0205.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pure check_entry(type,text,limits) validator (chars not bytes, boundary at cap,
unknown-type safe) reusable by both gates. New 'drone @memory lint run' scans all
branches' .trinity via registry, handles dict+list containers and both
key_learning value shapes, sorts worst-first — strictly READ-ONLY (never writes/
trims, honors never_trim_s153). Phase-1 unused_function bypass removed (reader now
called). +12 tests (902 total), seedgo 100%. Verified by artifact incl. live lint:
513 over-limit entries across 17 branches (devpulse worst at 71, top offender
5724/600). enforce still false. Part of DPLAN-0205.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Config-driven char caps for .trinity memory entries. Phase 1 = foundation only:
adds entry_limits section to memory.config.json (4 caps: learnings 200, sessions
300, todos 200, observations 600) and the load_entry_limits(branch) reader
(deep-merge per_branch overrides, safe-defaults on missing/malformed). Reader has
NO callers yet (Phase 3 wires it) — unused_function bypass is intentional.
Verified by artifact: 14/14 tests, seedgo 100%, scope clean. enforce:false →
zero behavior change. Part of DPLAN-0205.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>