# Services Architecture

This file documents the local service shape on `ballbox-first`, with emphasis on terminal persistence for `pi` and tmux.

## Goal

Keep `pi` usable as an interactive TUI while making tmux only a persistence layer.

Desired properties:

- `pi` runs inside tmux.
- Closing a terminal client does not kill `pi`.
- tmux does not become the primary UI.
- Mouse scroll works for history without moving the `pi` cursor.
- Color rendering stays correct inside tmux.

## Current tmux model

### tmux config

`~/.tmux.conf` is intentionally persistence-first:

- `mouse on`
- `history-limit 100000`
- `set-clipboard on`
- custom prefix: `C-\\`
- status bar off
- dangerous interactive defaults unbound
- copy mode uses vi keys
- wheel scroll enters/uses copy mode
- explicit detach/reload bindings only

### Important tmux bindings

- Prefix: `Ctrl-\\`
- Detach: `Ctrl-\\` then `D`
- Reload config: `Ctrl-\\` then `r`
- Scroll/history:
  - wheel up enters copy mode or scrolls within it
  - wheel down scrolls or exits copy mode

### Why tmux stays minimal

tmux is not used for pane/window management here. It is only the durable PTY container for the running `pi` process.

That means:

- no pane splitting
- no tab/session UI workflow
- no mouse-driven tmux navigation
- no session picker dependency for normal `pi` use

## `pitmux`

`~/.bashrc` defines `pitmux()`.

Purpose:

- start `pi` inside a tmux session
- keep the session attachable later
- keep color output correct inside tmux

### Current launch behavior

`pitmux` forces these env values for the `pi` process:

- `TERM=tmux-256color`
- `COLORTERM=truecolor`
- `TERM_PROGRAM=`

This avoids the brown background / palette mismatch that appeared when `pi` ran inside tmux with the host terminal's `TERM_PROGRAM` leaking through.

### Usage

```bash
pitmux
pitmux pi-1
```

If no name is passed, `pitmux` creates a timestamped session name like:

```text
pi-20260421-165900
```

### Launch shape

`pitmux` runs:

```bash
env TERM=tmux-256color COLORTERM=truecolor TERM_PROGRAM= tmux new-session -A -s <session> pi
```

## `pi` resume helper

`~/.bashrc` also defines `piresume()`.

That wrapper is separate from tmux.

Purpose:

- run `pi` normally
- after exit, print the latest session id hint
- help resume with:

```bash
pi --session <id>
```

## Native detached batch jobs for subagents

For simple one-prompt subagent runs, prefer detached shell job wrappers instead of extension-based side-agent orchestration.

Desired shape:

- exact existing `cwd` is chosen by caller
- one initial prompt only
- `pi` runs non-interactively (`pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p` by default) in a detached background process
- output is written to a durable log file
- parent orchestrator can poll state, tail logs, wait, or stop the process
- no cron, no background daemon, no extra service layer
- no tmux dependency for one-shot jobs

### Runtime layout

Use `/mnt/rpi/pi-jobs` as durable runtime storage.

Per job:

```text
/mnt/rpi/pi-jobs/jobs/<job-id>/
  meta.env
  prompt.txt
  output.log
  status
  pid
  exit_code
  started_at
  finished_at
```

### Process model

- `pi-job-start` creates the job directory and launches a detached runner with `nohup`.
- the runner executes `pi -p` in the requested directory with explicit provider/model defaults to avoid accidental fallback to unauthenticated providers.
- the runner writes status files and appends full stdout/stderr to `output.log`.
- `pi-job-check` reconciles file status with `pid` liveness and prints recent log lines.
- `pi-job-wait` is a simple poll loop over status files plus `pid` liveness.
- `pi-job-stop` sends `TERM`/`KILL` to the recorded `pid` and marks the job as stopped.

### Why this path

This keeps the batch layer native and inspectable:

- detached process for lifetime
- shell scripts for control
- plain files for logs and metadata

Use tmux only for interactive persistent `pi` sessions (`pitmux`, Telegram service), not one-shot batch jobs.

Default batch overrides:
- `PI_PROVIDER` defaults to `openai-codex`
- `PI_MODEL` defaults to `gpt-5.4-mini`
- `PI_THINKING` defaults to `low`

No interactive key injection workflow is assumed beyond the initial prompt.

## Dedicated Telegram `pi` service

For an always-reachable Telegram agent, run one dedicated `pi` instance under a user systemd service and keep tmux as the PTY container.

Desired shape:

- one fixed tmux session: `pi-telegram`
- one fixed working directory: `/home/sebas`
- one fixed config dir: `/home/sebas/pi-config`
- service restarts on boot via user systemd
- service loop recreates the tmux session if `pi` exits
- Telegram ownership stays on that one instance; use `/new` there when you want a fresh conversation

### Runtime pieces

- launcher loop: `/home/sebas/pi-config/bin/pi-telegram-service`
- user service: `~/.config/systemd/user/pi-telegram.service`
- tmux session name: `pi-telegram`

### Behavior

- first-time setup still needs one manual `/telegram-connect` inside that dedicated `pi` session
- after that, normal conversation and `/new` stay inside the same long-lived instance
- after reboot, the service relaunches the same tmux-backed `pi` process from the same `cwd`, which matches the Telegram bridge auto-resume model

## Wi‑Fi tracker service

A lightweight Wi‑Fi/LAN presence tracker now runs on this machine and publishes a small dashboard through nginx.

Scope of this service:

- collect device presence visible from this Pi on the local LAN
- keep an append-only JSONL activity log
- maintain per-device aliases set from the web UI
- publish a derived summary for dashboard rendering
- expose a tiny local API for alias CRUD

Non-goals:

