refactor(status): decommission entire STATUS flow — STATUS.local.md + central STATUS.md retired, replaced by local.json todos[] + dashboard count (TDPLAN-0007)
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) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f4b203a74e
commit
626ab81a46
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -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.**
|
||||
|
||||
@@ -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 @<self>` — where `<self>` 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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <plan_id>
|
||||
□ EMAIL SENDER → drone @ai_mail email @<sender> "Subject" "Summary"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
" <agent_name>/ # 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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]:
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ DASHBOARD_TEMPLATE = {
|
||||
"new_mail": 0,
|
||||
"opened_mail": 0,
|
||||
"active_plans": 0,
|
||||
"todo_count": 0,
|
||||
"action_required": False,
|
||||
"summary": "",
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -47,7 +47,6 @@ _RUNTIME_ARTIFACTS = {
|
||||
"system_logs",
|
||||
"docs.local",
|
||||
".trinity",
|
||||
"STATUS.local.md",
|
||||
"DASHBOARD.local.json",
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# @{{BRANCHNAME}}
|
||||
|
||||
> {{BRANCHNAME}} branch
|
||||
|
||||
**State:** Not Started
|
||||
**Last update:** None
|
||||
|
||||
## Milestones
|
||||
-
|
||||
|
||||
## Current Work
|
||||
-
|
||||
|
||||
## Known Issues
|
||||
-
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user