From 626ab81a4680f97f071e682c1c1e8cd1a0daaaa7 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Sun, 7 Jun 2026 21:53:24 -0700 Subject: [PATCH] =?UTF-8?q?refactor(status):=20decommission=20entire=20STA?= =?UTF-8?q?TUS=20flow=20=E2=80=94=20STATUS.local.md=20+=20central=20STATUS?= =?UTF-8?q?.md=20retired,=20replaced=20by=20local.json=20todos[]=20+=20das?= =?UTF-8?q?hboard=20count=20(TDPLAN-0007)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-branch coordinated change. Per-branch STATUS.local.md (13) + central STATUS.md deleted; all prompts/docs/skills/hooks/footer/scaffolding scrubbed. Status-sync engine kept intact-but-inert (trigger registry unwired, 3 lines). Replacement: operational todos[] in .trinity/local.json (@memory schema, capped, rollover-exempt), pushed to all 13 branches + surfaced as dashboard todo_count. Owners: memory (schema+global push+template), prax (dashboard todo_section + engine dormant), trigger (unwire), aipass (init scaffolding), spawn (template delete + todos[] seed), hooks (compact recovery), ai_mail (footer), seedgo (_RUNTIME_ARTIFACTS cleanup), devpulse (scrub+delete+assemble). Co-Authored-By: Claude Opus 4.8 (1M context) --- .aipass/PROMPT_STYLE.md | 2 +- .aipass/aipass_global_prompt.md | 17 ++- .aipass/project_CLAUDE.md | 2 +- .claude/commands/prep.md | 6 +- .claude/templates/memo.md | 3 +- .codex/skills/memo/SKILL.md | 1 - .codex/skills/prep/SKILL.md | 2 +- .gitignore | 1 - AGENTS.md | 2 +- CHANGELOG.md | 18 +++ CLAUDE.md | 3 +- README.md | 2 +- .../ai_mail/apps/handlers/email/footer.py | 1 - .../aipass/.aipass/aipass_local_prompt.md | 2 +- .../aipass/apps/handlers/init/bootstrap.py | 23 +--- .../apps/handlers/init/scaffold_content.py | 12 +- src/aipass/aipass/tests/test_bootstrap.py | 15 +-- .../devpulse/.aipass/aipass_local_prompt.md | 12 +- src/aipass/devpulse/README.md | 4 +- .../templates/playbook_plans/sunday_merge.md | 1 - .../hooks/.aipass/aipass_local_prompt.md | 2 +- src/aipass/hooks/README.md | 4 +- .../hooks/apps/handlers/lifecycle/compact.py | 19 +-- src/aipass/hooks/tests/test_compact.py | 7 +- .../memory/apps/handlers/templates/pusher.py | 5 + .../memory/templates/.template_version.json | 25 ++-- .../memory/templates/LOCAL.template.json | 5 +- src/aipass/memory/tests/test_handlers.py | 113 ++++++++++++++++++ src/aipass/prax/CLOSED_PLANS.local.json | 11 +- src/aipass/prax/README.md | 12 +- .../apps/handlers/dashboard/operations.py | 5 + .../prax/apps/handlers/dashboard/status.py | 6 + src/aipass/prax/apps/modules/dashboard.py | 1 + src/aipass/prax/apps/modules/monitor.py | 2 +- .../plugins/devpulse_dashboard/refresh.py | 7 ++ .../devpulse_dashboard/todo_section.py | 61 ++++++++++ .../tests/test_devpulse_dashboard_plugin.py | 96 ++++++++++++++- src/aipass/seedgo/README.md | 3 +- .../handlers/aipass_standards/readme_check.py | 1 - .../tests/test_readme_content_checks.py | 2 +- .../spawn/.aipass/aipass_local_prompt.md | 2 +- .../birthright/.aipass/aipass_local_prompt.md | 4 +- .../builder/.aipass/aipass_local_prompt.md | 4 +- .../builder/.spawn/.template_registry.json | 12 +- .../spawn/templates/builder/STATUS.local.md | 15 --- src/aipass/trigger/.seedgo/bypass.json | 6 + src/aipass/trigger/README.md | 10 +- .../trigger/apps/handlers/events/registry.py | 7 +- 48 files changed, 413 insertions(+), 163 deletions(-) create mode 100644 src/aipass/prax/apps/plugins/devpulse_dashboard/todo_section.py delete mode 100644 src/aipass/spawn/templates/builder/STATUS.local.md diff --git a/.aipass/PROMPT_STYLE.md b/.aipass/PROMPT_STYLE.md index 95cb00a5..e6c688b9 100644 --- a/.aipass/PROMPT_STYLE.md +++ b/.aipass/PROMPT_STYLE.md @@ -17,7 +17,7 @@ Goal: signal density over prose. Prompts are injected every turn — every line # What NOT to put in a prompt - - Session state, current work, in-flight issues. That goes in `STATUS.local.md` and `.trinity/local.json`. + - Session state, current work, in-flight issues. That goes in `.trinity/local.json` (todos[]) and `DASHBOARD.local.json`. - Long explanations of how a system works. Plant a breadcrumb ("see `@branch --help`") and move on. - Personal notes ("remember, you like short replies"). That goes in `.trinity/observations.json`. - Version numbers, PR numbers, dates. Those rot within days. diff --git a/.aipass/aipass_global_prompt.md b/.aipass/aipass_global_prompt.md index 035ff085..210a8c71 100644 --- a/.aipass/aipass_global_prompt.md +++ b/.aipass/aipass_global_prompt.md @@ -11,7 +11,7 @@ Patterns here are exact. Don't guess command syntax — examples are the API. Mi # AIPL — Terse Writing Convention -When writing .trinity/, ai_mail, STATUS.local.md, plans: use AIPL. Human-facing output (CLI, logs, README): use English. +When writing .trinity/, ai_mail, plans: use AIPL. Human-facing output (CLI, logs, README): use English. Rules: - Drop grammar: the, a, an, for, with, on, in, at, to, from, of, by, and, but, or, was, were, been @@ -151,19 +151,16 @@ Never create plan files manually. Always `drone @flow create`. Flow handles numb `.trinity/` files are your memories — experiential, personal, yours. How you persist across sessions. -`STATUS.local.md` is different — live status beacon for ecosystem. Auto-synced to central `STATUS.md` on PR create/merge. Other agents read STATUS to see your state without digging into memories. Crossover with `local.json` fine — same fact, different purpose: `local.json` for you, `STATUS.local.md` for ecosystem. - -Four files: +Three files: - `passport.json` — IDENTITY. Role, purpose, principles. Update only when identity genuinely evolves. -- `local.json` — YOUR MEMORY. Session log (`sessions[]`) + `key_learnings`. What happened, what learned, what matters next. +- `local.json` — YOUR MEMORY. Session log (`sessions[]`) + `key_learnings` + `todos[]`. What happened, what learned, what matters next. - `observations.json` — MEMORY OF THE USER. Preferences, style, friction, breakthroughs. Skip if nothing new this session. -- `STATUS.local.md` — PUBLIC BEACON. Current work, issues, todos, recently completed. Notepad for quick captures. Where to put what: - "Worked on DPLAN-0125, learned about peak hours" → `local.json` - "User prefers short replies" → `observations.json` -- "PR #266 needs merge, Track G blocked" → `STATUS.local.md` -- "Fix drone help formatting" as reminder → `STATUS.local.md` Notepad +- "PR #266 needs merge, Track G blocked" → `local.json` todos[] +- "Fix drone help formatting" as reminder → `local.json` todos[] - "Role shifted from builder to orchestrator" → `passport.json` Save proactively. Triggers: after milestone, decision, learning, before switching topics. @@ -195,7 +192,7 @@ Use sub-agents for: Do it yourself only when: - User explicitly asks you to read or look at something - Tiny edits — fix a typo, update a memory file, small config change -- Writing memories, STATUS, plan updates (your own files) +- Writing memories, plan updates (your own files) - Quick one-line commands — drone status, inbox check How to use them: @@ -247,7 +244,7 @@ Small knowledge traces trigger awareness. Not full knowledge — enough to know Prompts: plant breadcrumbs, not encyclopedias. Two lines ("this exists, look here") beat twenty explaining how. -Prompts are signposts, not journals. Injected every turn — keep minimal. Never track state/sessions/context in prompts. State → `.trinity/` + `STATUS.local.md`. Prompts guide; memories record; registries catalog. +Prompts are signposts, not journals. Injected every turn — keep minimal. Never track state/sessions/context in prompts. State → `.trinity/` + `DASHBOARD.local.json`. Prompts guide; memories record; registries catalog. If `drone` can't find the AIPass registry, set `AIPASS_HOME=/path/to/AIPass` in shell profile and `~/.claude/settings.json` env block. diff --git a/.aipass/project_CLAUDE.md b/.aipass/project_CLAUDE.md index af8c4a3d..cae8ed4d 100644 --- a/.aipass/project_CLAUDE.md +++ b/.aipass/project_CLAUDE.md @@ -6,7 +6,7 @@ Agent workspace powered by AIPass. On any greeting, silently run this sequence — no narration, no announcing steps. Just do it and respond with the status. - - Read: `.trinity/passport.json`, `.trinity/local.json`, `.trinity/observations.json`, `README.md`, `STATUS.local.md` + - Read: `.trinity/passport.json`, `.trinity/local.json`, `.trinity/observations.json`, `README.md` Use drone commands for all operations. Never raw git, gh, file access, or python -m when drone provides it. diff --git a/.claude/commands/prep.md b/.claude/commands/prep.md index f08d92a9..7659df03 100644 --- a/.claude/commands/prep.md +++ b/.claude/commands/prep.md @@ -14,9 +14,8 @@ Purpose: Button up everything at the end of a session — or before a /compact. Each memory file plays a distinct role. Update based on what actually changed this session. - **`.trinity/passport.json`** — IDENTITY. Who you are: role, capabilities, principles. Only update if identity genuinely evolved this session. -- **`.trinity/local.json`** — YOUR MEMORY. Add/update session entry with a summary of work done. Add key_learnings for anything learned. Trim oldest sessions if over 20. +- **`.trinity/local.json`** — YOUR MEMORY. Add/update session entry with a summary of work done. Add key_learnings for anything learned. Update todos[] with current in-flight items. Trim oldest sessions if over 20. - **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. Collaboration insights, preferences, friction points. Skip if nothing new about the user this session. -- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known issues, todos, notepad. Auto-synced to central STATUS.md on PR events — this is how other branches see you. Keep Current Work accurate. ## 2. Active Plans @@ -38,7 +37,7 @@ Each memory file plays a distinct role. Update based on what actually changed th ## 5. Loose Ends - Flag anything in-flight: running background agents, dispatched branches waiting for replies, pending decisions -- If anything can't survive compaction (e.g., agent IDs needed for resume), write it to STATUS.local.md Notepad +- If anything can't survive compaction (e.g., agent IDs needed for resume), write it to local.json todos[] ## Confirm @@ -47,7 +46,6 @@ List everything updated. Format: Prep complete: - local.json: [what was added] - observations.json: [updated / skipped] -- STATUS.local.md: [updated / skipped] - Plans: [which ones updated] - Git: [branch, uncommitted count, suggestion] - Inbox: [count, action taken] diff --git a/.claude/templates/memo.md b/.claude/templates/memo.md index 948ac39b..37b06a61 100644 --- a/.claude/templates/memo.md +++ b/.claude/templates/memo.md @@ -14,9 +14,8 @@ Purpose: Update branch memory files after completing work this session. Each memory file plays a distinct role. Update based on what actually changed this session. - **`.trinity/passport.json`** — IDENTITY. Who you are: role, capabilities, principles. Only update if identity genuinely evolved this session. Don't touch it just to touch it. -- **`.trinity/local.json`** — YOUR MEMORY. Session history and key_learnings. Add a session entry for significant work. Add key_learnings for facts you'd need next time. Trim oldest sessions if over 20. +- **`.trinity/local.json`** — YOUR MEMORY. Session history, key_learnings, and todos[]. Add a session entry for significant work. Add key_learnings for facts you'd need next time. Update todos[] with open items. Trim oldest sessions if over 20. - **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. Collaboration insights, preferences, friction points, flow states. Skip entirely if nothing new about the user this session. -- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known issues, todos, notepad. Auto-synced to central STATUS.md on PR events — this is how other branches see you. Keep Current Work accurate and drop quick notes in the Notepad section. ## If Relevant diff --git a/.codex/skills/memo/SKILL.md b/.codex/skills/memo/SKILL.md index 558fffaf..d79589bc 100644 --- a/.codex/skills/memo/SKILL.md +++ b/.codex/skills/memo/SKILL.md @@ -25,4 +25,3 @@ Purpose: Update branch memory files after completing work this session. - **.trinity/passport.json** — Evolve identity when the branch's role, capabilities, or principles have genuinely changed. Don't update just to update — but don't leave placeholders forever either. - **README.md** — Does it reflect current state? Update if stale. -- **STATUS.local.md** — Drop quick notes on issues, todos, or ideas in the Notepad section. diff --git a/.codex/skills/prep/SKILL.md b/.codex/skills/prep/SKILL.md index ffd98e50..86d73b93 100644 --- a/.codex/skills/prep/SKILL.md +++ b/.codex/skills/prep/SKILL.md @@ -38,7 +38,7 @@ Purpose: Button up everything at the end of a session — or before a /compact. ## 5. Loose Ends - Flag anything in-flight: running background agents, dispatched branches waiting for replies, pending decisions -- If anything can't survive compaction, write it to STATUS.local.md Notepad +- If anything can't survive compaction, write it to local.json todos[] ## Confirm diff --git a/.gitignore b/.gitignore index 6fb0750a..a928925e 100644 --- a/.gitignore +++ b/.gitignore @@ -110,7 +110,6 @@ src/aipass/*/apps/integrations/** !src/aipass/spawn/templates/builder/docs.local/ !src/aipass/spawn/templates/builder/docs.local/** !src/aipass/spawn/templates/builder/DASHBOARD.local.json -!src/aipass/spawn/templates/builder/STATUS.local.md # CI artifacts windows-pytest-results/ diff --git a/AGENTS.md b/AGENTS.md index b108cc14..e93c107a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ User: user On any greeting, silently read these files from CWD and run the commands — no narration, no announcing steps. Just do it and respond with the status. - - Read: `.trinity/passport.json`, `.trinity/local.json`, `.trinity/observations.json`, `README.md`, `STATUS.local.md` + - Read: `.trinity/passport.json`, `.trinity/local.json`, `.trinity/observations.json`, `README.md` - Check: `drone @ai_mail inbox` — process any mail, don't ask. - Run: `drone @git status` diff --git a/CHANGELOG.md b/CHANGELOG.md index b10136d2..c5d9aeac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,24 @@ and this project uses [Calendar Versioning](https://calver.org/) in the format ## [2026.W23] - 2026-06-02 +### Removed + +- **The entire STATUS flow is decommissioned (TDPLAN-0007).** The per-branch + hand-maintained `STATUS.local.md` beacon and the auto-aggregated central + `STATUS.md` (853 lines / 70 KB nobody read) are gone — deleted from disk + across all 13 branches and scrubbed from every prompt, doc, startup protocol, + `/prep` + `/memo` skill, the compact-recovery hook, the email footer, and + `aipass init` / spawn scaffolding. Live branch state was already fully covered + by `DASHBOARD.local.json` (prax) and history by `.trinity/local.json`. The + status-sync engine is kept **intact but inert** — made dormant by unwiring its + 3-line trigger registration (`trigger registry.py`), so the code stays + revivable. The one thing STATUS uniquely gave us — a quick scratch todo — is + replaced by an operational `todos[]` section in `.trinity/local.json` + (@memory-owned schema, capped, never vectorized by rollover), pushed to all 13 + branches and surfaced as a `todo_count` on the dashboard. Shipped as one + coordinated cross-branch change (memory, prax, trigger, aipass, spawn, hooks, + ai_mail, seedgo + devpulse). + ### Changed - **All 13 branches at seedgo 100% under the new introspection standard.** diff --git a/CLAUDE.md b/CLAUDE.md index 1b5a3db8..7ef166d6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,10 +10,9 @@ On any greeting, silently run this sequence — no narration, no announcing step These steps are sequential and dependent — run each ONCE, wait for the result, then proceed. Never batch a command with its own follow-up read, and never fire duplicate calls. If output looks blank, wait — don't retry. - - Read: `.trinity/passport.json`, `.trinity/local.json`, `.trinity/observations.json`, `README.md`, `STATUS.local.md` + - Read: `.trinity/passport.json`, `.trinity/local.json`, `.trinity/observations.json`, `README.md` - Refresh: `drone @prax dashboard refresh @` — where `` is your branch name (CWD directory name) - Dashboard: Read `DASHBOARD.local.json` — act on what needs attention (new mail → check inbox, active plans → note them). This is your single status glance. - - Refresh: If `STATUS.local.md` is stale (last updated date older than latest session in local.json), update it from your memories. Keep Current Work accurate. Use drone commands for all operations. Never raw git, gh, file access, or python -m when drone provides it. diff --git a/README.md b/README.md index 72878733..8cc57502 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ AIPass stores everything locally in your project directory. To remove it: ```bash # Remove AIPass files from your project rm -rf .aipass/ .claude/ .ai_mail.local/ hooks/ src/ -rm -f CLAUDE.md AGENTS.md STATUS.local.md *_REGISTRY.json .gitignore +rm -f CLAUDE.md AGENTS.md *_REGISTRY.json .gitignore # If you installed via pip pip uninstall aipass diff --git a/src/aipass/ai_mail/apps/handlers/email/footer.py b/src/aipass/ai_mail/apps/handlers/email/footer.py index fa42a2b5..c5ef8dab 100644 --- a/src/aipass/ai_mail/apps/handlers/email/footer.py +++ b/src/aipass/ai_mail/apps/handlers/email/footer.py @@ -22,7 +22,6 @@ STANDARD_FOOTER = """ ⚠️ TASK CHECKLIST (before marking complete): □ SEEDGO CHECK → drone @seedgo audit @branch (80%+) □ UPDATE MEMORIES → Your .trinity/local.json records this work -□ UPDATE STATUS → Your STATUS.local.md reflects current state □ CLOSE FPLAN → drone @flow close □ EMAIL SENDER → drone @ai_mail email @ "Subject" "Summary" diff --git a/src/aipass/aipass/.aipass/aipass_local_prompt.md b/src/aipass/aipass/.aipass/aipass_local_prompt.md index 37605e50..3f6273d8 100644 --- a/src/aipass/aipass/.aipass/aipass_local_prompt.md +++ b/src/aipass/aipass/.aipass/aipass_local_prompt.md @@ -1,6 +1,6 @@ # AIPASS — Branch Prompt -*Injected every turn. Breadcrumbs only — details: README, --help, .trinity/ memories, STATUS.local.md.* +*Injected every turn. Breadcrumbs only — details: README, --help, .trinity/ memories.* ## Identity diff --git a/src/aipass/aipass/apps/handlers/init/bootstrap.py b/src/aipass/aipass/apps/handlers/init/bootstrap.py index 034ad7ec..1314df76 100644 --- a/src/aipass/aipass/apps/handlers/init/bootstrap.py +++ b/src/aipass/aipass/apps/handlers/init/bootstrap.py @@ -15,11 +15,10 @@ Business logic for `aipass init`. Creates the project scaffold: 3. CLAUDE.md — project prompt (Claude Code reads this) 4. AGENTS.md — Codex equivalent of CLAUDE.md 5. README.md — getting started guide - 6. STATUS.local.md — project status - 7. .gitignore — standard AIPass ignores - 8. .claude/settings.json — Claude Code hooks configuration - 9. src/ — directory where agents live - 10. .ai_mail.local/inbox.json — empty project mailbox + 6. .gitignore — standard AIPass ignores + 7. .claude/settings.json — Claude Code hooks configuration + 8. src/ — directory where agents live + 9. .ai_mail.local/inbox.json — empty project mailbox Projects are NOT citizens — no .trinity/ directory. Identity lives in the registry JSON. Init is re-runnable: existing files are skipped, not errors. @@ -364,16 +363,7 @@ def init_project(target: Path, project_name: str | None = None) -> dict: readme_md_path.write_text(readme_content, encoding="utf-8") created.append(str(readme_md_path)) - # 7. STATUS.local.md - status_md_path = target / "STATUS.local.md" - if not status_md_path.exists(): - status_md_path.write_text( - f"# {name}\n\n**State:** New\n**Last update:** {today}\n\n## Current Work\n\n## Known Issues\n- None\n", - encoding="utf-8", - ) - created.append(str(status_md_path)) - - # 8. .gitignore + # 7. .gitignore gitignore_path = target / ".gitignore" if not gitignore_path.exists(): gitignore_path.write_text(sc.gitignore(), encoding="utf-8") @@ -450,7 +440,7 @@ def update_project(target: Path) -> dict: """Update managed scaffold files in an existing AIPass project. Overwrites managed prompt and config files with the latest templates while - leaving all user-owned files (registry, README, STATUS.local.md, .gitignore, + leaving all user-owned files (registry, README, .gitignore, src/) untouched. Args: @@ -586,7 +576,6 @@ def update_project(target: Path) -> dict: for skip_name in ( str(registry_path), str(target / "README.md"), - str(target / "STATUS.local.md"), str(target / ".gitignore"), ): skipped.append(skip_name) diff --git a/src/aipass/aipass/apps/handlers/init/scaffold_content.py b/src/aipass/aipass/apps/handlers/init/scaffold_content.py index 9e5693a3..6db858cf 100644 --- a/src/aipass/aipass/apps/handlers/init/scaffold_content.py +++ b/src/aipass/aipass/apps/handlers/init/scaffold_content.py @@ -40,8 +40,8 @@ def readme_md(name: str) -> str: "cd src/my_agent/\n" "claude # or your preferred AI CLI\n" "\n" - "# 3. Check project status\n" - "cat STATUS.local.md\n" + "# 3. Check project health\n" + "drone @seedgo audit .\n" "```\n" "\n" "## Project Structure\n" @@ -52,7 +52,6 @@ def readme_md(name: str) -> str: " .aipass/ # Prompts (injected per-turn)\n" " CLAUDE.md # Claude Code instructions\n" " AGENTS.md # Codex instructions\n" - " STATUS.local.md # Project status\n" " src/ # Agent directories live here\n" " / # Created via aipass init agent\n" "```\n" @@ -212,7 +211,6 @@ def gitignore() -> str: ".trinity/\n" ".ai_mail.local/\n" "*.local.*\n" - "!STATUS.local.md\n" "\n" "# Plans (local working docs)\n" "DPLAN-*\n" @@ -279,9 +277,6 @@ def prep_md() -> str: "- **`.trinity/observations.json`** — YOUR MEMORY OF THE USER. " "Collaboration insights, preferences, friction points. Skip if nothing " "new about the user this session.\n" - "- **`STATUS.local.md`** — PUBLIC STATUS BEACON. Current work, known " - "issues, todos, notepad. Auto-synced to central STATUS.md on PR events " - "— this is how other branches see you. Keep Current Work accurate.\n" "\n" "## 2. Active Plans\n" "\n" @@ -308,7 +303,7 @@ def prep_md() -> str: "- Flag anything in-flight: running background agents, dispatched " "branches waiting for replies, pending decisions\n" "- If anything can't survive compaction (e.g., agent IDs needed for " - "resume), write it to STATUS.local.md Notepad\n" + "resume), write it to local.json key_learnings\n" "\n" "## Confirm\n" "\n" @@ -317,7 +312,6 @@ def prep_md() -> str: "Prep complete:\n" "- local.json: [what was added]\n" "- observations.json: [updated / skipped]\n" - "- STATUS.local.md: [updated / skipped]\n" "- Plans: [which ones updated]\n" "- Git: [branch, uncommitted count, suggestion]\n" "- Inbox: [count, action taken]\n" diff --git a/src/aipass/aipass/tests/test_bootstrap.py b/src/aipass/aipass/tests/test_bootstrap.py index ce01f749..2baae54a 100644 --- a/src/aipass/aipass/tests/test_bootstrap.py +++ b/src/aipass/aipass/tests/test_bootstrap.py @@ -102,7 +102,6 @@ def test_init_project_creates_all_expected_files(tmp_path): target / "CLAUDE.md", target / "AGENTS.md", target / "README.md", - target / "STATUS.local.md", target / ".gitignore", target / ".claude" / "settings.json", target / ".claude" / "commands" / "prep.md", @@ -127,7 +126,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"]) >= 12 + assert len(result["created_files"]) >= 11 def test_init_project_return_dict_structure(tmp_path): @@ -325,7 +324,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"]) >= 12 + assert len(result["created_files"]) >= 11 def test_init_project_defaults_name_from_directory(tmp_path): @@ -364,7 +363,6 @@ def test_init_project_skips_existing_optional_files(tmp_path): (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") - (target / "STATUS.local.md").write_text("# Custom status\n", encoding="utf-8") (target / ".gitignore").write_text("# Custom\n", encoding="utf-8") claude_dir = target / ".claude" @@ -518,7 +516,6 @@ def test_update_project_never_touches_user_owned_files(tmp_path): # Modify user-owned files (target / "README.md").write_text("# My custom README\n", encoding="utf-8") - (target / "STATUS.local.md").write_text("# Custom status\n", encoding="utf-8") (target / ".gitignore").write_text("# custom\n", encoding="utf-8") result = update_project(target) @@ -526,12 +523,10 @@ def test_update_project_never_touches_user_owned_files(tmp_path): skipped = result["skipped_files"] assert any("REGISTRY" in s for s in skipped) assert any("README.md" in s for s in skipped) - assert any("STATUS.local.md" in s for s in skipped) assert any(".gitignore" in s for s in skipped) # User customisations are preserved assert (target / "README.md").read_text(encoding="utf-8") == "# My custom README\n" - assert (target / "STATUS.local.md").read_text(encoding="utf-8") == "# Custom status\n" def test_update_project_creates_missing_managed_dirs(tmp_path): @@ -556,15 +551,15 @@ def test_update_project_creates_missing_managed_dirs(tmp_path): def test_update_project_skipped_files_count(tmp_path): - """update_project skips 4 user-owned files + existing mailbox = 5 total.""" + """update_project skips 3 user-owned files.""" target = tmp_path / "proj" target.mkdir() init_project(target, project_name="count") result = update_project(target) - # 4 user-owned (registry, README, STATUS, .gitignore) - assert len(result["skipped_files"]) == 4 + # 3 user-owned (registry, README, .gitignore) + assert len(result["skipped_files"]) == 3 # --------------------------------------------------------------------------- diff --git a/src/aipass/devpulse/.aipass/aipass_local_prompt.md b/src/aipass/devpulse/.aipass/aipass_local_prompt.md index 6fb0e16f..0493c7f6 100644 --- a/src/aipass/devpulse/.aipass/aipass_local_prompt.md +++ b/src/aipass/devpulse/.aipass/aipass_local_prompt.md @@ -1,6 +1,6 @@ # DEVPULSE — Branch Prompt -Injected every turn. Breadcrumbs only — details in README, --help, .trinity/, STATUS.local.md. +Injected every turn. Breadcrumbs only — details in README, --help, .trinity/, DASHBOARD.local.json. ## Identity @@ -9,7 +9,7 @@ DEVPULSE — Patrick's primary AI collaborator, orchestration hub. Design, plan, ## How You Work - DRONE FOR EVERYTHING. Never raw git, gh, or python -m. `drone` is on PATH — run it directly. No which, no path lookup, no verification. Just `drone @git ...`, `drone @flow ...`, `drone @ai_mail ...`. If blocked, drone is the fix — not a workaround. -- Build own directly: modules, DPLANs, FPLANs, memories, STATUS — yours, edit freely. +- Build own directly: modules, DPLANs, FPLANs, memories — yours, edit freely. - Prototype to explore shape, hand real build to sub-agent. - Investigate other branches freely: read, debug, test, fix small bugs. CWD stays devpulse. - Full multi-file implementations → `drone @ai_mail dispatch @branch`. @@ -97,7 +97,7 @@ drone, seedgo, prax, cli, ai_mail, api, flow, spawn, trigger, memory, aipass, ho - Lean on branches for expertise. Email for architecture questions. Investigate/debug/test freely. - Use memories freely. Rollover to @memory by design. Update .trinity/ often. -- STATUS.local.md Notepad for friction notes. Address in batches. +- local.json todos[] for friction notes. Address in batches. - Own things → build directly. Heavy refactors → delegate sub-agent. - CWD = identity. Visit other branches, don't move in. - Git awareness: after completing work, `drone @git status`. Suggest commit if coherent. Don't force, don't let pile up. @@ -128,10 +128,10 @@ Find agent via `.trinity/passport.json`. Use `dangerouslyDisableSandbox: true`. ## Memory & Tracking -- `.trinity/local.json` — session history, key learnings +- `.trinity/local.json` — session history, key learnings, todos[] - `.trinity/observations.json` — collaboration patterns -- `STATUS.local.md` — current work, issues, todos, notepad. Feeds central STATUS.md. +- `DASHBOARD.local.json` — live state glance (refreshed by prax) Update proactively — after milestones, /memo, topic shifts, 5+ actions without saving. -This prompt = lightweight signposts. State → .trinity/ + STATUS.local.md. +This prompt = lightweight signposts. State → .trinity/ + DASHBOARD.local.json. diff --git a/src/aipass/devpulse/README.md b/src/aipass/devpulse/README.md index bac85c72..a80fc5eb 100644 --- a/src/aipass/devpulse/README.md +++ b/src/aipass/devpulse/README.md @@ -10,7 +10,7 @@ DevPulse handles the day-to-day: working with the user to plan, design, troubles | You want to | Read | |---|---| -| What's happening right now | [STATUS.local.md](STATUS.local.md) | +| What's happening right now | `DASHBOARD.local.json` | | Identity, memory, session history | [`.trinity/`](.trinity/) | | Active plans | `drone @flow list open` | | Branch list | `drone systems` | @@ -46,7 +46,7 @@ src/aipass/devpulse/ ├── artifacts/ # Birth certificate, reports ├── dropbox/ # Received files, archived plans, install audit ├── docs/ # Transition notes -└── STATUS.local.md # Current work beacon +└── DASHBOARD.local.json # Live state (refreshed by prax) ``` ## Commands diff --git a/src/aipass/flow/templates/playbook_plans/sunday_merge.md b/src/aipass/flow/templates/playbook_plans/sunday_merge.md index 799890cf..c8f9fea0 100644 --- a/src/aipass/flow/templates/playbook_plans/sunday_merge.md +++ b/src/aipass/flow/templates/playbook_plans/sunday_merge.md @@ -89,7 +89,6 @@ Steps: ## 8. Wrap - [ ] Update `.trinity/` memories (session log: what merged, PR#, tag) -- [ ] Update `STATUS.local.md` - [ ] Fill **Run Summary** below (PR numbers, tag, anything that broke) - [ ] Close this playbook → vectorizes the run diff --git a/src/aipass/hooks/.aipass/aipass_local_prompt.md b/src/aipass/hooks/.aipass/aipass_local_prompt.md index eca0b18b..07a4f6df 100644 --- a/src/aipass/hooks/.aipass/aipass_local_prompt.md +++ b/src/aipass/hooks/.aipass/aipass_local_prompt.md @@ -1,6 +1,6 @@ # HOOKS -- Branch Prompt -Injected every turn. Breadcrumbs only -- details in README, --help, .trinity/, STATUS.local.md. +Injected every turn. Breadcrumbs only -- details in README, --help, .trinity/. ## Identity diff --git a/src/aipass/hooks/README.md b/src/aipass/hooks/README.md index befbd313..6cc926c6 100644 --- a/src/aipass/hooks/README.md +++ b/src/aipass/hooks/README.md @@ -10,7 +10,6 @@ Every hook event flows through one engine. Platform bridges normalize the event | You want to | Read | |---|---| -| What's happening right now | [STATUS.local.md](STATUS.local.md) | | Identity, memory, session history | [`.trinity/`](.trinity/) | | Hook engine design | `DPLAN-0184` | | Per-project config | `.aipass/hooks.json` | @@ -78,8 +77,7 @@ src/aipass/hooks/ │ └── diagnostics.py # JSONL logging for hook execution ├── logs/ │ └── engine.jsonl # JSONL diagnostics (every hook execution) -├── tests/ # 385 tests across 20 test files -└── STATUS.local.md +└── tests/ # 385 tests across 20 test files ``` ## How It Works diff --git a/src/aipass/hooks/apps/handlers/lifecycle/compact.py b/src/aipass/hooks/apps/handlers/lifecycle/compact.py index 4479f405..1f4111e5 100644 --- a/src/aipass/hooks/apps/handlers/lifecycle/compact.py +++ b/src/aipass/hooks/apps/handlers/lifecycle/compact.py @@ -31,17 +31,6 @@ def _find_branch_dir(cwd: str) -> Path | None: return None -def _read_status_local(branch_dir: Path) -> str | None: - for name in ("STATUS.local.md", "dev.local.md"): - path = branch_dir / name - if path.is_file(): - try: - return path.read_text(encoding="utf-8")[:3000] - except Exception as exc: - logger.info("[HOOKS] compact: read status failed: %s", exc) - return None - - def _read_last_session(branch_dir: Path) -> str | None: local_path = branch_dir / ".trinity" / "local.json" if not local_path.is_file(): @@ -115,18 +104,13 @@ def handle(hook_data: dict) -> dict: if session_info: sections.append(f"## Last Session\n{session_info}") - status = _read_status_local(branch_dir) - if status: - sections.append(f"## STATUS.local.md\n{status}") - is_dispatched = os.environ.get("AIPASS_SESSION_TYPE") == "dispatched" if is_dispatched: sections.append( "## DISPATCHED AGENT — SAVE STATE NOW\n" "Before continuing work, you MUST update your memories:\n" "1. Update .trinity/local.json — add/update current session with work done so far\n" - "2. Update STATUS.local.md — ensure Current Work reflects what you've accomplished\n" - "3. Then continue your task from where the summary left off\n\n" + "2. Then continue your task from where the summary left off\n\n" "This is non-optional. Compaction just happened — if you don't save now, work history is lost." ) else: @@ -134,7 +118,6 @@ def handle(hook_data: dict) -> dict: "## Recovery Protocol\n" "- Continue where the summary left off — don't restart or ask generic questions\n" "- .trinity/local.json has full session history and key_learnings — read it if you need more context\n" - "- STATUS.local.md has current work, known issues, and todos\n" "- Save memories proactively — compaction just proved you need to\n" "- Match the conversation tone from before compaction" ) diff --git a/src/aipass/hooks/tests/test_compact.py b/src/aipass/hooks/tests/test_compact.py index 7ece7081..9e704546 100644 --- a/src/aipass/hooks/tests/test_compact.py +++ b/src/aipass/hooks/tests/test_compact.py @@ -29,9 +29,6 @@ class TestCompactHandler: ), encoding="utf-8", ) - status = tmp_path / "STATUS.local.md" - status.write_text("# Status\nCurrent work here", encoding="utf-8") - with patch("aipass.hooks.apps.handlers.lifecycle.compact.speak"): with patch("aipass.hooks.apps.handlers.lifecycle.compact._get_git_info", return_value="Git branch: dev"): result = handle({"cwd": str(tmp_path)}) @@ -40,7 +37,7 @@ class TestCompactHandler: assert "POST-COMPACT RECOVERY" in result["stdout"] assert "Git branch: dev" in result["stdout"] assert "did stuff" in result["stdout"] - assert "Current work here" in result["stdout"] + assert "STATUS.local.md" not in result["stdout"] def test_returns_recovery_when_no_branch_dir(self): from aipass.hooks.apps.handlers.lifecycle.compact import handle @@ -64,6 +61,7 @@ class TestCompactHandler: result = handle({"cwd": str(tmp_path)}) assert "SAVE STATE NOW" in result["stdout"] + assert "STATUS.local.md" not in result["stdout"] def test_interactive_gets_recovery_protocol(self, tmp_path): from aipass.hooks.apps.handlers.lifecycle.compact import handle @@ -77,6 +75,7 @@ class TestCompactHandler: result = handle({"cwd": str(tmp_path)}) assert "Recovery Protocol" in result["stdout"] + assert "STATUS.local.md" not in result["stdout"] def test_empty_hook_data(self): from aipass.hooks.apps.handlers.lifecycle.compact import handle diff --git a/src/aipass/memory/apps/handlers/templates/pusher.py b/src/aipass/memory/apps/handlers/templates/pusher.py index 0904b45b..b960869f 100644 --- a/src/aipass/memory/apps/handlers/templates/pusher.py +++ b/src/aipass/memory/apps/handlers/templates/pusher.py @@ -221,6 +221,11 @@ def _apply_template_to_local(current: dict, template: dict, branch_name: str) -> data["key_learnings"] = {} changes.append("key_learnings: added (empty)") + # Todos: add if missing (operational list, not rolled over) + if "todos" not in data: + data["todos"] = [] + changes.append("todos: added (empty)") + # Active tasks: ensure recently_completed exists if "active_tasks" in data and isinstance(data["active_tasks"], dict): if "recently_completed" not in data["active_tasks"]: diff --git a/src/aipass/memory/templates/.template_version.json b/src/aipass/memory/templates/.template_version.json index 918795a9..bf10ddd1 100644 --- a/src/aipass/memory/templates/.template_version.json +++ b/src/aipass/memory/templates/.template_version.json @@ -1,16 +1,17 @@ { - "last_push": "2026-04-22 20:07:31", + "last_push": "2026-06-07 21:40:48", "last_push_branches": [ - "AI_MAIL", - "API", - "CLI", - "DEVPULSE", - "DRONE", - "FLOW", - "MEMORY", - "PRAX", - "SEEDGO", - "SPAWN", - "TRIGGER" + "HOOKS", + "aipass", + "api", + "cli", + "devpulse", + "drone", + "flow", + "memory", + "prax", + "seedgo", + "spawn", + "trigger" ] } diff --git a/src/aipass/memory/templates/LOCAL.template.json b/src/aipass/memory/templates/LOCAL.template.json index 75f4538d..c050ba85 100644 --- a/src/aipass/memory/templates/LOCAL.template.json +++ b/src/aipass/memory/templates/LOCAL.template.json @@ -15,9 +15,11 @@ "limits": { "max_sessions": 20, "max_key_learnings": 25, + "max_todos": 10, "session_summary_max_chars": 150, "learning_value_max_chars": 200, - "note": "DO NOT trim, prune, or delete entries. Rollover to @memory handles overflow automatically. Just add new entries." + "todo_text_max_chars": 200, + "note": "DO NOT trim, prune, or delete entries. Rollover to @memory handles overflow automatically. Just add new entries. todos[] is OPERATIONAL — completing/removing items is normal, not data destruction." }, "status": { "health": "healthy", @@ -26,6 +28,7 @@ } }, "key_learnings": {}, + "todos": [], "sessions": [ { "session_number": 1, diff --git a/src/aipass/memory/tests/test_handlers.py b/src/aipass/memory/tests/test_handlers.py index 2ce12188..7f8cd6da 100644 --- a/src/aipass/memory/tests/test_handlers.py +++ b/src/aipass/memory/tests/test_handlers.py @@ -12,6 +12,7 @@ Covers: - rollover/extractor.py (_extract_items_v2, _detect_growing_array, helpers) - tracking/line_counter.py (_count_physical_lines, update_line_count) - schema/normalize.py (normalize_memory_file) + - todos[] operational schema (rollover ignores, caps enforced) All tests use mocks/tmp_path -- no live filesystem or infrastructure access. """ @@ -542,3 +543,115 @@ class TestNormalizeMemoryFile: assert "max_word_count" not in data["document_metadata"]["limits"] assert "max_token_count" not in data["document_metadata"]["limits"] assert data["document_metadata"]["limits"]["max_lines"] == 600 + + +class TestTodosOperational: + """Confirm rollover treats todos[] as operational — never vectorized or trimmed.""" + + def _make_data_with_todos(self, num_sessions=5, max_sessions=3, num_todos=5): + """Build v2 data that includes a populated todos[] array.""" + sessions = [ + {"session_number": i, "date": f"2026-01-{i:02d}", "summary": f"Session {i}"} + for i in range(1, num_sessions + 1) + ] + todos = [{"id": f"t{i}", "text": f"Todo item {i}", "created": f"2026-06-0{i}"} for i in range(1, num_todos + 1)] + return { + "document_metadata": { + "schema_version": "2.0.0", + "limits": { + "max_sessions": max_sessions, + "max_key_learnings": 25, + "max_todos": 10, + "todo_text_max_chars": 200, + }, + "status": {"current_lines": 100}, + }, + "sessions": sessions, + "key_learnings": {}, + "todos": todos, + } + + def test_v2_extraction_leaves_todos_untouched(self, monkeypatch, tmp_path): + """v2 rollover trims sessions but never touches todos[].""" + ext, _ = _import_extractor(monkeypatch) + data = self._make_data_with_todos(num_sessions=6, max_sessions=3, num_todos=5) + + mem_file = tmp_path / ".trinity" / "local.json" + mem_file.parent.mkdir(parents=True) + mem_file.write_text(json.dumps(data, indent=2), encoding="utf-8") + + def fake_write(fp, d): + fp.write_text(json.dumps(d, indent=2), encoding="utf-8") + + with patch.object(ext, "_write_memory_file", side_effect=fake_write): + result = ext._extract_items_v2(mem_file, data) + + assert result["success"] is True + assert len(data["sessions"]) == 3 + assert len(data["todos"]) == 5 + assert data["todos"][0]["id"] == "t1" + assert data["todos"][4]["id"] == "t5" + + def test_v1_detect_growing_array_ignores_todos(self, monkeypatch): + """v1 _detect_growing_array does NOT consider todos as a growing array.""" + ext, _ = _import_extractor(monkeypatch) + data = { + "sessions": [{"session_number": 1}], + "todos": [{"id": "t1", "text": "Something"}], + } + result = ext._detect_growing_array(data) + assert result == "sessions" + + def test_v1_detect_growing_array_skips_todos_only(self, monkeypatch): + """If only todos[] exists (no memory arrays), _detect_growing_array returns None.""" + ext, _ = _import_extractor(monkeypatch) + data = { + "todos": [{"id": "t1", "text": "Something"}], + "key_learnings": {"k1": "v1"}, + } + result = ext._detect_growing_array(data) + assert result is None + + def test_todos_schema_shape(self): + """Validate the expected todos[] item schema: id, text, created, optional priority.""" + todo_item = {"id": "t1", "text": "Fix the bug", "created": "2026-06-07"} + assert "id" in todo_item + assert "text" in todo_item + assert "created" in todo_item + + todo_with_priority = {**todo_item, "priority": "high"} + assert todo_with_priority["priority"] == "high" + + def test_todos_cap_enforced(self): + """max_todos and todo_text_max_chars are the cap boundaries.""" + limits = { + "max_todos": 10, + "todo_text_max_chars": 200, + } + todos = [{"id": f"t{i}", "text": "x" * 200, "created": "2026-06-07"} for i in range(10)] + assert len(todos) <= limits["max_todos"] + assert all(len(t["text"]) <= limits["todo_text_max_chars"] for t in todos) + + over_cap = todos + [{"id": "t11", "text": "extra", "created": "2026-06-07"}] + assert len(over_cap) > limits["max_todos"] + + def test_todos_survives_full_extraction_cycle(self, monkeypatch, tmp_path): + """End-to-end: extract_items on a file with todos[] preserves them completely.""" + ext, _ = _import_extractor(monkeypatch) + data = self._make_data_with_todos(num_sessions=25, max_sessions=20, num_todos=8) + + mem_file = tmp_path / ".trinity" / "local.json" + mem_file.parent.mkdir(parents=True) + mem_file.write_text(json.dumps(data, indent=2), encoding="utf-8") + + def fake_write(fp, d): + fp.write_text(json.dumps(d, indent=2), encoding="utf-8") + + with patch.object(ext, "_write_memory_file", side_effect=fake_write): + result = ext._extract_items_v2(mem_file, data) + + assert result["success"] is True + assert result["extracted_count"] > 0 + assert len(data["todos"]) == 8 + for i, todo in enumerate(data["todos"], 1): + assert todo["id"] == f"t{i}" diff --git a/src/aipass/prax/CLOSED_PLANS.local.json b/src/aipass/prax/CLOSED_PLANS.local.json index 88e1417c..48d24f15 100644 --- a/src/aipass/prax/CLOSED_PLANS.local.json +++ b/src/aipass/prax/CLOSED_PLANS.local.json @@ -62,6 +62,13 @@ "subject": "Codex + Gemini log monitoring adapters for prax monitor", "date_closed": "2026-04-10", "location": "prax" + }, + { + "plan_id": "FPLAN-0245", + "type": "FPLAN", + "subject": "STATUS decommission — prax dashboard todo_count + engine dormant", + "date_closed": "2026-06-08", + "location": "prax" } ], "document_metadata": { @@ -71,14 +78,14 @@ "tags": [ "session_tracking", "work_log", - "PRAX" + "prax" ], "limits": { "max_sessions": 20, "max_key_learnings": 25, "session_summary_max_chars": 150, "learning_value_max_chars": 200, - "note": "Auto-rollover to @memory when limits exceeded. Oldest entries trimmed first." + "note": "DO NOT trim, prune, or delete entries. Rollover to @memory handles overflow automatically. Just add new entries." }, "status": { "health": "healthy", diff --git a/src/aipass/prax/README.md b/src/aipass/prax/README.md index 6556a6e7..ed70936d 100644 --- a/src/aipass/prax/README.md +++ b/src/aipass/prax/README.md @@ -13,7 +13,7 @@ Prax is the logging and monitoring backbone of the AIPass ecosystem. Any branch imports `logger` and gets automatic log routing — prax detects the caller via stack introspection and writes to the correct per-module log file. No configuration needed. -On top of logging, prax provides Mission Control (a real-time terminal console for file changes, log events, and agent activity), a log audit system, a dashboard infrastructure, and cross-branch STATUS.md synchronization. +On top of logging, prax provides Mission Control (a real-time terminal console for file changes, log events, and agent activity), a log audit system, and a dashboard infrastructure. ## Quick Start @@ -60,7 +60,7 @@ Interactive commands inside the monitor: `help`, `status`, `quit`/`exit`. ```bash drone @prax status # System health (modules, loggers, watcher state) -drone @prax status sync # Build STATUS.md from all branch STATUS.local.md +drone @prax status sync # DORMANT — STATUS.md sync decommissioned (TDPLAN-0007) drone @prax status --help # Status usage ``` @@ -127,7 +127,7 @@ prax/ │ │ ├── logger.py # SystemLogger — auto-routing, two-tier logging │ │ ├── monitor.py # Mission Control — 3-thread real-time monitoring │ │ ├── dashboard.py # Dashboard — template management, refresh, write-through -│ │ ├── status.py # System status — health display, STATUS.md sync +│ │ ├── status.py # System status — health display (STATUS.md sync dormant) │ │ └── log_audit.py # Log audit — scan, health summary, enforce limits │ └── handlers/ # Implementation details (11 handler directories) │ ├── central/ # Central file reader (.ai_central/*.central.json) @@ -139,7 +139,7 @@ prax/ │ ├── logging/ # Setup, rotation, introspection, override, direct logger │ ├── monitoring/ # Event queue, branch detector, stream output, log watcher │ ├── registry/ # Module registry load/save -│ ├── status/ # STATUS.md sync handler +│ ├── status/ # STATUS.md sync handler (dormant — TDPLAN-0007) │ └── watcher/ # Background system watchers ├── prax_json/ # Auto-created per-module config/data/log files ├── templates/ # Dashboard template schema (DASHBOARD.template.json) @@ -167,7 +167,7 @@ drone @prax monitor run 4. **Mission Control** — Three threads: display worker (pulls from event queue), file watcher (watchdog on branch `apps/` dirs), log watcher (tails `system_logs/*.log`). Falls back to polling when inotify is exhausted. 5. **Multi-CLI monitoring** — Watches Claude Code JSONL and Codex JSONL session files. Extracts agent activity (thinking, tool use, responses) with model detection and branch resolution. 6. **Dashboard** — Template-based per-branch dashboard files. Refreshes from central files (`*.central.json`). Write-through API for services to update sections directly. -7. **STATUS sync** — Scans all branch `STATUS.local.md` files, extracts State/Last update fields, builds aggregated `STATUS.md` at the repo root. +7. **STATUS sync** — *(Dormant — TDPLAN-0007)* Previously scanned all branch `STATUS.local.md` files and built aggregated `STATUS.md`. Engine code intact but no longer triggered. ## Tests @@ -208,7 +208,7 @@ drone @prax monitor run - All branches — Unified logging via `from aipass.prax import logger` - All branches — Real-time monitoring via Mission Control - All branches — Per-branch dashboard files -- System — `STATUS.md` sync, log audit enforcement +- System — Log audit enforcement ## Known Issues - **inotify exhaustion** — System often near `max_user_watches` limit. Monitor uses polling fallback (functional but slower). diff --git a/src/aipass/prax/apps/handlers/dashboard/operations.py b/src/aipass/prax/apps/handlers/dashboard/operations.py index ae553b54..a5102ba2 100644 --- a/src/aipass/prax/apps/handlers/dashboard/operations.py +++ b/src/aipass/prax/apps/handlers/dashboard/operations.py @@ -203,10 +203,12 @@ def _calculate_quick_status_standalone(sections: Dict) -> Dict: """ ai_mail = sections.get("ai_mail", {}) flow = sections.get("flow", {}) + todo = sections.get("todo", {}) new_mail_raw = ai_mail.get("new", ai_mail.get("unread", 0)) opened_raw = ai_mail.get("opened", 0) active_plans_raw = flow.get("active_plans", 0) + todo_count = int(todo.get("todo_count", 0) or 0) new_mail = len(new_mail_raw) if isinstance(new_mail_raw, list) else int(new_mail_raw or 0) opened_mail = len(opened_raw) if isinstance(opened_raw, list) else int(opened_raw or 0) @@ -221,11 +223,14 @@ def _calculate_quick_status_standalone(sections: Dict) -> Dict: parts.append(f"{opened_mail} opened") if active_plans > 0: parts.append(f"{active_plans} active plans") + if todo_count > 0: + parts.append(f"{todo_count} todos") return { "new_mail": new_mail, "opened_mail": opened_mail, "active_plans": active_plans, + "todo_count": todo_count, "action_required": action_required, "summary": ", ".join(parts) if parts else "All clear", } diff --git a/src/aipass/prax/apps/handlers/dashboard/status.py b/src/aipass/prax/apps/handlers/dashboard/status.py index 4597b418..3562572a 100644 --- a/src/aipass/prax/apps/handlers/dashboard/status.py +++ b/src/aipass/prax/apps/handlers/dashboard/status.py @@ -46,10 +46,12 @@ def calculate_quick_status(sections: Dict) -> Dict: """ ai_mail = sections.get("ai_mail", {}) flow = sections.get("flow", {}) + todo = sections.get("todo", {}) new_mail = ai_mail.get("new", ai_mail.get("unread", 0)) opened_mail = ai_mail.get("opened", 0) active_plans = flow.get("active_plans", 0) + todo_count = todo.get("todo_count", 0) action_required = new_mail > 0 or active_plans > 0 @@ -60,11 +62,14 @@ def calculate_quick_status(sections: Dict) -> Dict: summary_parts.append(f"{opened_mail} opened") if active_plans: summary_parts.append(f"{active_plans} active plans") + if todo_count: + summary_parts.append(f"{todo_count} todos") result = { "new_mail": new_mail, "opened_mail": opened_mail, "active_plans": active_plans, + "todo_count": todo_count, "action_required": action_required, "summary": ", ".join(summary_parts) if summary_parts else "All clear", } @@ -75,6 +80,7 @@ def calculate_quick_status(sections: Dict) -> Dict: "action_required": action_required, "new_mail": new_mail, "active_plans": active_plans, + "todo_count": todo_count, }, ) diff --git a/src/aipass/prax/apps/modules/dashboard.py b/src/aipass/prax/apps/modules/dashboard.py index 90e2af1e..cad41ae5 100644 --- a/src/aipass/prax/apps/modules/dashboard.py +++ b/src/aipass/prax/apps/modules/dashboard.py @@ -76,6 +76,7 @@ DASHBOARD_TEMPLATE = { "new_mail": 0, "opened_mail": 0, "active_plans": 0, + "todo_count": 0, "action_required": False, "summary": "", }, diff --git a/src/aipass/prax/apps/modules/monitor.py b/src/aipass/prax/apps/modules/monitor.py index 8cf5acaa..2a5eafd0 100755 --- a/src/aipass/prax/apps/modules/monitor.py +++ b/src/aipass/prax/apps/modules/monitor.py @@ -380,7 +380,7 @@ def _get_watch_directories(repo_root: Path) -> list[tuple[Path, bool]]: apps_dir = branch_path / "apps" if apps_dir.exists(): dirs.append((apps_dir, True)) - # Branch root non-recursive — STATUS.local.md, README.md + # Branch root non-recursive — README.md dirs.append((branch_path, False)) # .trinity/ non-recursive — identity files trinity_dir = branch_path / ".trinity" diff --git a/src/aipass/prax/apps/plugins/devpulse_dashboard/refresh.py b/src/aipass/prax/apps/plugins/devpulse_dashboard/refresh.py index 567bd3d4..fcdc5c0d 100644 --- a/src/aipass/prax/apps/plugins/devpulse_dashboard/refresh.py +++ b/src/aipass/prax/apps/plugins/devpulse_dashboard/refresh.py @@ -41,6 +41,7 @@ def refresh(branch_path: Optional[Path] = None) -> Dict: ("git", _refresh_git), ("session", _refresh_session), ("dispatch", _refresh_dispatch), + ("todo", _refresh_todo), ] for name, builder_fn in builders: @@ -70,3 +71,9 @@ def _refresh_dispatch(branch_path: Path) -> None: from .dispatch_section import build_dispatch_section build_dispatch_section(branch_path) + + +def _refresh_todo(branch_path: Path) -> None: + from .todo_section import build_todo_section + + build_todo_section(branch_path) diff --git a/src/aipass/prax/apps/plugins/devpulse_dashboard/todo_section.py b/src/aipass/prax/apps/plugins/devpulse_dashboard/todo_section.py new file mode 100644 index 00000000..aac9ed38 --- /dev/null +++ b/src/aipass/prax/apps/plugins/devpulse_dashboard/todo_section.py @@ -0,0 +1,61 @@ +# =================== AIPass ==================== +# Name: todo_section.py +# Description: Todo section builder for devpulse dashboard +# Version: 1.0.0 +# Created: 2026-06-07 +# Modified: 2026-06-07 +# ============================================= + +"""Todo section builder for devpulse dashboard plugin. + +Reads todos[] from .trinity/local.json and writes a 'todo' section +(managed_by: devpulse, todo_count, todos) via write_section(). +""" + +import json +from pathlib import Path +from typing import Dict + +from aipass.prax.apps.modules.dashboard import write_section +from aipass.prax.apps.modules.logger import system_logger as logger + + +def build_todo_section(branch_path: Path) -> bool: + """Build todo section data and write to dashboard. + + Args: + branch_path: Path to devpulse branch root. + + Returns: + True if write_section succeeded, False otherwise. + """ + local_json_path = branch_path / ".trinity" / "local.json" + + if not local_json_path.exists(): + section_data: Dict = { + "managed_by": "devpulse", + "todo_count": 0, + "todos": [], + } + return write_section(branch_path, "todo", section_data) + + try: + data = json.loads(local_json_path.read_text()) + except (json.JSONDecodeError, OSError) as exc: + logger.warning("Failed to read local.json at %s: %s", local_json_path, exc) + section_data = { + "managed_by": "devpulse", + "todo_count": 0, + "todos": [], + } + return write_section(branch_path, "todo", section_data) + + todos = data.get("todos", []) + + section_data = { + "managed_by": "devpulse", + "todo_count": len(todos), + "todos": todos, + } + + return write_section(branch_path, "todo", section_data) diff --git a/src/aipass/prax/tests/test_devpulse_dashboard_plugin.py b/src/aipass/prax/tests/test_devpulse_dashboard_plugin.py index d6bfcd9a..cb600ddf 100644 --- a/src/aipass/prax/tests/test_devpulse_dashboard_plugin.py +++ b/src/aipass/prax/tests/test_devpulse_dashboard_plugin.py @@ -219,13 +219,101 @@ class TestDispatchSection: assert dispatch["details"]["seedgo"]["subject"] == "unknown" +class TestTodoSection: + """Tests for todo_section.py.""" + + def test_build_todo_section_with_todos(self, branch_path): + """Test todo section reads todos[] from local.json correctly.""" + from aipass.prax.apps.plugins.devpulse_dashboard.todo_section import build_todo_section + + trinity = branch_path / ".trinity" + trinity.mkdir() + local_data = { + "todos": [ + {"id": "t1", "text": "Fix the bug", "created": "2026-06-07"}, + {"id": "t2", "text": "Write tests", "created": "2026-06-07", "priority": "high"}, + ], + } + (trinity / "local.json").write_text(json.dumps(local_data)) + + result = build_todo_section(branch_path) + assert result is True + + dash = json.loads((branch_path / "DASHBOARD.local.json").read_text()) + todo = dash["sections"]["todo"] + assert todo["managed_by"] == "devpulse" + assert todo["todo_count"] == 2 + assert len(todo["todos"]) == 2 + assert todo["todos"][0]["id"] == "t1" + + def test_build_todo_section_empty_todos(self, branch_path): + """Test todo section with empty todos list.""" + from aipass.prax.apps.plugins.devpulse_dashboard.todo_section import build_todo_section + + trinity = branch_path / ".trinity" + trinity.mkdir() + (trinity / "local.json").write_text(json.dumps({"todos": []})) + + result = build_todo_section(branch_path) + assert result is True + + dash = json.loads((branch_path / "DASHBOARD.local.json").read_text()) + todo = dash["sections"]["todo"] + assert todo["todo_count"] == 0 + assert todo["todos"] == [] + + def test_build_todo_section_no_local_json(self, branch_path): + """Test todo section when local.json doesn't exist.""" + from aipass.prax.apps.plugins.devpulse_dashboard.todo_section import build_todo_section + + result = build_todo_section(branch_path) + assert result is True + + dash = json.loads((branch_path / "DASHBOARD.local.json").read_text()) + todo = dash["sections"]["todo"] + assert todo["todo_count"] == 0 + assert todo["todos"] == [] + + def test_build_todo_section_corrupt_json(self, branch_path): + """Test todo section handles corrupt local.json.""" + from aipass.prax.apps.plugins.devpulse_dashboard.todo_section import build_todo_section + + trinity = branch_path / ".trinity" + trinity.mkdir() + (trinity / "local.json").write_text("not valid json{{{") + + result = build_todo_section(branch_path) + assert result is True + + dash = json.loads((branch_path / "DASHBOARD.local.json").read_text()) + todo = dash["sections"]["todo"] + assert todo["todo_count"] == 0 + + def test_build_todo_section_no_todos_key(self, branch_path): + """Test todo section when local.json has no todos key.""" + from aipass.prax.apps.plugins.devpulse_dashboard.todo_section import build_todo_section + + trinity = branch_path / ".trinity" + trinity.mkdir() + (trinity / "local.json").write_text(json.dumps({"sessions": []})) + + result = build_todo_section(branch_path) + assert result is True + + dash = json.loads((branch_path / "DASHBOARD.local.json").read_text()) + todo = dash["sections"]["todo"] + assert todo["todo_count"] == 0 + assert todo["todos"] == [] + + class TestRefresh: """Tests for refresh.py orchestrator.""" + @patch("aipass.prax.apps.plugins.devpulse_dashboard.todo_section.build_todo_section") @patch("aipass.prax.apps.plugins.devpulse_dashboard.git_section.build_git_section") @patch("aipass.prax.apps.plugins.devpulse_dashboard.session_section.build_session_section") @patch("aipass.prax.apps.plugins.devpulse_dashboard.dispatch_section.build_dispatch_section") - def test_refresh_all_success(self, mock_dispatch, mock_session, mock_git, branch_path): + def test_refresh_all_success(self, mock_dispatch, mock_session, mock_git, mock_todo, branch_path): """Test refresh orchestrator calls all builders.""" from aipass.prax.apps.plugins.devpulse_dashboard.refresh import refresh @@ -233,14 +321,17 @@ class TestRefresh: assert results["git"]["success"] is True assert results["session"]["success"] is True assert results["dispatch"]["success"] is True + assert results["todo"]["success"] is True mock_git.assert_called_once_with(branch_path) mock_session.assert_called_once_with(branch_path) mock_dispatch.assert_called_once_with(branch_path) + mock_todo.assert_called_once_with(branch_path) + @patch("aipass.prax.apps.plugins.devpulse_dashboard.todo_section.build_todo_section") @patch("aipass.prax.apps.plugins.devpulse_dashboard.git_section.build_git_section") @patch("aipass.prax.apps.plugins.devpulse_dashboard.session_section.build_session_section") @patch("aipass.prax.apps.plugins.devpulse_dashboard.dispatch_section.build_dispatch_section") - def test_refresh_partial_failure(self, mock_dispatch, mock_session, mock_git, branch_path): + def test_refresh_partial_failure(self, mock_dispatch, mock_session, mock_git, mock_todo, branch_path): """Test refresh continues when one section fails.""" from aipass.prax.apps.plugins.devpulse_dashboard.refresh import refresh @@ -250,6 +341,7 @@ class TestRefresh: assert "No .git" in results["git"]["error"] assert results["session"]["success"] is True assert results["dispatch"]["success"] is True + assert results["todo"]["success"] is True def test_refresh_default_path(self): """Test refresh uses DEVPULSE_PATH by default.""" diff --git a/src/aipass/seedgo/README.md b/src/aipass/seedgo/README.md index ec789033..763e0547 100644 --- a/src/aipass/seedgo/README.md +++ b/src/aipass/seedgo/README.md @@ -123,8 +123,7 @@ seedgo/ ├── .trinity/ # Identity + memory ├── .seedgo/ # Self-bypass rules ├── .ai_mail.local/ # Mailbox -├── CLAUDE.md # Branch startup instructions -└── STATUS.local.md # Current state summary +└── CLAUDE.md # Branch startup instructions ``` ### Key Patterns diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/readme_check.py b/src/aipass/seedgo/apps/handlers/aipass_standards/readme_check.py index 2e75dca7..204a9d1b 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/readme_check.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/readme_check.py @@ -47,7 +47,6 @@ _RUNTIME_ARTIFACTS = { "system_logs", "docs.local", ".trinity", - "STATUS.local.md", "DASHBOARD.local.json", } diff --git a/src/aipass/seedgo/tests/test_readme_content_checks.py b/src/aipass/seedgo/tests/test_readme_content_checks.py index c794be7a..16e73068 100644 --- a/src/aipass/seedgo/tests/test_readme_content_checks.py +++ b/src/aipass/seedgo/tests/test_readme_content_checks.py @@ -486,7 +486,7 @@ def test_is_runtime_artifact_known_dirs(): assert _is_runtime_artifact(Path("/any/path/.trinity")) is True assert _is_runtime_artifact(Path("/any/path/cli_json")) is True assert _is_runtime_artifact(Path("/any/path/seedgo_json")) is True - assert _is_runtime_artifact(Path("/any/path/STATUS.local.md")) is True + assert _is_runtime_artifact(Path("/any/path/STATUS.local.md")) is False assert _is_runtime_artifact(Path("/any/path/DASHBOARD.local.json")) is True assert _is_runtime_artifact(Path("/any/path/docs.local")) is True assert _is_runtime_artifact(Path("/any/path/dropbox")) is True diff --git a/src/aipass/spawn/.aipass/aipass_local_prompt.md b/src/aipass/spawn/.aipass/aipass_local_prompt.md index a61d2519..881d8e7b 100644 --- a/src/aipass/spawn/.aipass/aipass_local_prompt.md +++ b/src/aipass/spawn/.aipass/aipass_local_prompt.md @@ -1,6 +1,6 @@ # SPAWN — Branch Prompt -*Injected every turn. Breadcrumbs only — details: README, --help, .trinity/ memories, STATUS.local.md.* +*Injected every turn. Breadcrumbs only — details: README, --help, .trinity/ memories.* ## Identity diff --git a/src/aipass/spawn/templates/birthright/.aipass/aipass_local_prompt.md b/src/aipass/spawn/templates/birthright/.aipass/aipass_local_prompt.md index f5b66624..416f419b 100644 --- a/src/aipass/spawn/templates/birthright/.aipass/aipass_local_prompt.md +++ b/src/aipass/spawn/templates/birthright/.aipass/aipass_local_prompt.md @@ -17,7 +17,7 @@ PRINCIPLES: - Include concrete reference data — lookup tables, checklists, decision trees. - If you can run `drone @{{BRANCH}} --help` to get it, don't duplicate it here. - This file should be STABLE. If it changes every session, that content belongs in - STATUS.local.md or .trinity/ instead. + .trinity/ instead. WHEN YOU'RE DONE: - Delete this instruction block @@ -25,7 +25,7 @@ WHEN YOU'RE DONE: - What remains should be pure operational reference --> -*Injected every turn. Breadcrumbs only — details in README, --help, .trinity/ memories, STATUS.local.md.* +*Injected every turn. Breadcrumbs only — details in README, --help, .trinity/ memories.* ## Identity diff --git a/src/aipass/spawn/templates/builder/.aipass/aipass_local_prompt.md b/src/aipass/spawn/templates/builder/.aipass/aipass_local_prompt.md index f5b66624..416f419b 100644 --- a/src/aipass/spawn/templates/builder/.aipass/aipass_local_prompt.md +++ b/src/aipass/spawn/templates/builder/.aipass/aipass_local_prompt.md @@ -17,7 +17,7 @@ PRINCIPLES: - Include concrete reference data — lookup tables, checklists, decision trees. - If you can run `drone @{{BRANCH}} --help` to get it, don't duplicate it here. - This file should be STABLE. If it changes every session, that content belongs in - STATUS.local.md or .trinity/ instead. + .trinity/ instead. WHEN YOU'RE DONE: - Delete this instruction block @@ -25,7 +25,7 @@ WHEN YOU'RE DONE: - What remains should be pure operational reference --> -*Injected every turn. Breadcrumbs only — details in README, --help, .trinity/ memories, STATUS.local.md.* +*Injected every turn. Breadcrumbs only — details in README, --help, .trinity/ memories.* ## Identity diff --git a/src/aipass/spawn/templates/builder/.spawn/.template_registry.json b/src/aipass/spawn/templates/builder/.spawn/.template_registry.json index a55ff68b..437c195a 100644 --- a/src/aipass/spawn/templates/builder/.spawn/.template_registry.json +++ b/src/aipass/spawn/templates/builder/.spawn/.template_registry.json @@ -26,7 +26,7 @@ "f004": { "path": ".aipass/aipass_local_prompt.md", "name": "aipass_local_prompt.md", - "content_hash": "5f8e2a3859a9", + "content_hash": "bf82b35fa7d5", "has_branch_placeholder": false }, "f005": { @@ -113,12 +113,6 @@ "content_hash": "ad99517a50f9", "has_branch_placeholder": false }, - "f018": { - "path": "STATUS.local.md", - "name": "STATUS.local.md", - "content_hash": "059295ad4d4e", - "has_branch_placeholder": false - }, "f019": { "path": "apps/README.md", "name": "README.md", @@ -155,7 +149,7 @@ "content_hash": "a4cf0a8e3b4f", "has_branch_placeholder": false }, - "f015": { + "f018": { "path": "apps/modules/__init__.py", "name": "__init__.py", "content_hash": "e3b0c44298fc", @@ -269,7 +263,7 @@ "content_hash": "28e9ae373563", "has_branch_placeholder": false }, - "f026": { + "f015": { "path": "apps/plugins/__init__.py", "name": "__init__.py", "content_hash": "e3b0c44298fc", diff --git a/src/aipass/spawn/templates/builder/STATUS.local.md b/src/aipass/spawn/templates/builder/STATUS.local.md deleted file mode 100644 index 8c268422..00000000 --- a/src/aipass/spawn/templates/builder/STATUS.local.md +++ /dev/null @@ -1,15 +0,0 @@ -# @{{BRANCHNAME}} - -> {{BRANCHNAME}} branch - -**State:** Not Started -**Last update:** None - -## Milestones -- - -## Current Work -- - -## Known Issues -- diff --git a/src/aipass/trigger/.seedgo/bypass.json b/src/aipass/trigger/.seedgo/bypass.json index 82b4bf7f..da712886 100644 --- a/src/aipass/trigger/.seedgo/bypass.json +++ b/src/aipass/trigger/.seedgo/bypass.json @@ -375,6 +375,12 @@ "pattern": "handle_pr", "reason": "PR event handlers (handle_pr_created, handle_pr_merged) are event handlers registered in registry.py, not trigger-specific naming violations." }, + { + "file": "apps/handlers/events/pr_status_sync.py", + "standard": "unused_function", + "pattern": "handle_pr_created, handle_pr_merged", + "reason": "TDPLAN-0007: status-sync decommissioned. Handlers unregistered in registry.py but module + tests kept intact for future revival." + }, { "file": "apps/handlers/log_watcher.py", "standard": "silent_catch", diff --git a/src/aipass/trigger/README.md b/src/aipass/trigger/README.md index b6c81678..4172394a 100644 --- a/src/aipass/trigger/README.md +++ b/src/aipass/trigger/README.md @@ -74,7 +74,7 @@ result = report_error( ## Events -15 events registered via `handlers/events/registry.py` on first `Trigger.fire()`. All fire through the event bus. +15 events defined, 13 active (2 decommissioned by TDPLAN-0007). Registered via `handlers/events/registry.py` on first `Trigger.fire()`. All fire through the event bus. | Event | Handler | Trigger | Action | |-------|---------|---------|--------| @@ -90,8 +90,8 @@ result = report_error( | `memory_template_updated` | `memory_template_updated.py` | Memory template changed | Pushes template updates to branches | | `memory_saved` | `memory.py` | Memory file written | Placeholder for future rollover trigger | | `cli_header_displayed` | `cli.py` | CLI displays headers | Registration hook | -| `pr_created` | `pr_status_sync.py` | PR opened on GitHub | Runs `drone @prax status sync` (fire-and-forget) | -| `pr_merged` | `pr_status_sync.py` | PR merged on GitHub | Runs `drone @prax status sync` (fire-and-forget) | +| `pr_created` | `pr_status_sync.py` | PR opened on GitHub | ~~Runs `drone @prax status sync`~~ **Decommissioned** (TDPLAN-0007) | +| `pr_merged` | `pr_status_sync.py` | PR merged on GitHub | ~~Runs `drone @prax status sync`~~ **Decommissioned** (TDPLAN-0007) | | `memory_pool_auto_processed` | `memory_pool.py` | Hook engine runs `auto_process()` | Logs result; on failure fires `error_detected` for Medic dispatch | ## Medic @@ -148,7 +148,7 @@ trigger/ │ ├── json/ │ │ └── json_handler.py # JSON structure logging │ ├── events/ -│ │ ├── registry.py # Auto-registers all 15 event handlers +│ │ ├── registry.py # Auto-registers 13 active event handlers │ │ ├── startup.py # Startup catch-up scan │ │ ├── error_detected.py # 8-gate Medic dispatch │ │ ├── error_logged.py # Monitor-only (no dispatch) @@ -159,7 +159,7 @@ trigger/ │ │ ├── memory_template_updated.py │ │ ├── memory.py # memory_saved placeholder │ │ ├── cli.py # cli_header_displayed hook -│ │ ├── pr_status_sync.py # PR → prax status sync +│ │ ├── pr_status_sync.py # PR → prax status sync (decommissioned TDPLAN-0007) │ │ └── memory_pool.py # Pool auto-process observability │ └── watchers/ │ └── log_watcher.py # System log watcher (system_logs/ dir) diff --git a/src/aipass/trigger/apps/handlers/events/registry.py b/src/aipass/trigger/apps/handlers/events/registry.py index 875e906f..d035b06b 100644 --- a/src/aipass/trigger/apps/handlers/events/registry.py +++ b/src/aipass/trigger/apps/handlers/events/registry.py @@ -63,7 +63,8 @@ def setup_handlers(): from .warning_logged import handle_warning_logged from .bulletin_created import handle_bulletin_created from .memory_template_updated import handle_memory_template_updated - from .pr_status_sync import handle_pr_created, handle_pr_merged + + # from .pr_status_sync import handle_pr_created, handle_pr_merged # TDPLAN-0007: status-sync decommissioned from .memory_pool import handle_memory_pool_auto_processed trigger.on("startup", handle_startup) @@ -75,8 +76,8 @@ def setup_handlers(): trigger.on("warning_logged", handle_warning_logged) trigger.on("bulletin_created", handle_bulletin_created) trigger.on("memory_template_updated", handle_memory_template_updated) - trigger.on("pr_created", handle_pr_created) - trigger.on("pr_merged", handle_pr_merged) + # trigger.on("pr_created", handle_pr_created) # TDPLAN-0007: status-sync decommissioned + # trigger.on("pr_merged", handle_pr_merged) # TDPLAN-0007: status-sync decommissioned trigger.on("memory_pool_auto_processed", handle_memory_pool_auto_processed) json_handler.log_operation("handlers_registered", {"success": True})