- this is not exact Wi‑Fi association telemetry from the router/AP
- no packet payload capture
- no DPI
- no traffic-content logging

### Current model

This Pi is not the AP/router controller for the home network.
So the tracker uses LAN presence observation:

- network sweep over the local subnet
- neighbor table (`ip neigh`) snapshot
- reverse DNS lookup when available
- router HTML probe for basic vendor/model identification

This yields:

- IP
- MAC
- hostname sometimes
- first seen / last seen
- online/offline transitions inferred from snapshots

It does not yield:

- exact connect/disconnect at Wi‑Fi layer
- SSID/BSSID
- RSSI
- AP roaming details

### Runtime pieces

Collector and derived data:

[115 more lines in file. Use offset=241 to continue.]


docs/agents-database-memory-contract.md
docs/wifi-tracker.md


**Updating documentation**

I think I need to add some documentation and update the README, maybe even the architecture section. It feels important to include a comprehensive list of changes or features. I'll want to make sure everything is clear for anyone who reads it later. This way, they can easily understand the updates I’ve made. It’s a good practice to keep everything organized and accessible! Let's get started on that!

write

# pi-loop

`pi-loop` is the built-in helper for running the same task prompt through multiple sequential `pi` sessions.

Use it when one pass is not enough and you want later runs to reinforce, continue, verify, or refine the work from earlier runs.

## What it does

For each iteration, `pi-loop`:

- runs `pi` non-interactively in the same working tree
- prepends loop context with iteration number
- injects tail output from the previous iteration into the next prompt
- keeps going for the requested count even if the task looks mostly done
- writes per-iteration prompts, logs, status, and timestamps to a durable run directory

Default execution:

```bash
pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p ...
```

## When to use it

Use `pi-loop` when you want:

- multiple passes over the same coding task
- a simple "keep pushing this forward" loop
- cheap reinforcement without manual reprompting
- sequential refinement in the same repo state

Examples:

- implement feature, then let later passes fix gaps
- review and tighten docs after initial draft
- keep testing/refining after main code already landed

## When to use `pi-job-*` instead

Use `pi-job-*` when you want:

- one detached batch run
- explicit status/check/wait/stop lifecycle
- orchestration around one prompt, not repeated passes
- a long-running job you inspect later

Short rule:

- multiple sequential passes on same task -> `pi-loop`
- one detached inspectable batch run -> `pi-job-*`

## Usage

```bash
bin/pi-loop --count 5 --cwd /path/repo --prompt "Implement feature X"
```

```bash
bin/pi-loop --count 10 --cwd /path/repo --prompt-file ./task.txt
```

## Safe defaults

Defaults:

- provider: `openai-codex`
- model: `gpt-5.4-mini`
- thinking: `low`
- extensions: off

Override if needed:

```bash
bin/pi-loop \
  --count 3 \
  --cwd /path/repo \
  --provider openai-codex \
  --model gpt-5.4 \
  --thinking low \
  --prompt "Do the task"
```

## Output layout

Runs are stored under:

```text
/mnt/rpi/pi-loop-runs/<timestamp>/
```

Per iteration:

```text
iter-001/
  prompt.txt
  output.log
  status.txt
  started_at.txt
  finished_at.txt
  exit_code.txt
```

Top-level run files:

```text
base-prompt.txt
run-meta.txt
```

## Example workflow

```bash
bin/pi-loop \
  --count 3 \
  --cwd /tmp/test-repo \
  --prompt "Create or update one small file and improve it each pass"
```

Then inspect:

- repo changes in `--cwd`
- logs in `/mnt/rpi/pi-loop-runs/...`

## Notes

- No early-stop condition by design.
- Later iterations use current repo state as source of truth.
- `--keep-going` continues after failed iterations.
- For unattended use, prefer explicit provider/model defaults over implicit resolution.


Successfully wrote 2734 bytes to /home/sebas/pi-config/docs/pi-loop.md


edit

## Included
- `AGENTS.md`
- `MIGRATION.md`
- `settings.json`
- `extensions/subagent/`
- `extensions/questionnaire.ts`
- `extensions/session-memory-prompt.ts`
- `scripts/apply-pi-shutdown-hook-patch.py`
- `scripts/session-memory-analyzer.py`
- `scripts/session-memory-dispatcher.py`
- `scripts/install-session-memory-dispatcher.service`
- `bin/tmp-cleanup` / `/tmp` cleanup tool
- `scripts/install-tmp-cleanup-cron.sh` cron installer for /tmp cleanup
- `bin/laptop-*` remote laptop helper scripts
- `bin/laptop-write` atomic remote file writer for laptop files

## Included
- `AGENTS.md`
- `MIGRATION.md`
- `settings.json`
- `extensions/subagent/`
- `extensions/questionnaire.ts`
- `extensions/session-memory-prompt.ts`
- `scripts/apply-pi-shutdown-hook-patch.py`
- `scripts/session-memory-analyzer.py`
- `scripts/session-memory-dispatcher.py`
- `scripts/install-session-memory-dispatcher.service`
- `bin/pi-loop` repeated multi-session task runner
- `docs/pi-loop.md` usage guide for repeated multi-session loops
- `bin/tmp-cleanup` / `/tmp` cleanup tool
- `scripts/install-tmp-cleanup-cron.sh` cron installer for /tmp cleanup
- `bin/laptop-*` remote laptop helper scripts
- `bin/laptop-write` atomic remote file writer for laptop files

## Batch `pi` helpers

Unattended helpers should avoid implicit model/provider resolution.

- `bin/pi-job-runner` now forces `--provider openai-codex --model gpt-5.4-mini --thinking low` by default.
- Override per process with env vars: `PI_PROVIDER`, `PI_MODEL`, `PI_THINKING`.
- `bin/pi-loop` now defaults to `--provider openai-codex --model gpt-5.4-mini --thinking low` and still lets you override via flags.

