diff --git a/.aipass/.gitignore b/.aipass/.gitignore index 150f0d08..89af5913 100644 --- a/.aipass/.gitignore +++ b/.aipass/.gitignore @@ -1,10 +1,10 @@ * -!aipass_global_prompt.md !tier0_kernel.md !tier1_navmap.md !hooks.json !.gitignore +!README.md +!PROMPT_STYLE.md !project_CLAUDE.md -!project_global_prompt.md !project_hooks.json #Do not add other exceptions here without careful consideration. Developer permissions0ns needed. \ No newline at end of file diff --git a/.aipass/README.md b/.aipass/README.md new file mode 100644 index 00000000..ba55209a --- /dev/null +++ b/.aipass/README.md @@ -0,0 +1,76 @@ +# `.aipass/` — project prompt & hook config + +This folder holds the **project-level prompt** and **hook configuration** for the AIPass +repo, plus the **templates** `aipass init` stamps into every new project. It is the +*project* layer; each branch additionally has its own branch prompt at +`src/aipass//.aipass/aipass_local_prompt.md`. + +> **Nothing here is dead weight.** Every file is live injection, live config, or a +> required new-project template. Superseded files live in `.archive/` (never deleted). + +## One prompt system, every runtime + +There is **one** source of prompt truth — the **tier files** — and **all** runtimes inject +the same content. We do **not** keep separate prompts per CLI. Only the *delivery* differs: + +| Runtime | How the same content is delivered | +|---|---| +| **Claude Code** | **Tiered by cadence** (FPLAN-0284): `tier0_kernel.md` every turn + `tier1_navmap.md` periodically + post-compaction | +| **Codex CLI** | Injected **once at SessionStart** (no per-turn cadence): the same tier content, combined | + +> ⚠️ **Migration in progress.** The Codex SessionStart hook +> (`.codex/hooks/session_start_identity.py`) currently still reads the legacy +> `aipass_global_prompt.md`. @hooks is wiring it onto the tier files. **Retire for one +> runtime = retire for all** — once Codex is on the tiers, `aipass_global_prompt.md` is +> read by nothing and moves to `.archive/`. + +## Files + +### Live — this repo's prompt + config +| File | What it is | +|---|---| +| `tier0_kernel.md` | **The kernel** — tiny identity + `drone --help` reflex + don't-get-lost rules. The always-on core, for every runtime. | +| `tier1_navmap.md` | **The navmap** — full agent roster, framework, terminology. The periodic/fuller layer, for every runtime. | +| `hooks.json` | Claude Code **handler registration** for this repo — which prompt/gate/notification handlers fire on which events. | +| `PROMPT_STYLE.md` | The writing-style guide every prompt here follows. | +| `.gitignore` | Whitelist guard — only files listed here are tracked; everything else in `.aipass/` is ignored. | +| `aipass_global_prompt.md` | **Legacy single global — being retired.** Disabled for Claude Code; Codex still reads it until its migration lands, then archived. **Not** the source of truth. | + +### Templates — stamped into new projects by `aipass init` (`bootstrap.py`) +| File | Stamps → | Notes | +|---|---|---| +| `project_hooks.json` | new project's `.aipass/hooks.json` | **REQUIRED** — without it a new project's hooks never fire. Mirrors the live wiring (tier0 + navmap enabled, global disabled). | +| `project_CLAUDE.md` | new project's `CLAUDE.md` | the project's Claude Code instructions. | +| `project_global_prompt.md` | new project's `aipass_global_prompt.md` | **Legacy** — same retirement path as the global above (new projects ship tiers-only once Codex is migrated). | + +(`AGENTS.md` — Codex's equivalent of `CLAUDE.md` — is **generated** by `bootstrap.py` +when no `project_AGENTS.md` template exists, so none is kept here.) + +## What a new project gets (`aipass init`) + +`bootstrap.py` seeds a fresh project with the tiered system: +- `tier0_kernel.md` + `tier1_navmap.md` → the prompt content (every runtime) +- `hooks.json` (from `project_hooks.json`) → tier0 + navmap enabled, global disabled +- `CLAUDE.md` (from `project_CLAUDE.md`) + a generated `AGENTS.md` +- `aipass_global_prompt.md` (from `project_global_prompt.md`) → legacy, retiring with the above + +`aipass init update` backfills the tier files + refreshes hooks for existing projects. + +## Changing a prompt here + +Run the **prompt-change playbook** so a change reaches every runtime and every seed path: + +``` +drone @flow create . "What changed" prompt_change +``` + +Golden rule: **live ≠ seeded.** Editing this folder fixes *this* repo only. New projects +come from the `project_*` templates + `bootstrap.py`; fresh clones get their machine-local +wiring from `setup.sh` + `.claude/provider_manifest.json` + `cadence.py` defaults. And +**every runtime** (Claude Code + Codex) must point at the same tier content. + +## Archive & recovery + +Superseded files move to `.archive/` (never deleted — house rule). Recover from there, or +from git history, any time. Current archive: the pre-tiering +`aipass_global_prompt.BACKUP-2026-06-09-S211.md` snapshot. diff --git a/.aipass/aipass_global_prompt.md b/.aipass/aipass_global_prompt.md deleted file mode 100644 index 61d10711..00000000 --- a/.aipass/aipass_global_prompt.md +++ /dev/null @@ -1,119 +0,0 @@ -# AIPass — Global Prompt (SUPERSEDED) - - - -Persistent Agent Workspace. AIPass is the system: autonomous agents (citizens) with identity, memory, and a mailbox, providing services to each other and to external projects. Each agent lives in a branch — its home and address. Everything routes through `drone`. - -# Drone — the router - -`drone` reaches every agent and service. Installed binary, always on PATH — run directly, never as a python module. - -``` -drone @agent [args] # route a command to any agent -drone @agent --help # full curated reference for that agent -drone @agent # bare → introspection: the agent's live self-map -drone systems # list all agents -drone --help # drone itself -``` - -One reflex above all: before using an agent's services, run `drone @agent --help`. This prompt says what exists — `--help` says how. Don't guess syntax; fetch it. Doubly so right after a compaction. - -# Finding your way - -You can't carry everything; you can find anything. This prompt plants breadcrumbs — what exists and where to look, not the full answer. Cheapest, highest-signal sources first: - - - bare `drone @agent` — introspection: the agent's live self-map of modules and commands. - - `drone @agent --help` — the full curated reference. Source of truth for usage. - - the agent's `README.md` — best quick overview of its domain and shape. - -# Git — drone only, devpulse only - -Raw `git`/`gh` are blocked — `drone @git` is the only interface. Write ops are devpulse-only; everyone else gets read-only awareness: `drone @git status / diff / log`. - -# The framework - -Every branch is built the same. All agents live at `src/aipass/` · mail address `@`. - -``` -src/aipass// -├── .trinity/ # identity & memory (passport, local, observations) -├── .aipass/ # branch prompt -├── .ai_mail.local/ # mailbox -├── apps/ -│ ├── .py # entry point -│ ├── modules/ # business logic -│ └── handlers/ # implementation details -├── logs/ # prax log output -└── README.md -``` - -# The agents - - - @drone — command router. Resolves `@agent`, routes commands, enforces tier-based access. Also the only git interface (`drone @git`). - - @devpulse — orchestration hub, the user's primary collaborator. Coordinates the other agents, dispatches work, only agent with git write. - - @aipass — the user-facing front door and a system-ops collaborator. Onboarding (`aipass init`), `doctor` diagnostics, help chat, handoff; also partners with the user on host-level health (disk, thermal, docker, config). Concierge to other branches: reads, never writes. - - @ai_mail — inter-agent email. `dispatch` = send + wake (default for handing work), `email` = no wake, plus inbox/view/reply/close. - - @flow — plan lifecycle: create, list, close, templates, registry. Plan types in the Plans section — never create plan files by hand. - - @seedgo — code standards and audits. The standard pack, `audit` and `checklist`, the quality gate before and after building. - - @prax — logging and monitoring. The only logging system: `from aipass.prax import logger`. Real-time monitor, dashboards. Logs are the first diagnostic tool. - - @memory — long-term memory. Archives overflowing `.trinity/` files into searchable vectors; `search` recalls past sessions. Nothing is lost — it moves deeper. - - @spawn — branch lifecycle. Creates, updates, syncs, retires agents — scaffolding, passports, registry, templates. - - @hooks — Claude Code hook engine. Prompt injection and cadence, security gates (git/edit/rm), bridges, per-project config, sound. - - @trigger — event handling. Pub/sub event bus, error detection (medic), log watching, error registry. Detects and dispatches — owners fix. - - @api — external API gateway. Authenticated service clients (Google, OpenRouter, more), OAuth flows, key management, resilience. - - @cli — display formatting with Rich. Shared rendering for terminal output. - - @skills — capability framework. Discoverable, self-contained skill units any agent can run; consume AIPass services as opt-in imports (e.g. the Telegram skill). - - @daemon — task scheduler. Cron-triggered firing; each branch owns its `.daemon/schedule.json`, the daemon discovers and fires. - - @commons — the social space. Where branches post, comment, vote, and gather as a community. - - @backup — local-first backups. Project-owned snapshots and restore for any directory; no external service. - -# Daily commands - -``` -drone @ai_mail dispatch @target "Subject" "Body" # send + wake -drone @ai_mail inbox # check mail → view → reply "msg" -drone @flow create . "Subject" [dplan] # new plan (default FPLAN) -drone @seedgo audit aipass @branch # standards audit (drop @branch = all) -drone @seedgo checklist # quick standards check -drone @git status / diff / log # read-only git awareness -drone @memory search "query" # recall archived context -``` - -Always reply to dispatches — reply auto-closes. No silent completions. - -# Plans — flow - -Plans carry context so you don't have to. Create only via `drone @flow create "Subject" [type]` — never by hand. - - - DPLAN — dev plan. Thinking, brainstorming, architecture. Before building. - - FPLAN — flow plan, the default. Building and executing. `master` template = multi-phase, spawns sub-FPLANs. - - PPLAN — playbook. A throwaway run stamped from a reusable SOP template. Operating the system, not changing it. - - More types exist and new ones register over time. Named a type you don't know? `drone @flow templates` lists them all, live. - -# Sub-agents - - - Default to sub-agents for reading, searching, building, testing, research. Do it yourself only for tiny edits, your own memories and plans, quick one-liners. - - One clear task per agent. Brief with full context — they know nothing of your conversation. - - No git, no memory, no dispatch. They build and report; you decide and act. - - Sub-agent = local disposable worker. Dispatch (`@ai_mail`) = wake a citizen with memory and identity. Branch-expert work → dispatch; else → sub-agent. - - Models, good practice: opus for build and analysis, sonnet for routine investigation, haiku for trivial mechanical tasks. Never fable for sub-agents. - -# Memory — .trinity/ - -Your continuity across sessions. Save proactively — after milestones, decisions, topic switches. - - - `passport.json` — identity. Update only when identity genuinely evolves. - - `local.json` — session log, key learnings, todos. - - `observations.json` — what you learn about the user. - - Overflow rolls to vectors automatically — never trim by hand. Two ChromaDB stores: your branch's `.chroma` (local) + a global one across all branches. `drone @memory search "query"` recalls them. Search before assuming you're cold. - -# House rules - - - No cross-branch file edits. Issue in another agent's code → mail the owner. - - Never delete files. Rename `name(disabled).py` or move to a sibling `.archive/`. - - Fail to errors, never fall back silently. - - Verify after fixing — don't say "fixed" until a test or command confirms it. - - Cross-platform, no hardcoded paths. Public repo — `pathlib`, never `/home/...`. - - No bare imports — always `from aipass..apps...`. - - Registries are machine-managed (spawn, flow) — never hand-edit them. - - State lives in `.trinity/` and dashboards, never in prompts. Prompts are signposts. diff --git a/.aipass/hooks.json b/.aipass/hooks.json index 6c8cc353..d4f049ee 100644 --- a/.aipass/hooks.json +++ b/.aipass/hooks.json @@ -18,12 +18,6 @@ "handler": "aipass.hooks.apps.handlers.prompt.branch_loader.handle", "matcher": "" }, - "global_prompt": { - "enabled": false, - "_retired": "Superseded by tier0_kernel + navmap (FPLAN-0284, 2026-06-18). Re-enable here to roll back to the single 8k global prompt.", - "handler": "aipass.hooks.apps.handlers.prompt.global_loader.handle", - "matcher": "" - }, "tier0_kernel": { "enabled": true, "handler": "aipass.hooks.apps.handlers.prompt.tier0_kernel.handle", diff --git a/.aipass/project_global_prompt.md b/.aipass/project_global_prompt.md deleted file mode 100644 index 38e22dd2..00000000 --- a/.aipass/project_global_prompt.md +++ /dev/null @@ -1,99 +0,0 @@ -# {name} — Project Context - - -Multi-agent framework. Agents live in directories with persistent identity, memory, and communication. All AIPass infrastructure available from any project via `drone`. - -Patterns here are exact. Don't guess command syntax — examples are the API. - -`drone` = installed binary, always on PATH. Run directly. - -# Terminology - -- Branch — directory `src/{name}//`. Agent home and address. -- Agent (citizen) — persistent identity. Has passport (`.trinity/`), memory, mailbox, code (`apps/`). Addressable as `@name`. -- Sub-agent — disposable worker spawned for a task. No passport, no memory. -- Registry — `{name}_REGISTRY.json` tracks all agents. -- Project — this directory. Contains registry and agents. - -# Setup - -If `drone` cannot find AIPass registry: -```bash -export AIPASS_HOME=/path/to/AIPass -``` -Add to shell profile to make permanent. - -# Commands - -## Agent Lifecycle -``` -aipass init agent # Create new agent in src// -drone @spawn create # Create agent (alternative) -drone @spawn list # List registered agents -``` - -## Dispatch — Send Task + Wake Agent -``` -drone @ai_mail dispatch @ "Subject" "Body" # Send + wake (default) -drone @ai_mail dispatch @ "Subject" "Body" --fresh # Send + wake fresh session -drone @ai_mail email @ "Subject" "Body" # FYI only (no wake) -``` - -Use `dispatch` by default. Use `email` only when you don't need the agent to act now. - -## Communication -``` -drone @ai_mail inbox # Check mailbox -drone @ai_mail view # Read message -drone @ai_mail close # Mark read -``` - -## Standards -``` -drone @seedgo audit # Full standards audit -drone @seedgo checklist # Check single file -``` - -## Plans -``` -drone @flow create . "Subject" dplan # DPLAN (design/thinking) -drone @flow create . "Subject" # FPLAN (execution) -drone @flow create . "Subject" aplan # APLAN (agent task) -drone @flow list open # Active plans -drone @flow close # Close plan -``` - -DPLAN = thinking before building. FPLAN = building and executing. - -## Memory -``` -drone @memory archive # Archive to vector store -drone @memory search # Search archived memories -``` - -## Git -``` -drone @git status # Git status (branch-scoped) -drone @git pr 'description' # Create pull request -drone @git sync # Sync with main -``` - -## Infrastructure -``` -drone systems # List all available branches -drone @ --help # Branch command reference -``` - -# Patterns - -- Communication — agents communicate via `.ai_mail.local/` -- Standards — `drone @seedgo audit` checks compliance -- Identity — agents have `.trinity/passport.json`, projects use registry -- Memory — update `.trinity/local.json` at session end. Memory is presence. -- Use drone commands for all operations. Never raw git, gh, or python -m. - -# Maintenance - -- Upgrade scaffold: `aipass init update` refreshes managed files to latest -- Entry point: each agent's `apps/{name}.py` auto-configures sys.path -- Layout: `src/{name}//` for standalone projects diff --git a/.aipass/project_hooks.json b/.aipass/project_hooks.json index 2521ade5..2b6fb54e 100644 --- a/.aipass/project_hooks.json +++ b/.aipass/project_hooks.json @@ -18,9 +18,14 @@ "handler": "aipass.hooks.apps.handlers.prompt.branch_loader.handle", "matcher": "" }, - "global_prompt": { + "tier0_kernel": { "enabled": true, - "handler": "aipass.hooks.apps.handlers.prompt.global_loader.handle", + "handler": "aipass.hooks.apps.handlers.prompt.tier0_kernel.handle", + "matcher": "" + }, + "navmap": { + "enabled": true, + "handler": "aipass.hooks.apps.handlers.prompt.navmap.handle", "matcher": "" } }, diff --git a/.claude/commands/compass.md b/.claude/commands/compass.md index 74aff7e2..be040874 100644 --- a/.claude/commands/compass.md +++ b/.claude/commands/compass.md @@ -1,6 +1,6 @@ # Compass — Record a Decision -Purpose: Capture the decision just made into compass (the rated decision engine) with Patrick's rating and note. Patrick fires this when he notices a decision worth recording — he supplies the judgement, you supply the decision text from the conversation. This is the human-triggered answer to the "noticing" problem: Patrick notices, you describe and store. +Purpose: Capture the decision just made into compass (the rated decision engine) with the user's rating and note. The user fires this when they notice a decision worth recording — they supply the judgement, you supply the decision text from the conversation. This is the human-triggered answer to the "noticing" problem: the user notices, you describe and store. Usage: `/compass ` — rating is one of: `good`, `bad`, `impressive`, `interesting`. @@ -14,15 +14,15 @@ Arguments: `$ARGUMENTS` ## Execution 1. Parse `$ARGUMENTS`: - - First token = `rating`. It MUST be one of `good | bad | impressive | interesting`. If it isn't, don't guess — ask Patrick which rating he meant and stop. - - Everything after the first token = `note` (Patrick's observation; may be empty). + - First token = `rating`. It MUST be one of `good | bad | impressive | interesting`. If it isn't, don't guess — ask the user which rating they meant and stop. + - Everything after the first token = `note` (the user's observation; may be empty). 2. From the recent conversation, identify the decision being rated. Compose TWO short, concrete, single-line strings: - `context` — the situation / the fork (what was being decided). - `decision` — what was actually chosen. - This is your job: Patrick rated it, you describe it accurately from what just happened. -3. Store it (source is `patrick`, since he triggered the rating): + This is your job: the user rated it, you describe it accurately from what just happened. +3. Store it (source is `user`, since they triggered the rating): ``` - drone @devpulse compass add "" "" --rating --note "" --source patrick + drone @devpulse compass add "" "" --rating --note "" --source user ``` Omit `--note` if the note is empty. 4. Confirm in one line: the rating, the decision recorded, and the new id. @@ -31,5 +31,5 @@ Arguments: `$ARGUMENTS` - Compass is the curated truth-store of decisions — short entries only. Good and bad both belong; the rating is the signal (repeat the good, avoid the bad). - Compass is separate from @memory. Do NOT also write this to `.trinity/` or memory — different store, different purpose. -- If the decision Patrick means is ambiguous, ask before storing. One good entry beats a vague one. +- If the decision the user means is ambiguous, ask before storing. One good entry beats a vague one. - Before a real fork later, you can `drone @devpulse compass query ""` to see how similar past decisions were rated. diff --git a/.codex/hooks/session_start_identity.py b/.codex/hooks/session_start_identity.py index 3a4fe6cf..b0c6097c 100755 --- a/.codex/hooks/session_start_identity.py +++ b/.codex/hooks/session_start_identity.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 """Codex SessionStart hook: inject AIPass identity context. -Reads .trinity/passport.json and branch prompt, outputs Codex-format JSON -with additionalContext for identity injection. +Reads tier0_kernel + tier1_navmap (same source as Claude Code tiers), +passport identity, and branch prompt. Outputs Codex-format JSON with +additionalContext. Codex fires once at SessionStart — no per-turn cadence. """ + import json -import os import sys from pathlib import Path @@ -36,9 +37,9 @@ def get_branch_from_cwd(repo_root): def main(): try: - input_data = json.loads(sys.stdin.read()) + json.loads(sys.stdin.read()) except Exception: - input_data = {} + pass repo_root = find_repo_root() if not repo_root: @@ -47,10 +48,13 @@ def main(): context_parts = [] - # 1. Global prompt - global_prompt = repo_root / ".aipass" / "aipass_global_prompt.md" - if global_prompt.exists(): - context_parts.append(global_prompt.read_text(encoding="utf-8")[:8000]) + # 1. Tiered prompts (same source as Claude Code tiers) + tier0 = repo_root / ".aipass" / "tier0_kernel.md" + if tier0.exists(): + context_parts.append(tier0.read_text(encoding="utf-8")[:2500]) + tier1 = repo_root / ".aipass" / "tier1_navmap.md" + if tier1.exists(): + context_parts.append(tier1.read_text(encoding="utf-8")[:8000]) # 2. Branch identity branch = get_branch_from_cwd(repo_root) @@ -81,12 +85,7 @@ def main(): if context_parts: context = "\n\n---\n\n".join(context_parts) - output = { - "hookSpecificOutput": { - "hookEventName": "SessionStart", - "additionalContext": context - } - } + output = {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": context}} else: output = {} diff --git a/CHANGELOG.md b/CHANGELOG.md index 314b7d9d..7c1eba69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,63 @@ PyPI version — not the changelog header. --- +## [2026-06-23] + +### Fixed + +- **Hardcoded home paths removed (seedgo #37 cleanup).** `@memory` `symbolic.py` + derived 8 dash-encoded branch-path names from a literal `-home-patrick-` home — + now built at runtime from `Path(__file__).resolve().parents[3]`. `@prax` + `branch_detector.py` docstring examples genericized to `-home-user-`. Both + branches back to 100% `Hardcoded_Path`. (959 / 906 tests green.) + +## [2026-06-19] + +### Fixed + +- **`aipass init` now seeds the tiered prompts to new projects (@aipass).** The + init template + bootstrap still handed new projects the retired global prompt + with no tiers; now `.aipass/project_hooks.json` mirrors the live wiring + (`tier0_kernel` + `navmap` enabled, `global_prompt` disabled) and `bootstrap.py` + seeds both tier `.md` files. `init update` backfills existing projects. + (77 bootstrap tests, 100% seedgo.) +- **Cadence reset observability (@hooks).** `reset_counter()` silently no-op'd + when the Claude session id was absent; it now fails loud, logs the session id + + prior turn on each reset, falls back to hook data for the id, and handles a + corrupt state file. (The post-compaction counter reset was already working — + this makes it visible so it can't fail invisibly.) +- **Memory rollover was silently dead — fixed end-to-end (@hooks + @memory).** The + PreCompact rollover hook read its limits from `.trinity` file metadata, but + DPLAN-0210 had moved limits into @memory's `memory.config.json` — so the hook + always fell back to a 600-line check the lean files never reached, and rollover + never fired (for weeks). The hook is now a thin trigger delegating to + `drone @memory rollover check/run`; `compact.py` reads the current list schema + (it was calling `.keys()` on a now-list `key_learnings`). Both fail loud instead + of a silent exit-0. +- **Removed @memory's v1 line-count / 600-line silent fallback entirely.** The + detector + extractor are now v2-only (`per_branch` → `defaults` → warn-and-skip); + a parse failure logs loud and skips rather than silently falling back. Deleted + `_get_max_lines` / `_load_config` / `_detect_growing_array` / the line-count + extraction path. (959 tests.) + +### Removed + +- **Legacy global prompt fully retired across every runtime (DPLAN-0215).** After + the tiered cutover the old `global_prompt` is now gone, not just disabled: + `global_loader.py` + its tests deleted, the `global_prompt` block stripped from + `.aipass/hooks.json` + `project_hooks.json`, `_resolve_global_prompt` + all global + seeding removed from `aipass init` bootstrap/update, the cadence default + bypass + entries cleaned, and both `aipass_global_prompt.md` / `project_global_prompt.md` + archived. Claude (cadence) and Codex (SessionStart) now read the same tier files — + one prompt source, every runtime. + +### Added + +- **seedgo `HARDCODED_PATH` standard (#37).** A new checker (`hardcoded_path_check.py` + + `hardcoded_path_content.py`, `test_checkers_batch10.py`) flags hardcoded home + paths — `/home/` and dash-encoded `-home--` — in source and docstrings, + keeping the public repo clean. + ## [2026-06-18] ### Changed @@ -24,6 +81,15 @@ PyPI version — not the changelog header. is retired (kept as a reference snapshot). Net: more navigation context reaches agents while less is paid per turn. Fresh-clone wiring is seeded from `cadence.py` defaults + `setup.sh` + `provider_manifest.json`. +- **Public source genericized — `Patrick` → generic `user`.** No personal + identifiers in tracked code/docs: the compass decision-source enum + (`patrick` → `user`) + the `/compass` command, the devpulse local prompt, the + `aipass init` onboarding example (`--name Patrick` → `--name YourName`), and + stale refs across @ai_mail / @backup / @flow. Private memories (`.trinity/`, + compass DB) keep personal context — they're gitignored. +- **Telegram skill genericized (@skills).** Retired the inactive `patrick_private` + personal bot from the skill's tests; the message sender now defaults to the + Telegram user's first name (fallback `User`) instead of a hardcoded `Patrick`. ### Added @@ -35,6 +101,13 @@ PyPI version — not the changelog header. - **Skill frontmatter discipline (@skills).** A `when_to_use` field with trigger phrases (surfaced during discovery scans) and per-step "Done when:" success criteria across the SKILL.md templates. +- **`HARDCODED_PATH` standard (@seedgo, 37th checker).** Flags absolute home-dir + literals in source — POSIX `/home//`, macOS `/Users//`, Windows + user-home paths, and Claude Code's dash-encoded `-home--` form — with a + bypass for legitimate test fixtures. Swept the repo for violations. +- **`prompt_change` flow playbook (PPLAN template).** A reusable SOP for changing + any injected prompt — leads with "live ≠ seeded" and walks every wiring layer + + fresh-install seed path; born from the `aipass init` seeding gap this surfaced. ## [2026-06-16] diff --git a/src/aipass/ai_mail/apps/handlers/email/delivery.py b/src/aipass/ai_mail/apps/handlers/email/delivery.py index 9fa6523d..16cd6bf9 100644 --- a/src/aipass/ai_mail/apps/handlers/email/delivery.py +++ b/src/aipass/ai_mail/apps/handlers/email/delivery.py @@ -165,7 +165,7 @@ def _is_private_branch_email(email: str) -> bool: email address is registered to a private (isolated) branch. Args: - email: Email address to check (e.g., "@patrick_private") + email: Email address to check (e.g., "@private_branch") Returns: True if email belongs to a private branch, False otherwise diff --git a/src/aipass/aipass/apps/handlers/init/bootstrap.py b/src/aipass/aipass/apps/handlers/init/bootstrap.py index 695e705e..db3cab4f 100644 --- a/src/aipass/aipass/apps/handlers/init/bootstrap.py +++ b/src/aipass/aipass/apps/handlers/init/bootstrap.py @@ -11,7 +11,8 @@ Init Bootstrap Handler - PRIVATE implementation Business logic for `aipass init`. Creates the project scaffold: 1. {NAME}_REGISTRY.json — project registry with UUID - 2. .aipass/aipass_global_prompt.md — global prompt (injected every turn) + 2. .aipass/tier0_kernel.md — tier 0 kernel prompt (every turn) + 2b..aipass/tier1_navmap.md — tier 1 navigation map (periodic) 3. CLAUDE.md — project prompt (Claude Code reads this) 4. AGENTS.md — Codex equivalent of CLAUDE.md 5. README.md — getting started guide @@ -69,14 +70,6 @@ def _detect_aipass_home() -> str | None: return None -def _resolve_global_prompt(name: str, aipass_home: str | None, dest: Path) -> str: - """Resolve global prompt content from source template or fallback generator.""" - source = Path(aipass_home) / ".aipass" / "project_global_prompt.md" if aipass_home else None - if source and source.is_file(): - return source.read_text(encoding="utf-8").replace("{name}", name) - return sc.with_source(sc.global_prompt_md(name), dest) - - def _hook_fingerprint(hook_entry: dict) -> str: """Extract a comparable fingerprint from a hook entry.""" commands = [] @@ -323,10 +316,14 @@ def init_project(target: Path, project_name: str | None = None) -> dict: aipass_dir = target / ".aipass" aipass_dir.mkdir(exist_ok=True) - global_prompt_path = aipass_dir / "aipass_global_prompt.md" - if not global_prompt_path.exists(): - global_prompt_path.write_text(_resolve_global_prompt(name, aipass_home, global_prompt_path), encoding="utf-8") - created.append(str(global_prompt_path)) + # 2. .aipass/tier0_kernel.md + tier1_navmap.md — tiered prompt injection + for tier_file in ("tier0_kernel.md", "tier1_navmap.md"): + tier_dest = aipass_dir / tier_file + if not tier_dest.exists() and aipass_home: + tier_src = Path(aipass_home) / ".aipass" / tier_file + if tier_src.is_file(): + shutil.copy2(str(tier_src), str(tier_dest)) + created.append(str(tier_dest)) # 2b. .aipass/hooks.json — project hook config from template hooks_json_path = aipass_dir / "hooks.json" @@ -488,14 +485,21 @@ def update_project(target: Path) -> dict: # --- Managed files: write only when content has changed --- - global_prompt_path = aipass_dir / "aipass_global_prompt.md" aipass_home = aipass_home or _detect_aipass_home() - generated = _resolve_global_prompt(name, aipass_home, global_prompt_path) - if not global_prompt_path.exists() or global_prompt_path.read_text(encoding="utf-8") != generated: - global_prompt_path.write_text(generated, encoding="utf-8") - updated.append(str(global_prompt_path)) - else: - already_current.append(str(global_prompt_path)) + + # tier0_kernel.md + tier1_navmap.md — tiered prompt injection + for tier_file in ("tier0_kernel.md", "tier1_navmap.md"): + tier_dest = aipass_dir / tier_file + tier_src = Path(aipass_home) / ".aipass" / tier_file if aipass_home else None + if tier_src and tier_src.is_file(): + canonical = tier_src.read_text(encoding="utf-8") + if not tier_dest.exists() or tier_dest.read_text(encoding="utf-8") != canonical: + tier_dest.write_text(canonical, encoding="utf-8") + updated.append(str(tier_dest)) + else: + already_current.append(str(tier_dest)) + elif tier_dest.exists(): + already_current.append(str(tier_dest)) # settings.json — smart merge: preserve user hooks + env, update AIPass hooks settings_path = claude_dir / "settings.json" diff --git a/src/aipass/aipass/apps/modules/init_flow.py b/src/aipass/aipass/apps/modules/init_flow.py index 86b02a31..7e68e830 100644 --- a/src/aipass/aipass/apps/modules/init_flow.py +++ b/src/aipass/aipass/apps/modules/init_flow.py @@ -16,7 +16,7 @@ Usage: aipass init # show progress / introspection aipass init run # interactive aipass init run --non-interactive # CI/headless, all defaults - aipass init run --name Patrick --cli claude + aipass init run --name YourName --cli claude aipass init run --dry-run # walk all 12 stages, no destructive ops # - skips drone @spawn create (stage 8) # - skips tmux/wt handoff (stage 11) @@ -876,7 +876,7 @@ def print_help() -> None: console.print("[yellow]USAGE:[/yellow]") console.print(" [green]aipass init run[/green] [dim]# interactive[/dim]") console.print(" [green]aipass init run --non-interactive[/green] [dim]# CI/headless[/dim]") - console.print(" [green]aipass init run --name Patrick[/green] [dim]# pre-fill name[/dim]") + console.print(" [green]aipass init run --name YourName[/green] [dim]# pre-fill name[/dim]") console.print(" [green]aipass init run --cli claude[/green] [dim]# pre-fill CLI[/dim]") console.print(" [green]aipass init run --no-docker[/green] [dim]# skip docker offer[/dim]") console.print(" [green]aipass init run --dry-run[/green] [dim]# walk all stages, no writes[/dim]") diff --git a/src/aipass/aipass/tests/test_bootstrap.py b/src/aipass/aipass/tests/test_bootstrap.py index 30f4ddb9..3f5182b5 100644 --- a/src/aipass/aipass/tests/test_bootstrap.py +++ b/src/aipass/aipass/tests/test_bootstrap.py @@ -98,7 +98,6 @@ def test_init_project_creates_all_expected_files(tmp_path): expected_files = [ target / "DEMO_REGISTRY.json", - target / ".aipass" / "aipass_global_prompt.md", target / "CLAUDE.md", target / "AGENTS.md", target / "README.md", @@ -107,8 +106,13 @@ def test_init_project_creates_all_expected_files(tmp_path): target / ".claude" / "commands" / "prep.md", target / "src" / "demo" / "__init__.py", ] + # Tier files are env-dependent (need AIPASS_HOME) + if result["aipass_home"]: + expected_files.append(target / ".aipass" / "tier0_kernel.md") + expected_files.append(target / ".aipass" / "tier1_navmap.md") for f in expected_files: assert f.exists(), f"Expected file not created: {f}" + assert not (target / ".aipass" / "aipass_global_prompt.md").exists(), "Retired global prompt should NOT be seeded" # src// is a directory with __init__.py assert (target / "src" / "demo").is_dir(), "Expected src/demo/ package directory" @@ -126,7 +130,7 @@ def test_init_project_creates_all_expected_files(tmp_path): created_basenames = [Path(f).name for f in result["created_files"]] for f in expected_files: assert f.name in created_basenames or f.exists(), f"Expected {f.name} in created_files" - assert len(result["created_files"]) >= 11 + assert len(result["created_files"]) >= 10 def test_init_project_return_dict_structure(tmp_path): @@ -288,19 +292,6 @@ def test_init_project_settings_no_hooks(tmp_path): assert "permissions" in data -def test_init_project_global_prompt_content(tmp_path): - """Global prompt contains project name and AIPass terminology.""" - target = tmp_path / "proj" - target.mkdir() - - init_project(target, project_name="alpha") - - content = (target / ".aipass" / "aipass_global_prompt.md").read_text(encoding="utf-8") - assert "# ALPHA" in content - assert "ALPHA_REGISTRY.json" in content - assert "# Commands" in content - - def test_init_project_readme_md_content(tmp_path): """README.md contains getting started guide with project name.""" target = tmp_path / "proj" @@ -324,7 +315,7 @@ def test_init_project_auto_creates_target_dir(tmp_path): assert target.is_dir() assert result["project_name"] == "NESTED" - assert len(result["created_files"]) >= 11 + assert len(result["created_files"]) >= 10 def test_init_project_defaults_name_from_directory(tmp_path): @@ -359,7 +350,6 @@ def test_init_project_skips_existing_optional_files(tmp_path): # Pre-create optional files aipass_dir = target / ".aipass" aipass_dir.mkdir() - (aipass_dir / "aipass_global_prompt.md").write_text("# Custom global\n", encoding="utf-8") (target / "CLAUDE.md").write_text("# Custom CLAUDE\n", encoding="utf-8") (target / "AGENTS.md").write_text("# Custom AGENTS\n", encoding="utf-8") (target / "README.md").write_text("# Custom README\n", encoding="utf-8") @@ -542,10 +532,12 @@ def test_update_project_creates_missing_managed_dirs(tmp_path): result = update_project(target) - assert (target / ".aipass" / "aipass_global_prompt.md").exists() assert (target / ".claude" / "settings.json").exists() - # Managed files in deleted dirs re-written (global_prompt, hooks.json, settings, prep) - assert len(result["updated_files"]) == 4 + # Managed files in deleted dirs re-written (tier0_kernel, tier1_navmap, hooks.json, settings, prep) + if result["aipass_home"]: + assert len(result["updated_files"]) == 5 + else: + assert len(result["updated_files"]) == 2 assert len(result["already_current"]) >= 2 @@ -850,6 +842,154 @@ def test_update_project_hooks_json_already_current(tmp_path): assert any("hooks.json" in f for f in result["already_current"]) +# --------------------------------------------------------------------------- +# Tiered prompt injection tests (FPLAN-0284) +# --------------------------------------------------------------------------- + + +def test_init_project_creates_tier_files(tmp_path): + """init_project seeds tier0_kernel.md and tier1_navmap.md when AIPASS_HOME available.""" + target = tmp_path / "proj" + target.mkdir() + + result = init_project(target, project_name="tiers") + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + assert (target / ".aipass" / "tier0_kernel.md").exists() + assert (target / ".aipass" / "tier1_navmap.md").exists() + + +def test_init_project_tier_files_match_canonical(tmp_path): + """Tier files in new project match the canonical source exactly.""" + target = tmp_path / "proj" + target.mkdir() + + result = init_project(target, project_name="canon") + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + for tier_file in ("tier0_kernel.md", "tier1_navmap.md"): + canonical = Path(result["aipass_home"]) / ".aipass" / tier_file + if not canonical.exists(): + pytest.skip(f"{tier_file} not found in canonical .aipass/") + assert (target / ".aipass" / tier_file).read_bytes() == canonical.read_bytes() + + +def test_init_project_tier_files_in_created_list(tmp_path): + """Tier files appear in created_files list.""" + target = tmp_path / "proj" + target.mkdir() + + result = init_project(target, project_name="listed") + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + assert any("tier0_kernel.md" in f for f in result["created_files"]) + assert any("tier1_navmap.md" in f for f in result["created_files"]) + + +def test_init_project_no_tier_files_without_aipass_home(tmp_path, monkeypatch): + """Without AIPASS_HOME, tier files are not created.""" + target = tmp_path / "proj" + target.mkdir() + + monkeypatch.setattr( + "aipass.aipass.apps.handlers.init.bootstrap._detect_aipass_home", + lambda: None, + ) + + init_project(target, project_name="notiers") + + assert not (target / ".aipass" / "tier0_kernel.md").exists() + assert not (target / ".aipass" / "tier1_navmap.md").exists() + + +def test_init_project_hooks_json_has_tiers_enabled(tmp_path): + """hooks.json from template has tier0_kernel and navmap enabled, no global_prompt.""" + target = tmp_path / "proj" + target.mkdir() + + result = init_project(target, project_name="hookstier") + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + hooks_json = target / ".aipass" / "hooks.json" + data = json.loads(hooks_json.read_text(encoding="utf-8")) + ups = data["UserPromptSubmit"] + + assert ups["tier0_kernel"]["enabled"] is True + assert ups["navmap"]["enabled"] is True + assert "global_prompt" not in ups + + +def test_update_project_adds_tier_files_to_existing(tmp_path): + """update_project adds tier files to a project that lacks them.""" + target = tmp_path / "proj" + target.mkdir() + + registry_data = { + "metadata": { + "id": "test-id", + "name": "OLD", + "version": "1.0.0", + "created": "2026-01-01", + "last_updated": "2026-01-01", + "total_branches": 0, + }, + "branches": [], + } + (target / "OLD_REGISTRY.json").write_text(json.dumps(registry_data), encoding="utf-8") + (target / ".aipass").mkdir() + + result = update_project(target) + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + assert (target / ".aipass" / "tier0_kernel.md").exists() + assert (target / ".aipass" / "tier1_navmap.md").exists() + assert any("tier0_kernel.md" in f for f in result["updated_files"]) + assert any("tier1_navmap.md" in f for f in result["updated_files"]) + + +def test_update_project_tier_files_already_current(tmp_path): + """update reports tier files as already_current when unchanged.""" + target = tmp_path / "proj" + target.mkdir() + result = init_project(target, project_name="tiercurr") + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + result = update_project(target) + + assert any("tier0_kernel.md" in f for f in result["already_current"]) + assert any("tier1_navmap.md" in f for f in result["already_current"]) + + +def test_update_project_refreshes_stale_tier_files(tmp_path): + """update overwrites tier files when they differ from canonical source.""" + target = tmp_path / "proj" + target.mkdir() + result = init_project(target, project_name="stale") + + if result["aipass_home"] is None: + pytest.skip("AIPASS_HOME not detectable in this environment") + + (target / ".aipass" / "tier0_kernel.md").write_text("# stale\n", encoding="utf-8") + + result = update_project(target) + + assert any("tier0_kernel.md" in f for f in result["updated_files"]) + content = (target / ".aipass" / "tier0_kernel.md").read_text(encoding="utf-8") + assert "AIPass" in content + + # --------------------------------------------------------------------------- # scaffold_content — global_prompt_md tests # --------------------------------------------------------------------------- diff --git a/src/aipass/backup/apps/modules/all.py b/src/aipass/backup/apps/modules/all.py index a1b07a84..86157fc4 100644 --- a/src/aipass/backup/apps/modules/all.py +++ b/src/aipass/backup/apps/modules/all.py @@ -56,7 +56,7 @@ def handle_command(command: str, args: list) -> bool: show_panels = "--quiet" not in args logger.info(f"[backup] Running full backup cycle for {project_root}") - # ONE scan shared between both modes (Patrick's Law #1) + # ONE scan shared between both modes (single-scan rule) config = load_project_config(project_root) spec = load_spec(project_root) whitelist_entries = load_whitelist(project_root) diff --git a/src/aipass/backup/run/.backupignore b/src/aipass/backup/run/.backupignore new file mode 100644 index 00000000..57595e6b --- /dev/null +++ b/src/aipass/backup/run/.backupignore @@ -0,0 +1,28 @@ +# Backup System ignore patterns (gitignore-style) +# Lines starting with # are comments. Blank lines are ignored. +# Edit this file to customize. Source defaults: handlers/ignore/patterns.py + +.backup/ +.git/ +.svn/ +.hg/ +__pycache__/ +.pytest_cache/ +*.pyc +*.pyo +*.egg-info/ +.venv/ +venv/ +.tox/ +node_modules/ +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store +Thumbs.db +build/ +dist/ +*.log +.ruff_cache/ +.coverage diff --git a/src/aipass/devpulse/.aipass/aipass_local_prompt.md b/src/aipass/devpulse/.aipass/aipass_local_prompt.md index b2e163a4..99df7675 100644 --- a/src/aipass/devpulse/.aipass/aipass_local_prompt.md +++ b/src/aipass/devpulse/.aipass/aipass_local_prompt.md @@ -4,7 +4,7 @@ Breadcrumbs only — details in README, `--help`, `.trinity/`, `DASHBOARD.local. # Identity -DEVPULSE — Patrick's primary collaborator, orchestration hub. Design, plan, debug, dispatch, track. Build own modules (watchdog, feedback), DPLANs, FPLANs, memories. Venture into other branches to investigate, debug, fix small bugs. Delegate heavy multi-file builds to sub-agents. CWD is identity grounding. +DEVPULSE — the user's primary collaborator, orchestration hub. Design, plan, debug, dispatch, track. Build own modules (watchdog, feedback), DPLANs, FPLANs, memories. Venture into other branches to investigate, debug, fix small bugs. Delegate heavy multi-file builds to sub-agents. CWD is identity grounding. # How you work @@ -82,7 +82,7 @@ drone @devpulse watchdog agent @target # Monitor tool, never run_in_b # Interactive wake — tmux -Gives Patrick an interactive session, distinct from autonomous dispatch. Find the agent via `.trinity/passport.json`; use `dangerouslyDisableSandbox: true`. +Gives User an interactive session, distinct from autonomous dispatch. Find the agent via `.trinity/passport.json`; use `dangerouslyDisableSandbox: true`. ``` tmux new-session -d -s "name" -c "/path/to/branch" @@ -96,4 +96,4 @@ Compass is the curated truth-store of rated decisions (`good/bad/impressive/inte - Recall what happened / did we do X → `drone @memory search`. - At a fork, setting a pattern, or unsure of a convention → `drone @devpulse compass query "topic"` (rating shows per hit). - A good or bad decision made, or a convention confirmed → `drone @devpulse compass add "context" "decision" --rating good`. Add freely, no asking. - - Patrick fires `/compass ` when he notices a decision — you write the entry from context. + - User fires `/compass ` when he notices a decision — you write the entry from context. diff --git a/src/aipass/devpulse/apps/handlers/compass/store.py b/src/aipass/devpulse/apps/handlers/compass/store.py index 048432fe..f700fdc7 100644 --- a/src/aipass/devpulse/apps/handlers/compass/store.py +++ b/src/aipass/devpulse/apps/handlers/compass/store.py @@ -53,7 +53,7 @@ _BRANCH_ROOT = Path(__file__).resolve().parents[3] DEFAULT_DB_PATH = _BRANCH_ROOT / "devpulse_json" / "compass" / "compass.db" VALID_RATINGS = ("good", "bad", "impressive", "interesting") -VALID_SOURCES = ("devpulse", "patrick") +VALID_SOURCES = ("devpulse", "user") VALID_STATUSES = ("active", "archived") # Columns we return / surface from the decisions table (everything useful). @@ -177,7 +177,7 @@ def add_decision( rating: One of ``good | bad | impressive | interesting``. note: Optional human observation. tags: Optional comma-separated tags. - source: ``devpulse`` or ``patrick`` (default ``devpulse``). + source: ``devpulse`` or ``user`` (default ``devpulse``). db_path: Optional DB path override (tests pass a temp path). created: Optional ISO date override; defaults to today. This is the ONLY place a "today" date is stamped. diff --git a/src/aipass/devpulse/apps/modules/compass.py b/src/aipass/devpulse/apps/modules/compass.py index 96742d0c..11e02881 100644 --- a/src/aipass/devpulse/apps/modules/compass.py +++ b/src/aipass/devpulse/apps/modules/compass.py @@ -63,13 +63,13 @@ HELP_TEXT = """\ compass --help Show this help [bold]Ratings:[/bold] good | bad | impressive | interesting -[bold]Sources:[/bold] devpulse | patrick +[bold]Sources:[/bold] devpulse | user [bold]Options (add):[/bold] --rating R Required. One of the ratings above. --note "..." Optional human observation. --tags a,b,c Optional comma-separated tags. - --source S Optional. devpulse (default) or patrick. + --source S Optional. devpulse (default) or user. [bold]Options (all subcommands):[/bold] --db PATH Use an alternate SQLite store (testing / power use). diff --git a/src/aipass/flow/.aipass/aipass_local_prompt.md b/src/aipass/flow/.aipass/aipass_local_prompt.md index c163be65..c126870a 100644 --- a/src/aipass/flow/.aipass/aipass_local_prompt.md +++ b/src/aipass/flow/.aipass/aipass_local_prompt.md @@ -1,10 +1,8 @@ # Flow — Plan Lifecycle Management Flow is AIPass's unified plan lifecycle system. Creates, tracks, closes, archives numbered work plans across multiple plan types (FPLAN, DPLAN) via data-driven plugin architecture. - -## Commands - -```bash +# Commands +`bash drone @flow create . "Subject" # FPLAN (default) drone @flow create . "Subject" master # FPLAN master template drone @flow create . "Design topic" dplan # DPLAN @@ -14,17 +12,13 @@ drone @flow list open # List open plans (all types) drone @flow list all # List all plans drone @flow restore FPLAN-0042 # Reopen closed plan ``` - -## Architecture - +# Architecture - `apps/flow.py` -- Entry point. Auto-discovers modules `apps/modules/` via `handle_command()` convention. - `apps/modules/` -- Thin orchestrators. No business logic. Route handlers, display results. - `apps/handlers/` -- Implementation. Grouped domain: `plan/`, `registry/`, `template/`, `dashboard/`, `mbank/`, `summary/`. - `templates/` -- Plan type directories. Each subdirectory contains Markdown templates. Registered via `drone @flow register`. -- `flow_json/` -- Registries: per-type plan registries + `template_registry.json` (plan type definitions). - -## Plan Type System - +- `flow_json/` -- Registries: per-type plan registries + `template_registry.json` (plan type definitions).] +# Plan Type System Plan types filesystem-driven. Drop directory `.md` templates into `templates/`, register, done: ```bash drone @flow register testing TPLAN # Register new type @@ -32,35 +26,24 @@ drone @flow unregister testing # Remove type drone @flow templates # List registered types drone @flow scan # Find unregistered directories ``` - Discovered runtime `plan_type_loader.py` + `registry_ops.py`. No per-directory JSON config needed. - | Type | Prefix | Registry File | Templates | |------|--------|---------------|-----------| | flow_plans | FPLAN | fplan_registry.json | default, master | | dev_plans | DPLAN | dplan_registry.json | default | - -## Critical Files - -- `apps/flow.py` -- CLI entry point, module discovery, command routing -- `apps/modules/create_plan.py` -- Plan creation orchestrator -- `apps/modules/close_plan.py` -- Plan closure orchestrator (async post-processing, archival) -- `apps/modules/list_plans.py` -- Multi-registry plan listing +# Critical Files +- `apps/modules/create/close/list_plan.py` -- Plan creation orchestrator - `apps/handlers/plan/list_ops.py` -- Merges plans all registries display - `apps/handlers/plan/display.py` -- All formatting functions (prefix-aware) - `apps/handlers/plan/close_ops.py` -- Close implementation (file ops, registry update, vector intake) - `apps/handlers/template/plan_type_loader.py` -- Plugin discovery + config resolution - `apps/handlers/registry/load_registry.py` -- Registry loader (supports per-type registry files) - -## Integration Points - +#Integration Points - **aipass.cli** -- Rich console output (`console`, `header`, `success`, `error`, `warning`) - **aipass.prax** -- System logger - **aipass.memory** -- Vector intake pipeline plan close - **aipass.trigger** -- Startup events + branch dashboard updates - -## Conventions - +#Conventions - Modules return `True` `handle_command()` when command recognized (even on failure), `False` only "not my command". - Plan IDs follow `{PREFIX}-{NNNN}_topic_slug_YYYY-MM-DD.md`. - All file I/O uses `pathlib.Path` + `encoding='utf-8'`. diff --git a/src/aipass/flow/templates/flow_plans/default.md b/src/aipass/flow/templates/flow_plans/default.md index 4b32e704..a7835b7e 100644 --- a/src/aipass/flow/templates/flow_plans/default.md +++ b/src/aipass/flow/templates/flow_plans/default.md @@ -9,12 +9,12 @@ ## What Are Flow Plans? -Flow Plans (FPLANs) are **building** - autonomous construction: systems, features, modules. +Default Flow Plans (FPLANs) are **building** - autonomous construction: systems, features, modules. -**FPLANs are disposable.** Exist exactly one task. When task complete, close this plan immediately -- do not leave open. Open FPLANs mean unfinished work. Work done = plan done: `drone @flow close {plan_number}` +**FPLANs are disposable.** Exist exactly one task. When task complete, close this plan immediately -- do not leave open. Open FPLANs mean unfinished work. Work done = plan done: `drone @flow close {plan_number}` The plan is never lost. It is strored into our locl and global ,chroma vector db. **This is NOT for:** -- Research or exploration (use agents directly) +- Research or exploration (use DPLANs and APLANs directly) - Quick fixes (just do it) - Discussion or planning (happens before creating FPLAN) @@ -22,6 +22,7 @@ Flow Plans (FPLANs) are **building** - autonomous construction: systems, feature - Building features or modules - Single focused construction tasks - Sub-plans within master plan +- Mostly use for sub-agents, in some cases may be issued to Agents citizens. Judjmemts calls. --- @@ -49,6 +50,7 @@ Use dedicated directories - don't scatter files: | `tools/` | Utility scripts | | `artifacts/` | Agent outputs | | `docs/` | Documentation | +| `Sub-agents/`|Projects/AIPass/src/aipass/flow/docs.local/sub_agent_drops| --- @@ -56,7 +58,7 @@ Use dedicated directories - don't scatter files: **You are orchestrator, not builder.** -Your 200k context is precious. Burning it on file reads + code writing risks compaction during autonomous work. Agents have clean context - use them for * building. +Your 200k context is precious. Burning it on file reads + code writing risks compaction tool early during autonomous work. Agents have clean context - use them for | You Do (Orchestrator) | Agents Do (Builders) | |-----------------------|----------------------| diff --git a/src/aipass/flow/templates/flow_plans/master.md b/src/aipass/flow/templates/flow_plans/master.md index 51b4b28e..19f9749a 100644 --- a/src/aipass/flow/templates/flow_plans/master.md +++ b/src/aipass/flow/templates/flow_plans/master.md @@ -46,10 +46,11 @@ Master Plan (roadmap) ``` **How to start:** -1. User provides planning doc or instructions (coordinate @devpulse) +1. User provides planning doc DPLAN or instructions (coordinate @devpulse) 2. Branch manager reads + understands scope -3. Branch manager creates master plan: `drone @flow create . "Build X" master` +3. Branch manager creates master plan: `drone @flow create "Build X" master` 4. Branch manager fills phases, then executes autonomously +5. Devepulse mayprovide a complete plan to you. Always confirm, Alwayd confirm the plan is sound, acucurate --- @@ -57,7 +58,7 @@ Master Plan (roadmap) **You are ORCHESTRATOR, not builder.** -Your 200k context is precious. Burning it on file reads + code writing risks compaction during autonomous work. Agents have clean context - use them for ALL building. +Your 200k context is precious. Burning it on file reads + code writing risks auto compaction during autonomous work. Agents have clean context - use them for ALL building. Only devpulse is this accempion, user decideds when to compact. no auto compct for devpulse. | You Do (Orchestrator) | Agents Do (Builders) | |-----------------------|----------------------| diff --git a/src/aipass/flow/templates/playbook_plans/merge.md b/src/aipass/flow/templates/playbook_plans/merge.md index dffa9921..ace021a1 100644 --- a/src/aipass/flow/templates/playbook_plans/merge.md +++ b/src/aipass/flow/templates/playbook_plans/merge.md @@ -17,7 +17,7 @@ Run by **devpulse** (only branch with git write). Tick each step as you go; fill > `.trinity/passport.json` in the cwd; running from the repo root fails with "No > passport found"). Read git (`status`, `log`, `diff`, `rev-parse`) is allowed raw. > ⚠️ `drone @git` has **no `tag` verb** — pushing the release tag is a MANUAL step -> (Patrick, or raw `git tag`/`push` via `!`). All other writes go through drone. +> (the user, or raw `git tag`/`push` via `!`). All other writes go through drone. --- diff --git a/src/aipass/flow/templates/playbook_plans/prompt_change.md b/src/aipass/flow/templates/playbook_plans/prompt_change.md new file mode 100644 index 00000000..351c6804 --- /dev/null +++ b/src/aipass/flow/templates/playbook_plans/prompt_change.md @@ -0,0 +1,103 @@ +# {plan_number} - {subject} (PROMPT CHANGE) + +**Created**: {today} +**Branch**: {location} +**Status**: Active +**Type**: Playbook — Prompt Change SOP + +--- + +## Purpose + +Any change to an injected prompt — the kernel, the navmap, a branch-local prompt, identity, a brand-new tier, a cadence tweak, or retiring one — must propagate to **every place that injects it AND every place that seeds it into a fresh install**. This SOP is the checklist that makes sure nothing is half-done. Run by **devpulse** (orchestrator); tick as you go; dispatch the owning branch where a step lives in its core. Born from FPLAN-0284, where the live wiring was fixed but `aipass init` kept handing new projects the retired global prompt for days because the *seed* template was never updated. + +--- + +## The Law (read first — this is the trap) + +1. **LIVE ≠ SEEDED.** Editing the repo's own `.aipass/hooks.json` makes the change work *for us* — it does **nothing** for a fresh clone, a new `aipass init` project, or a freshly spawned branch. Those are built from **separate seed sources**. A prompt change is not done until every seed path carries it. +2. **Three wiring layers, each with its own seed.** A prompt only injects if all three agree: + + | Layer | Live file | Seeded for fresh installs by | + |---|---|---| + | Handler registration | `.aipass/hooks.json` | git-tracked (travels with clone) **+** `.aipass/project_hooks.json` (what `aipass init` copies) | + | Cadence (period/offset) | `hooks_json/custom_config/cadence_config.json` (machine-local, gitignored) | `cadence.py` `DEFAULTS` (the committed backstop) | + | Event→handler bridge | `~/.claude/settings.json` (machine-local) | `setup.sh` **+** `.claude/provider_manifest.json` | + +3. **Never delete a retired prompt.** Mark `(superseded)` in its header / disable it in `hooks.json`, or move it to `.archive/`. If a disabled handler still reads it **by path** for rollback, leave the file exactly where the handler looks. +4. **The `.md` files travel by git; the wiring does not.** Prompt/tier `.md` files are tracked → a clone gets them free. The machine-local wiring (cadence_config, settings bridge) must be regenerated from the committed seed sources, so those seeds are what you must update. +5. **One prompt source, every runtime — retire for one = retire for all.** There is ONE source of prompt truth (the tier `.md` files); both runtimes inject *the same content*, only delivery differs: **Claude Code** tiers by cadence (`UserPromptSubmit`, per turn — tier0 every turn, navmap periodically); **Codex CLI** injects the combined tiers once at **SessionStart** (`.codex/hooks/session_start_identity.py`). A change or retirement is NOT done until *both* runtimes point at the new content. (FPLAN-0284 retired the 8k global for Claude via cadence, but Codex's SessionStart kept reading the old global for days until separately rewired — the classic "fixed one runtime, forgot the other.") + +--- + +## 1. Identify the change +- [ ] Which prompt? (`.aipass/tier0_kernel.md` / `.aipass/tier1_navmap.md` / a branch's `.aipass/aipass_local_prompt.md` / identity / a NEW prompt / retiring one) +- [ ] Kind? edit content · change cadence · add a new injected prompt · retire one +- [ ] Owners of the files you'll touch — devpulse owns the tier files + project prompts; **@hooks** owns the engine, handlers, cadence, **and the Codex SessionStart hook** (`.codex/hooks/`); **@aipass** owns init/bootstrap; **@spawn** owns branch templates + +## 2. Content +- [ ] Edit the `.md`. Follow `.aipass/PROMPT_STYLE.md`. +- [ ] ⚠️ Size caps: tier0 kernel target **< 2,000 chars**; navmap **< 8,000** (the hook truncates near 10k). To shrink, cut CONTENT, not whitespace — newlines are nearly free, so trimming spaces saves almost nothing. +- [ ] ⚠️ An unclosed `