Skip to content
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
64 changes: 64 additions & 0 deletions src/aipass/ai_mail/apps/integrations/README.md
Original file line number Diff line number Diff line change
@@ -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 <contract>`) — 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.
64 changes: 64 additions & 0 deletions src/aipass/api/apps/integrations/README.md
Original file line number Diff line number Diff line change
@@ -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 <contract>`) — 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.
64 changes: 64 additions & 0 deletions src/aipass/cli/apps/integrations/README.md
Original file line number Diff line number Diff line change
@@ -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 <contract>`) — 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.
64 changes: 64 additions & 0 deletions src/aipass/drone/apps/integrations/README.md
Original file line number Diff line number Diff line change
@@ -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 <contract>`) — 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.
25 changes: 24 additions & 1 deletion src/aipass/drone/apps/plugins/devpulse_ops/merge_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
64 changes: 64 additions & 0 deletions src/aipass/flow/apps/integrations/README.md
Original file line number Diff line number Diff line change
@@ -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 <contract>`) — 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.
Loading
Loading