Examples:
- `bin/pi-loop --count 3 --cwd /tmp/test --prompt "Touch one file"`
- `PI_MODEL=gpt-5.4 bin/pi-job-start --cwd /path/repo --prompt "Do the task"`

## Batch `pi` helpers

Two first-class helpers:

- `bin/pi-loop`: run the same task through multiple sequential `pi` sessions so later passes can reinforce and refine earlier work.
- `bin/pi-job-*`: run one detached inspectable batch job with status/check/wait/stop flow.

Decision rule:

- multiple passes on the same prompt -> `pi-loop`
- one detached batch run -> `pi-job-*`

Unattended helpers should avoid implicit model/provider resolution.

- `bin/pi-loop` defaults to `--provider openai-codex --model gpt-5.4-mini --thinking low` and still lets you override via flags.
- `bin/pi-job-runner` forces `--provider openai-codex --model gpt-5.4-mini --thinking low` by default.
- `pi-job-*` overrides per process with env vars: `PI_PROVIDER`, `PI_MODEL`, `PI_THINKING`.

Start here:
- `docs/pi-loop.md`

Examples:
- `bin/pi-loop --count 3 --cwd /tmp/test --prompt "Touch one file"`
- `PI_MODEL=gpt-5.4 bin/pi-job-start --cwd /path/repo --prompt "Do the task"`


Successfully replaced 2 block(s) in /home/sebas/pi-config/README.md.

     ...
  12 - `scripts/apply-pi-shutdown-hook-patch.py`
  13 - `scripts/session-memory-analyzer.py`
  14 - `scripts/session-memory-dispatcher.py`
  15 - `scripts/install-session-memory-dispatcher.service`
+ 16 - `bin/pi-loop` repeated multi-session task runner
+ 17 - `docs/pi-loop.md` usage guide for repeated multi-session loops
  16 - `bin/tmp-cleanup` / `/tmp` cleanup tool
  17 - `scripts/install-tmp-cleanup-cron.sh` cron installer for /tmp cleanup
  18 - `bin/laptop-*` remote laptop helper scripts
  19 - `bin/laptop-write` atomic remote file writer for laptop files
     ...
  67 - `bin/laptop-mount /Users/sebas/Downloads downloads` then work at `~/mnt/laptop/downloads`
  68
  69 ## Batch `pi` helpers
  70
+ 73 Two first-class helpers:
+ 74
+ 75 - `bin/pi-loop`: run the same task through multiple sequential `pi` sessions so later passes can reinforce and refine earlier work.
+ 76 - `bin/pi-job-*`: run one detached inspectable batch job with status/check/wait/stop flow.
+ 77
+ 78 Decision rule:
+ 79
+ 80 - multiple passes on the same prompt -> `pi-loop`
+ 81 - one detached batch run -> `pi-job-*`
+ 82
  71 Unattended helpers should avoid implicit model/provider resolution.
  72
- 73 - `bin/pi-job-runner` now forces `--provider openai-codex --model gpt-5.4-mini --thinking low` by default.
- 74 - Override per process with env vars: `PI_PROVIDER`, `PI_MODEL`, `PI_THINKING`.
- 75 - `bin/pi-loop` now defaults to `--provider openai-codex --model gpt-5.4-mini --thinking low` and still lets you override via flags.
+ 85 - `bin/pi-loop` defaults to `--provider openai-codex --model gpt-5.4-mini --thinking low` and still lets you override via flags.
+ 86 - `bin/pi-job-runner` forces `--provider openai-codex --model gpt-5.4-mini --thinking low` by default.
+ 87 - `pi-job-*` overrides per process with env vars: `PI_PROVIDER`, `PI_MODEL`, `PI_THINKING`.
  76
+ 89 Start here:
+ 90 - `docs/pi-loop.md`
+ 91
  77 Examples:
  78 - `bin/pi-loop --count 3 --cwd /tmp/test --prompt "Touch one file"`
  79 - `PI_MODEL=gpt-5.4 bin/pi-job-start --cwd /path/repo --prompt "Do the task"`
  80
     ...


## Native detached batch jobs for subagents

For simple one-prompt subagent runs, prefer detached shell job wrappers instead of extension-based side-agent orchestration.

## Native detached batch jobs and loops for subagents

For simple non-interactive `pi` automation, use native shell helpers instead of extension-based side-agent orchestration.

Tool split:

- `pi-loop`: multiple sequential passes over the same task prompt in the same working tree
- `pi-job-*`: one detached inspectable batch run with explicit lifecycle commands

Choose `pi-loop` when you want later sessions to reinforce or refine earlier work.
Choose `pi-job-*` when you want one background run with explicit `check`, `wait`, and `stop` controls.

Desired shape:

- exact existing `cwd` is chosen by caller
- one initial prompt only
- `pi` runs non-interactively (`pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p` by default) in a detached background process
- output is written to a durable log file
- parent orchestrator can poll state, tail logs, wait, or stop the process
- no cron, no background daemon, no extra service layer
- no tmux dependency for one-shot jobs

Shared shape:

- exact existing `cwd` is chosen by caller
- non-interactive `pi` execution with explicit provider/model defaults
- durable logs and artifacts on disk
- no cron, no background daemon, no extra service layer

### `pi-loop`

Use `pi-loop` for repeated passes on one task.

Behavior:

- caller sets iteration count and base prompt
- each iteration prepends loop context including current iteration number
- each iteration can include tail output from the previous iteration
- each iteration runs in the same working tree so repo state carries forward
- run artifacts are stored under `/mnt/rpi/pi-loop-runs/<timestamp>/`

