From 61a81662542ddc0a734a98b2f682920db9813b94 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Wed, 15 Apr 2026 15:16:49 -0700 Subject: [PATCH 1/3] =?UTF-8?q?feat(drone):=20add=20watchdog=20to=20INTERA?= =?UTF-8?q?CTIVE=5FCOMMANDS=20=E2=80=94=20bypass=2030s=20capture=20timeout?= =?UTF-8?q?=20for=20long-running=20poller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: @drone --- src/aipass/drone/apps/drone.py | 2 +- src/aipass/drone/tests/test_activation.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/aipass/drone/apps/drone.py b/src/aipass/drone/apps/drone.py index 2ff9ad36..35da5a32 100644 --- a/src/aipass/drone/apps/drone.py +++ b/src/aipass/drone/apps/drone.py @@ -41,7 +41,7 @@ MODULES_DIR = Path(__file__).parent / "modules" # Interactive mode — commands/branches that bypass capture + timeout for live terminal output. -INTERACTIVE_COMMANDS = ("monitor", "audit") +INTERACTIVE_COMMANDS = ("monitor", "audit", "watchdog") INTERACTIVE_BRANCHES = ("cli",) diff --git a/src/aipass/drone/tests/test_activation.py b/src/aipass/drone/tests/test_activation.py index 7e12f0b8..2fd64895 100644 --- a/src/aipass/drone/tests/test_activation.py +++ b/src/aipass/drone/tests/test_activation.py @@ -407,6 +407,20 @@ def test_interactive_detection_for_branch(self, mock_route: MagicMock) -> None: call_kwargs = mock_route.call_args.kwargs assert call_kwargs["interactive"] is True + @patch("aipass.drone.apps.drone.route_command") + def test_watchdog_routes_interactive(self, mock_route: MagicMock) -> None: + """watchdog command should route with interactive=True (long-running poller).""" + from aipass.drone.apps.drone import _handle_target + + mock_route.return_value = CommandResult( + stdout="", stderr="", exit_code=0, branch="devpulse", command="watchdog", + ) + + _handle_target(["@devpulse", "watchdog", "--help"]) + + call_kwargs = mock_route.call_args.kwargs + assert call_kwargs["interactive"] is True + @patch("aipass.drone.apps.drone.route_command") def test_propagates_exit_code(self, mock_route: MagicMock) -> None: """Should return the route_command exit code.""" From e6d4998c8d0d0193b8c5db509880ef2d697b0b22 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Wed, 15 Apr 2026 15:19:32 -0700 Subject: [PATCH 2/3] feat(drone): fix merge_plugin: stash unstaged changes before git pull --rebase to prevent dirty-tree abort Co-Authored-By: @drone --- .../apps/plugins/devpulse_ops/merge_plugin.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/aipass/drone/apps/plugins/devpulse_ops/merge_plugin.py b/src/aipass/drone/apps/plugins/devpulse_ops/merge_plugin.py index 8ac6ce68..02a47920 100644 --- a/src/aipass/drone/apps/plugins/devpulse_ops/merge_plugin.py +++ b/src/aipass/drone/apps/plugins/devpulse_ops/merge_plugin.py @@ -65,16 +65,39 @@ def merge_pr(pr_number: str, caller: str) -> dict: logger.error(result["message"]) return result - # Step 2: Sync local main + # Step 2: Sync local main — stash any unstaged changes first so + # git pull --rebase doesn't abort on a dirty working tree. + stash = subprocess.run( + ["git", "stash"], + capture_output=True, text=True, cwd=str(repo_root), + ) + stashed = "No local changes to save" not in stash.stdout + pull = subprocess.run( ["git", "pull", "--rebase"], capture_output=True, text=True, cwd=str(repo_root), ) if pull.returncode != 0: + if stashed: + subprocess.run( + ["git", "stash", "pop"], + capture_output=True, text=True, cwd=str(repo_root), + ) result["message"] = f"Pull after merge failed: {pull.stderr.strip()}" logger.error(result["message"]) return result + if stashed: + pop = subprocess.run( + ["git", "stash", "pop"], + capture_output=True, text=True, cwd=str(repo_root), + ) + if pop.returncode != 0: + logger.warning( + "merge_pr: stash pop after pull failed (manual restore may be needed): %s", + pop.stderr.strip(), + ) + # Step 3: Get the merge commit hash rev = subprocess.run( ["git", "rev-parse", "HEAD"], From b29dc085371e591b9b8d9963f666d760995212f2 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Wed, 15 Apr 2026 15:28:16 -0700 Subject: [PATCH 3/3] =?UTF-8?q?feat(system):=20DPLAN-0133=20Phase=201=20(p?= =?UTF-8?q?art=201/2):=20add=20gitignore=20rule=20for=20apps/integrations/?= =?UTF-8?q?**=20with=20README=20negation,=20plus=20scaffold=20README.md=20?= =?UTF-8?q?in=20all=2010=20core=20branches'=20apps/integrations/=20?= =?UTF-8?q?=E2=80=94=20private=20integration=20space=20is=20now=20leak-pro?= =?UTF-8?q?of=20by=20construction.=20Drivers=20and=20wrappers=20dropped=20?= =?UTF-8?q?here=20stay=20local;=20only=20README.md=20is=20tracked.=20See?= =?UTF-8?q?=20DPLAN-0133=20for=20architecture=20rationale=20(three-layer?= =?UTF-8?q?=20design:=20@api=20drivers,=20per-branch=20wrappers,=20public?= =?UTF-8?q?=20generic=20contracts).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: @devpulse --- .gitignore | 7 ++ STATUS.md | 2 +- .../ai_mail/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/api/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/cli/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/drone/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/flow/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/memory/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/prax/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/seedgo/apps/integrations/README.md | 64 +++++++++++++++++++ src/aipass/spawn/apps/integrations/README.md | 64 +++++++++++++++++++ .../trigger/apps/integrations/README.md | 64 +++++++++++++++++++ 12 files changed, 648 insertions(+), 1 deletion(-) create mode 100644 src/aipass/ai_mail/apps/integrations/README.md create mode 100644 src/aipass/api/apps/integrations/README.md create mode 100644 src/aipass/cli/apps/integrations/README.md create mode 100644 src/aipass/drone/apps/integrations/README.md create mode 100644 src/aipass/flow/apps/integrations/README.md create mode 100644 src/aipass/memory/apps/integrations/README.md create mode 100644 src/aipass/prax/apps/integrations/README.md create mode 100644 src/aipass/seedgo/apps/integrations/README.md create mode 100644 src/aipass/spawn/apps/integrations/README.md create mode 100644 src/aipass/trigger/apps/integrations/README.md diff --git a/.gitignore b/.gitignore index 343010f5..605784a3 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,10 @@ whiteboard.md README_ORIGINAL_DISABLED.md readme_history/ src/aipass/trigger/trigger_data.lock + +# Private integrations — driver layer (@api) and wrapper layer (all branches) +# Per DPLAN-0133. Contents are gitignored; only the scaffold README.md is tracked. +# Drop project-specific code into src/aipass/{branch}/apps/integrations/{project}/ +# It stays local. Never appears in git. +src/aipass/*/apps/integrations/** +!src/aipass/*/apps/integrations/README.md diff --git a/STATUS.md b/STATUS.md index 7392081d..727bf1ae 100644 --- a/STATUS.md +++ b/STATUS.md @@ -300,7 +300,7 @@ ### Friction notes -- **seedgo __init__.py false positive**: `imports: Failed` and `naming: invalid characters` fire on Python reserved filename. Need special-case in seedgo's naming standard and imports standard. Encountered in another project; likely also in AIPass repo. Open a seedgo standard PR when there's bandwidth. +(none active — seedgo __init__.py false positive FIXED by @seedgo in PR #279, 2026-04-14) ### S92 (2026-04-14 ~12:45 PT) — PRE-COMPACT, system prompt reformat work in flight diff --git a/src/aipass/ai_mail/apps/integrations/README.md b/src/aipass/ai_mail/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/ai_mail/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/api/apps/integrations/README.md b/src/aipass/api/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/api/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/cli/apps/integrations/README.md b/src/aipass/cli/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/cli/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/drone/apps/integrations/README.md b/src/aipass/drone/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/drone/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/flow/apps/integrations/README.md b/src/aipass/flow/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/flow/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/memory/apps/integrations/README.md b/src/aipass/memory/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/memory/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/prax/apps/integrations/README.md b/src/aipass/prax/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/prax/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/seedgo/apps/integrations/README.md b/src/aipass/seedgo/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/seedgo/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/spawn/apps/integrations/README.md b/src/aipass/spawn/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/spawn/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale. diff --git a/src/aipass/trigger/apps/integrations/README.md b/src/aipass/trigger/apps/integrations/README.md new file mode 100644 index 00000000..0fab10ce --- /dev/null +++ b/src/aipass/trigger/apps/integrations/README.md @@ -0,0 +1,64 @@ +# apps/integrations/ + +Private integration space for this branch. + +**This folder is gitignored.** Only this README is tracked. Everything else you drop in here stays local and never appears in git, PRs, or the public repo. Safe by construction, not by discipline. + +## What goes here + +**Branch-specific wrappers** that consume external systems via the @api driver layer. Each wrapper handles how THIS branch uses an external system in its own domain. + +``` +apps/integrations/ +└── {project}/ + ├── wrapper.py # How this branch uses the driver + ├── config.json # Optional — local config + └── tests/ # Private tests colocated +``` + +Wrappers should call into `@api`'s generic contracts (e.g. `api.memory_backend.query(...)`), never reference the private project by name in any tracked code. The private project name lives in the @api driver, not here. + +## What does NOT go here + +- **Driver code** — that belongs in `@api/apps/integrations/{project}/driver.py` (the connection layer). +- **Public business logic** — use `apps/modules/` or `apps/handlers/` for that. +- **Drone plugins** — use `apps/plugins/` for those. +- **Secrets** — they live in `~/.secrets/aipass/`, never in the repo. + +## Architecture + +The full design is in DPLAN-0133 (private integrations architecture). Three layers: + +1. **@api driver layer** (`@api/apps/integrations/{project}/`) — owns the physical connection, auth, transport. Knows the private project name. +2. **Per-branch wrapper layer** (`{this_folder}/{project}/`) — owns how this branch consumes the driver's output in its domain. Calls generic contracts, never names private projects. +3. **Public drone commands** (`drone @api integrations list`, `drone @api integrations call `) — advertise the extension points without naming specifics. Fork-safe. + +## Usage + +```python +# Your public code (committed, in apps/modules/ or apps/handlers/) +from aipass.api import memory_backend + +results = memory_backend.query("when did we ship watchdog?") +# memory_backend is a generic contract. In your local setup it routes to whatever +# driver you registered in @api/apps/integrations/. In a fresh clone with nothing +# registered, it returns NotConfigured gracefully. +``` + +```python +# Your private wrapper (in this folder, gitignored) +# apps/integrations/someproject/wrapper.py + +from aipass.api import memory_backend + +def domain_specific_query(context): + """Branch-specific query pattern for domain needs.""" + hint = build_query_from_context(context) + return memory_backend.query(hint, top_k=5, filter={"kind": "decision"}) +``` + +The wrapper stays here, the call into the contract stays here, no private name leaks into tracked code. + +--- + +See DPLAN-0133 for the full design rationale.