See: `docs/pi-loop.md`

### `pi-job-*`

Use `pi-job-*` for one detached inspectable batch run.

Desired shape:

- one initial prompt only
- `pi` runs non-interactively (`pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p` by default) in a detached background process
- output is written to a durable log file
- parent orchestrator can poll state, tail logs, wait, or stop the process
- no tmux dependency for one-shot jobs


Successfully replaced 2 block(s) in /home/sebas/pi-config/SERVICES-ARCHITECTURE.md.

     ...
 106 ```bash
 107 pi --session <id>
 108 ```
 109
-110 ## Native detached batch jobs for subagents
+110 ## Native detached batch jobs and loops for subagents
 111
-112 For simple one-prompt subagent runs, prefer detached shell job wrappers instead of extension-based side-agent orchestration.
+112 For simple non-interactive `pi` automation, use native shell helpers instead of extension-based side-agent orchestration.
 113
-114 Desired shape:
+114 Tool split:
 115
+116 - `pi-loop`: multiple sequential passes over the same task prompt in the same working tree
+117 - `pi-job-*`: one detached inspectable batch run with explicit lifecycle commands
+118
+119 Choose `pi-loop` when you want later sessions to reinforce or refine earlier work.
+120 Choose `pi-job-*` when you want one background run with explicit `check`, `wait`, and `stop` controls.
+121
+122 Shared shape:
+123
 116 - exact existing `cwd` is chosen by caller
+125 - non-interactive `pi` execution with explicit provider/model defaults
+126 - durable logs and artifacts on disk
+127 - no cron, no background daemon, no extra service layer
+128
+129 ### `pi-loop`
+130
+131 Use `pi-loop` for repeated passes on one task.
+132
+133 Behavior:
+134
+135 - caller sets iteration count and base prompt
+136 - each iteration prepends loop context including current iteration number
+137 - each iteration can include tail output from the previous iteration
+138 - each iteration runs in the same working tree so repo state carries forward
+139 - run artifacts are stored under `/mnt/rpi/pi-loop-runs/<timestamp>/`
+140
+141 See: `docs/pi-loop.md`
+142
+143 ### `pi-job-*`
+144
+145 Use `pi-job-*` for one detached inspectable batch run.
+146
+147 Desired shape:
+148
 117 - one initial prompt only
 118 - `pi` runs non-interactively (`pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p` by default) in a detached background process
 119 - output is written to a durable log file
 120 - parent orchestrator can poll state, tail logs, wait, or stop the process
-121 - no cron, no background daemon, no extra service layer
 122 - no tmux dependency for one-shot jobs
 123
 124 ### Runtime layout
 125
     ...


bash

cd /home/sebas/pi-config && sed -n '1,220p' README.md && printf '\n===== DOC =====\n' && sed -n '1,260p' docs/pi-loop.md && printf '\n===== ARCH =====\n' && sed -n '70,180p' SERVICES-ARCHITECTURE.md

cd /home/sebas/pi-config && git diff -- README.md SERVICES-ARCHITECTURE.md docs/pi-loop.md | sed -n '1,260p'


# pi-config

Personal Pi configuration for **sebasfavaron**.

## Included
- `AGENTS.md`
- `MIGRATION.md`
- `settings.json`
- `extensions/subagent/`
- `extensions/questionnaire.ts`
- `extensions/session-memory-prompt.ts`
- `scripts/apply-pi-shutdown-hook-patch.py`
- `scripts/session-memory-analyzer.py`
- `scripts/session-memory-dispatcher.py`
- `scripts/install-session-memory-dispatcher.service`
- `bin/pi-loop` repeated multi-session task runner
- `docs/pi-loop.md` usage guide for repeated multi-session loops
- `bin/tmp-cleanup` / `/tmp` cleanup tool
- `scripts/install-tmp-cleanup-cron.sh` cron installer for /tmp cleanup
- `bin/laptop-*` remote laptop helper scripts
- `bin/laptop-write` atomic remote file writer for laptop files

## Goal

Make this repo both the source of truth **and the live Pi config directory**.

## Notes

- Direct, compressed, low-noise communication is the default style.
- Session close hook now prompts once before switch/fork/exit to encourage saving durable memory into agents-database.
- Session memory now uses an external local dispatcher on `127.0.0.1:17882`; the hook only captures session identity/context, the dispatcher enqueues metadata, and a background analyzer decides whether anything durable is worth saving.
- The canonical memory pipeline lives in `agents-database` (`MemoryService` / `MemoryEngine`). Repo-local helpers should only capture session context and hand off to that service.
- The analyzer uses `pi` in background with a cheap model (`gpt-5.4-mini`) and thinking off when possible.
- For unattended `pi` scripts and jobs, prefer explicit provider/model selection. Current safe default on this machine: `--provider openai-codex --model gpt-5.4-mini`.
- Use `./scripts/install-session-memory-dispatcher.service` to keep the dispatcher alive under systemd user service if desired.
- The old `session_before_shutdown` runtime patch remains as fallback only if you choose to keep it enabled.
- Reapply the runtime patch after Pi upgrades only if you still use that fallback: `./scripts/apply-pi-shutdown-hook-patch.py`
- **Pending validation:** confirm the default communication mode auto-loads in every new Pi session.
- Top-level `/tmp` files older than 3 days are cleaned every 3 days via `bin/tmp-cleanup`.
- Install the cleanup cron with `scripts/install-tmp-cleanup-cron.sh`.
- Remote laptop workflow can be driven from this Pi using `bin/laptop-run`, `bin/laptop-read`, `bin/laptop-write`, `bin/laptop-rsync`, `bin/laptop-mount`, and `bin/laptop-umount` against host `sebastians-macbook-pro-2`.

## Local server routes on `ballbox-first`

Current nginx portal on `:80` exposes:
- `/` → portal home from `/var/www/portal`
- `/pi-job-status.json` → generated status file with active tmux `pi-job-` session count
- `/portal/` → proxied app on `127.0.0.1:18789`
- `/calistenia/` → static files from `/home/sebas/clawd/web/calistenia/`
- `/files/` → copyparty file server on `127.0.0.1:3923`
- `/mp3-share/` → static directory listing from `/home/sebas/downloads/mp3-share/`

Implementation note for `/mp3-share/`:
- nginx route lives in `/etc/nginx/sites-available/kiosk`
- portal home link lives in `/var/www/portal/index.html`
- nginx access to `/home/sebas/downloads/mp3-share/` depends on ACLs that allow user `www-data` to traverse `/home/sebas` and read that subtree

## Remote laptop helpers

Default host is `sebastians-macbook-pro-2`. Override with `LAPTOP_HOST` if needed.

Examples:
- `bin/laptop-run 'uname -a'`
- `bin/laptop-read /Users/sebas/.zshrc | sed -n '1,40p'`
- `bin/laptop-rsync sebastians-macbook-pro-2:/Users/sebas/Downloads/file ./`
- `bin/laptop-rsync ./localfile sebastians-macbook-pro-2:/Users/sebas/Downloads/`
- `printf 'new contents\n' | bin/laptop-write /Users/sebas/Downloads/test.txt`
- `bin/laptop-write --from ./localfile /Users/sebas/Downloads/test.txt`
- `bin/laptop-mount /Users/sebas/Downloads downloads` then work at `~/mnt/laptop/downloads`

## Batch `pi` helpers

Two first-class helpers:

- `bin/pi-loop`: run the same task through multiple sequential `pi` sessions so later passes can reinforce and refine earlier work.
- `bin/pi-job-*`: run one detached inspectable batch job with status/check/wait/stop flow.

Decision rule:

- multiple passes on the same prompt -> `pi-loop`
- one detached batch run -> `pi-job-*`

Unattended helpers should avoid implicit model/provider resolution.

- `bin/pi-loop` defaults to `--provider openai-codex --model gpt-5.4-mini --thinking low` and still lets you override via flags.
- `bin/pi-job-runner` forces `--provider openai-codex --model gpt-5.4-mini --thinking low` by default.
- `pi-job-*` overrides per process with env vars: `PI_PROVIDER`, `PI_MODEL`, `PI_THINKING`.

Start here:
- `docs/pi-loop.md`

Examples:
- `bin/pi-loop --count 3 --cwd /tmp/test --prompt "Touch one file"`
- `PI_MODEL=gpt-5.4 bin/pi-job-start --cwd /path/repo --prompt "Do the task"`

## /tmp cleanup

Top-level `/tmp` files older than 3 days are cleaned every 3 days.

- `bin/tmp-cleanup [--dry-run]`
- `scripts/install-tmp-cleanup-cron.sh`

===== DOC =====
# pi-loop

`pi-loop` is the built-in helper for running the same task prompt through multiple sequential `pi` sessions.

Use it when one pass is not enough and you want later runs to reinforce, continue, verify, or refine the work from earlier runs.

## What it does

For each iteration, `pi-loop`:

- runs `pi` non-interactively in the same working tree
- prepends loop context with iteration number
- injects tail output from the previous iteration into the next prompt
- keeps going for the requested count even if the task looks mostly done
- writes per-iteration prompts, logs, status, and timestamps to a durable run directory

Default execution:

```bash
pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p ...
```

## When to use it

Use `pi-loop` when you want:

- multiple passes over the same coding task
- a simple "keep pushing this forward" loop
- cheap reinforcement without manual reprompting
- sequential refinement in the same repo state

Examples:

- implement feature, then let later passes fix gaps
- review and tighten docs after initial draft
- keep testing/refining after main code already landed

## When to use `pi-job-*` instead

Use `pi-job-*` when you want:

- one detached batch run
- explicit status/check/wait/stop lifecycle
- orchestration around one prompt, not repeated passes
- a long-running job you inspect later

Short rule:

- multiple sequential passes on same task -> `pi-loop`
- one detached inspectable batch run -> `pi-job-*`

## Usage

```bash
bin/pi-loop --count 5 --cwd /path/repo --prompt "Implement feature X"
```

```bash
bin/pi-loop --count 10 --cwd /path/repo --prompt-file ./task.txt
```

## Safe defaults

Defaults:

- provider: `openai-codex`
- model: `gpt-5.4-mini`
- thinking: `low`
- extensions: off

Override if needed:

```bash
bin/pi-loop \
  --count 3 \
  --cwd /path/repo \
  --provider openai-codex \
  --model gpt-5.4 \
  --thinking low \
  --prompt "Do the task"
```

## Output layout

Runs are stored under:

```text
/mnt/rpi/pi-loop-runs/<timestamp>/
```

Per iteration:

```text
iter-001/
  prompt.txt
  output.log
  status.txt
  started_at.txt
  finished_at.txt
  exit_code.txt
```

Top-level run files:

```text
base-prompt.txt
run-meta.txt
```

## Example workflow

```bash
bin/pi-loop \
  --count 3 \
  --cwd /tmp/test-repo \
  --prompt "Create or update one small file and improve it each pass"
```

Then inspect:

- repo changes in `--cwd`
- logs in `/mnt/rpi/pi-loop-runs/...`

## Notes

- No early-stop condition by design.
- Later iterations use current repo state as source of truth.
- `--keep-going` continues after failed iterations.
- For unattended use, prefer explicit provider/model defaults over implicit resolution.

===== ARCH =====

This avoids the brown background / palette mismatch that appeared when `pi` ran inside tmux with the host terminal's `TERM_PROGRAM` leaking through.

### Usage

```bash
pitmux
pitmux pi-1
```

If no name is passed, `pitmux` creates a timestamped session name like:

```text
pi-20260421-165900
```

### Launch shape

`pitmux` runs:

```bash
env TERM=tmux-256color COLORTERM=truecolor TERM_PROGRAM= tmux new-session -A -s <session> pi
```

## `pi` resume helper

`~/.bashrc` also defines `piresume()`.

That wrapper is separate from tmux.

Purpose:

- run `pi` normally
- after exit, print the latest session id hint
- help resume with:

```bash
pi --session <id>
```

## Native detached batch jobs and loops for subagents

For simple non-interactive `pi` automation, use native shell helpers instead of extension-based side-agent orchestration.

Tool split:

- `pi-loop`: multiple sequential passes over the same task prompt in the same working tree
- `pi-job-*`: one detached inspectable batch run with explicit lifecycle commands

Choose `pi-loop` when you want later sessions to reinforce or refine earlier work.
Choose `pi-job-*` when you want one background run with explicit `check`, `wait`, and `stop` controls.

Shared shape:

- exact existing `cwd` is chosen by caller
- non-interactive `pi` execution with explicit provider/model defaults
- durable logs and artifacts on disk
- no cron, no background daemon, no extra service layer

### `pi-loop`

Use `pi-loop` for repeated passes on one task.

Behavior:

- caller sets iteration count and base prompt
- each iteration prepends loop context including current iteration number
- each iteration can include tail output from the previous iteration
- each iteration runs in the same working tree so repo state carries forward
- run artifacts are stored under `/mnt/rpi/pi-loop-runs/<timestamp>/`

See: `docs/pi-loop.md`

### `pi-job-*`

Use `pi-job-*` for one detached inspectable batch run.

Desired shape:

- one initial prompt only
- `pi` runs non-interactively (`pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p` by default) in a detached background process
- output is written to a durable log file
- parent orchestrator can poll state, tail logs, wait, or stop the process
- no tmux dependency for one-shot jobs

### Runtime layout

Use `/mnt/rpi/pi-jobs` as durable runtime storage.

Per job:

```text
/mnt/rpi/pi-jobs/jobs/<job-id>/
  meta.env
  prompt.txt
  output.log
  status
  pid
  exit_code
  started_at
  finished_at
```

### Process model

- `pi-job-start` creates the job directory and launches a detached runner with `nohup`.
- the runner executes `pi -p` in the requested directory with explicit provider/model defaults to avoid accidental fallback to unauthenticated providers.
- the runner writes status files and appends full stdout/stderr to `output.log`.
- `pi-job-check` reconciles file status with `pid` liveness and prints recent log lines.
- `pi-job-wait` is a simple poll loop over status files plus `pid` liveness.
- `pi-job-stop` sends `TERM`/`KILL` to the recorded `pid` and marks the job as stopped.


diff --git a/README.md b/README.md
index 89a9445..0138452 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ Personal Pi configuration for **sebasfavaron**.
 - `scripts/session-memory-analyzer.py`
 - `scripts/session-memory-dispatcher.py`
 - `scripts/install-session-memory-dispatcher.service`
+- `bin/pi-loop` repeated multi-session task runner
+- `docs/pi-loop.md` usage guide for repeated multi-session loops
 - `bin/tmp-cleanup` / `/tmp` cleanup tool
 - `scripts/install-tmp-cleanup-cron.sh` cron installer for /tmp cleanup
 - `bin/laptop-*` remote laptop helper scripts
@@ -29,6 +31,7 @@ Make this repo both the source of truth **and the live Pi config directory**.
 - Session memory now uses an external local dispatcher on `127.0.0.1:17882`; the hook only captures session identity/context, the dispatcher enqueues metadata, and a background analyzer decides whether anything durable is worth saving.
 - The canonical memory pipeline lives in `agents-database` (`MemoryService` / `MemoryEngine`). Repo-local helpers should only capture session context and hand off to that service.
 - The analyzer uses `pi` in background with a cheap model (`gpt-5.4-mini`) and thinking off when possible.
+- For unattended `pi` scripts and jobs, prefer explicit provider/model selection. Current safe default on this machine: `--provider openai-codex --model gpt-5.4-mini`.
 - Use `./scripts/install-session-memory-dispatcher.service` to keep the dispatcher alive under systemd user service if desired.
 - The old `session_before_shutdown` runtime patch remains as fallback only if you choose to keep it enabled.
 - Reapply the runtime patch after Pi upgrades only if you still use that fallback: `./scripts/apply-pi-shutdown-hook-patch.py`
@@ -41,6 +44,7 @@ Make this repo both the source of truth **and the live Pi config directory**.

 Current nginx portal on `:80` exposes:
 - `/` → portal home from `/var/www/portal`
+- `/pi-job-status.json` → generated status file with active tmux `pi-job-` session count
 - `/portal/` → proxied app on `127.0.0.1:18789`
 - `/calistenia/` → static files from `/home/sebas/clawd/web/calistenia/`
 - `/files/` → copyparty file server on `127.0.0.1:3923`
@@ -64,6 +68,31 @@ Examples:
 - `bin/laptop-write --from ./localfile /Users/sebas/Downloads/test.txt`
 - `bin/laptop-mount /Users/sebas/Downloads downloads` then work at `~/mnt/laptop/downloads`

+## Batch `pi` helpers
+
+Two first-class helpers:
+
+- `bin/pi-loop`: run the same task through multiple sequential `pi` sessions so later passes can reinforce and refine earlier work.
+- `bin/pi-job-*`: run one detached inspectable batch job with status/check/wait/stop flow.
+
+Decision rule:
+
+- multiple passes on the same prompt -> `pi-loop`
+- one detached batch run -> `pi-job-*`
+
+Unattended helpers should avoid implicit model/provider resolution.
+
+- `bin/pi-loop` defaults to `--provider openai-codex --model gpt-5.4-mini --thinking low` and still lets you override via flags.
+- `bin/pi-job-runner` forces `--provider openai-codex --model gpt-5.4-mini --thinking low` by default.
+- `pi-job-*` overrides per process with env vars: `PI_PROVIDER`, `PI_MODEL`, `PI_THINKING`.
+
+Start here:
+- `docs/pi-loop.md`
+
+Examples:
+- `bin/pi-loop --count 3 --cwd /tmp/test --prompt "Touch one file"`
+- `PI_MODEL=gpt-5.4 bin/pi-job-start --cwd /path/repo --prompt "Do the task"`
+
 ## /tmp cleanup

 Top-level `/tmp` files older than 3 days are cleaned every 3 days.
diff --git a/SERVICES-ARCHITECTURE.md b/SERVICES-ARCHITECTURE.md
index de24278..69882c0 100644
--- a/SERVICES-ARCHITECTURE.md
+++ b/SERVICES-ARCHITECTURE.md
@@ -107,6 +107,245 @@ Purpose:
 pi --session <id>
 ```

+## Native detached batch jobs and loops for subagents
+
+For simple non-interactive `pi` automation, use native shell helpers instead of extension-based side-agent orchestration.
+
+Tool split:
+
+- `pi-loop`: multiple sequential passes over the same task prompt in the same working tree
+- `pi-job-*`: one detached inspectable batch run with explicit lifecycle commands
+
+Choose `pi-loop` when you want later sessions to reinforce or refine earlier work.
+Choose `pi-job-*` when you want one background run with explicit `check`, `wait`, and `stop` controls.
+
+Shared shape:
+
+- exact existing `cwd` is chosen by caller
+- non-interactive `pi` execution with explicit provider/model defaults
+- durable logs and artifacts on disk
+- no cron, no background daemon, no extra service layer
+
+### `pi-loop`
+
+Use `pi-loop` for repeated passes on one task.
+
+Behavior:
+
+- caller sets iteration count and base prompt
+- each iteration prepends loop context including current iteration number
+- each iteration can include tail output from the previous iteration
+- each iteration runs in the same working tree so repo state carries forward
+- run artifacts are stored under `/mnt/rpi/pi-loop-runs/<timestamp>/`
+
+See: `docs/pi-loop.md`
+
+### `pi-job-*`
+
+Use `pi-job-*` for one detached inspectable batch run.
+
+Desired shape:
+
+- one initial prompt only
+- `pi` runs non-interactively (`pi --provider openai-codex --model gpt-5.4-mini --thinking low --no-extensions -p` by default) in a detached background process
+- output is written to a durable log file
+- parent orchestrator can poll state, tail logs, wait, or stop the process
+- no tmux dependency for one-shot jobs
+
+### Runtime layout
+
+Use `/mnt/rpi/pi-jobs` as durable runtime storage.
+
+Per job:
+
+```text
+/mnt/rpi/pi-jobs/jobs/<job-id>/
+  meta.env
+  prompt.txt
+  output.log
+  status
+  pid
+  exit_code
+  started_at
+  finished_at
+```
+
+### Process model
+
+- `pi-job-start` creates the job directory and launches a detached runner with `nohup`.
+- the runner executes `pi -p` in the requested directory with explicit provider/model defaults to avoid accidental fallback to unauthenticated providers.
+- the runner writes status files and appends full stdout/stderr to `output.log`.
+- `pi-job-check` reconciles file status with `pid` liveness and prints recent log lines.
+- `pi-job-wait` is a simple poll loop over status files plus `pid` liveness.
+- `pi-job-stop` sends `TERM`/`KILL` to the recorded `pid` and marks the job as stopped.
+
+### Why this path
+
+This keeps the batch layer native and inspectable:
+
+- detached process for lifetime
+- shell scripts for control
+- plain files for logs and metadata
+
+Use tmux only for interactive persistent `pi` sessions (`pitmux`, Telegram service), not one-shot batch jobs.
+
+Default batch overrides:
+- `PI_PROVIDER` defaults to `openai-codex`
+- `PI_MODEL` defaults to `gpt-5.4-mini`
+- `PI_THINKING` defaults to `low`
+
+No interactive key injection workflow is assumed beyond the initial prompt.
+
+## Dedicated Telegram `pi` service
+
+For an always-reachable Telegram agent, run one dedicated `pi` instance under a user systemd service and keep tmux as the PTY container.
+
+Desired shape:
+
+- one fixed tmux session: `pi-telegram`
+- one fixed working directory: `/home/sebas`
+- one fixed config dir: `/home/sebas/pi-config`
+- service restarts on boot via user systemd
+- service loop recreates the tmux session if `pi` exits
+- Telegram ownership stays on that one instance; use `/new` there when you want a fresh conversation
+
+### Runtime pieces
+
+- launcher loop: `/home/sebas/pi-config/bin/pi-telegram-service`
+- user service: `~/.config/systemd/user/pi-telegram.service`
+- tmux session name: `pi-telegram`
+
+### Behavior
+
+- first-time setup still needs one manual `/telegram-connect` inside that dedicated `pi` session
+- after that, normal conversation and `/new` stay inside the same long-lived instance
+- after reboot, the service relaunches the same tmux-backed `pi` process from the same `cwd`, which matches the Telegram bridge auto-resume model
+
+## Wi‑Fi tracker service
+
+A lightweight Wi‑Fi/LAN presence tracker now runs on this machine and publishes a small dashboard through nginx.
+
+Scope of this service:
+
+- collect device presence visible from this Pi on the local LAN
+- keep an append-only JSONL activity log
+- maintain per-device aliases set from the web UI
+- publish a derived summary for dashboard rendering
+- expose a tiny local API for alias CRUD
+
+Non-goals:
+
+- this is not exact Wi‑Fi association telemetry from the router/AP
+- no packet payload capture
+- no DPI
+- no traffic-content logging
+
+### Current model
+
+This Pi is not the AP/router controller for the home network.
+So the tracker uses LAN presence observation:
+
+- network sweep over the local subnet
+- neighbor table (`ip neigh`) snapshot
+- reverse DNS lookup when available
+- router HTML probe for basic vendor/model identification
+
+This yields:
+
+- IP
+- MAC
+- hostname sometimes
+- first seen / last seen
+- online/offline transitions inferred from snapshots
+
+It does not yield:
+
+- exact connect/disconnect at Wi‑Fi layer
+- SSID/BSSID
+- RSSI
+- AP roaming details
+
+### Runtime pieces
+
+Collector and derived data:
+
+- script: `/home/sebas/pi-config/bin/wifi-presence-log`
+- raw log: `/mnt/rpi/agent-logs/wifi-presence/activity.jsonl`
+- state: `/mnt/rpi/agent-logs/wifi-presence/state.json`
+- aliases: `/mnt/rpi/agent-logs/wifi-presence/aliases.json`
+- fingerprints/evidence: `/mnt/rpi/agent-logs/wifi-presence/fingerprints.json`
+- alert prefs: `/mnt/rpi/agent-logs/wifi-presence/alert-prefs.json`
+- dashboard summary: `/var/www/wifi-tracker/summary.json`
+- service note: `/home/sebas/pi-config/docs/wifi-tracker.md`
+
+Scheduling:
+
+- service: `~/.config/systemd/user/wifi-presence-log.service`
+- timer: `~/.config/systemd/user/wifi-presence-log.timer`
+- cadence: every 2 minutes
+
+Dashboard/API:
+
+- static UI: `/var/www/wifi-tracker/index.html`
+- API server: `/home/sebas/pi-config/bin/wifi-tracker-api.py`
+- API service: `~/.config/systemd/user/wifi-tracker-api.service`
+- health endpoint: `/wifi-tracker/api/health`
+- alert prefs endpoint: `/wifi-tracker/api/alert-prefs`
+- nginx route: `/wifi-tracker/`
+- nginx API route: `/wifi-tracker/api/`
+- portal link added on `/`
+
+### Data flow
+
+1. `wifi-presence-log.timer` triggers the collector.


**Checking for duplicates**

I noticed a huge duplication due to the file possibly containing the same sections. I need to check for duplication, as my edits may have inserted content before existing sections. It seems like there's a possibility that my architecture is duplicated too, considering the preview shows repeated sections later in the document. I'll inspect that area further, possibly using grep to look for headings to clarify where the duplicates are occurring.

cd /home/sebas/pi-config && rg -n "Native detached batch jobs|Dedicated Telegram|Wi‑Fi tracker" SERVICES-ARCHITECTURE.md && wc -l SERVICES-ARCHITECTURE.md


110:## Native detached batch jobs and loops for subagents
199:## Dedicated Telegram `pi` service
224:## Wi‑Fi tracker service
385 SERVICES-ARCHITECTURE.md


**Evaluating line counts**

I’m thinking that I need to compare against a base, and I'm wondering if a difference qualifies as large. I want to make sure I'm not duplicating anything in my calculations. Checking the line count, I’m estimating it might be around 385. This seems okay for what I’m trying to accomplish. It’s reassuring to mark the todo as completed, which feels like a nice little victory!


Updated #3 (pending → completed)

Diagnose pi CLI invocation/auth issue

Test pi in minimal ways to find which invocation works on this machine and why current loop/jobs fail with azure-openai-responses auth error.

Harden pi scripts to force openai-codex and update docs

Patch pi-loop and pi-job-runner to avoid ambiguous provider resolution. Update existing docs to document explicit provider/model usage for reliable unattended jobs.

Promote pi-loop as first-class tool in docs

Make pi-loop visible in main docs, architecture, and add dedicated usage doc with comparison against pi-job tools.


Sí. Ya quedó mucho más visible.

## Dónde lo dejé

### 1. README principal
- `~/pi-config/README.md`

Ahora:
- aparece en `Included`
- tiene sección propia dentro de `Batch pi helpers`
- compara directo:
  - `pi-loop` vs `pi-job-*`
- da regla de decisión clara

### 2. Doc dedicada
- `~/pi-config/docs/pi-loop.md`

Ahí quedó:
- qué hace
- cuándo usarlo
- cuándo usar `pi-job-*` en cambio
- defaults
- ejemplos
- layout de outputs

### 3. Arquitectura
- `~/pi-config/SERVICES-ARCHITECTURE.md`

Ahora:
- `pi-loop` ya figura como herramienta de primer nivel
- está puesto junto a `pi-job-*`
- queda explícito el split conceptual

## Resultado práctico

Ahora si alguien mira docs principales, debería ver:
- `pi-loop` existe
- para qué sirve
- cuándo proponerlo
- cómo se diferencia de `pi-job-*`

Si querés, siguiente mejora fuerte:
- agregar alias más “mentalmente recordable”
  - por ejemplo `pi-iterate`
  - manteniendo `pi-loop` como nombre real o alias inverso

Eso lo haría todavía más proponible en conversación.