Add capture mode: /capture + per-room capture threads → cross-project inbox
A capture-thread message (or /capture <text> in any room) logs to standards/ INBOX.md via capture-note.sh — deterministic, no claude call — and confirms in-thread; /triage stays the gate into each repo (D13). Thread roots seeded by seed-capture-threads.py.
This commit is contained in:
@@ -23,8 +23,10 @@ Matrix message in a project room
|
||||
```
|
||||
|
||||
Room determines the repo; the message text becomes the initial prompt — the v1 trigger surface.
|
||||
*Variant:* a `?`-prefixed message instead runs `ask-claude.sh` (headless `claude -p`) and posts
|
||||
the full answer back into the room (ask mode, D12).
|
||||
*Variants:* a `?`-prefixed message instead runs `ask-claude.sh` (headless `claude -p`) and posts
|
||||
the full answer back into the room (ask mode, D12). A message in a room's **capture thread** (or a
|
||||
`/capture <text>` message in any room) is logged to the cross-project inbox instead of launching —
|
||||
deterministic, no Claude call (capture mode, D13).
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -61,6 +63,10 @@ the full answer back into the room (ask mode, D12).
|
||||
- **Bot — venv (dev/fallback):** `python3 -m venv .venv && .venv/bin/pip install -r requirements.txt`,
|
||||
then `.venv/bin/python src/bot.py` — uses modelo's host `~/.ssh/config` for the alias.
|
||||
`MB_SSH_ALIAS` overrides the SSH target for testing.
|
||||
- **Seed capture threads:** `python3 scripts/seed-capture-threads.py` (reads `.env` + `config.toml`,
|
||||
needs only Python stdlib; run anywhere the homeserver is reachable). Posts each room's capture-thread
|
||||
root and prints the `capture_thread` event IDs to paste into `config.toml`. Skips rooms already set;
|
||||
pass labels or `--force` to reseed, `--dry-run` to preview.
|
||||
- **Deploy:** the Spark's `~/matrix-bridge` is a Gitea clone tracking `master`, so deploy =
|
||||
`git fetch origin && git reset --hard origin/master && docker compose up -d --build` (run as
|
||||
`modelo` from `~/matrix-bridge`). You normally don't run this by hand — the **Update** button on
|
||||
@@ -86,9 +92,17 @@ the full answer back into the room (ask mode, D12).
|
||||
- `scripts/ask-claude.sh` — headless `?`-ask wrapper (`#!/bin/zsh -l`): runs `claude -p` in the repo
|
||||
and prints the answer to stdout for the bot to capture and post back. Uses `CLAUDE_CODE_OAUTH_TOKEN`
|
||||
(Mac-side `.env`) because a non-GUI SSH session can't reach the login Keychain (D12).
|
||||
- `scripts/capture-note.sh` — capture wrapper (`#!/bin/zsh -l`): appends one `/capture`-format line
|
||||
to `~/Projects/standards/INBOX.md`, commits, best-effort pushes, and echoes the line back.
|
||||
Deterministic — no `claude`, no token, no frontier call (D13).
|
||||
- `scripts/seed-capture-threads.py` — one-time (re-runnable) helper that posts each room's
|
||||
capture-thread root message and prints the resulting `capture_thread` event IDs to paste into
|
||||
`config.toml`. Skips rooms already configured; run after adding a project.
|
||||
- `src/bot.py` — the matrix-nio bot (Phase 1): listens in mapped rooms; a plain message runs
|
||||
`ssh mac-bridge gui-launch.sh` (interactive, to the phone), a `?`-prefixed message runs
|
||||
`ask-claude.sh` (headless, answer posted back); fans out for all-projects; reports failures back.
|
||||
`ask-claude.sh` (headless, answer posted back), and a `/capture`/capture-thread message runs
|
||||
`capture-note.sh` (logs to the inbox, confirms in-thread); fans out for all-projects; reports
|
||||
failures back.
|
||||
- `requirements.txt` (matrix-nio) · `.env.example` (credential schema; real `.env` gitignored).
|
||||
- `.claude/` — Claude wiring (dir only for now).
|
||||
- `Dockerfile` · `docker-compose.yml` · `docker-entrypoint.sh` · `.dockerignore` — the Phase 1
|
||||
@@ -148,6 +162,18 @@ Condensed from the scoping workshop. Each: the call, why, what it beat.
|
||||
`claude setup-token` (`CLAUDE_CODE_OAUTH_TOKEN`) that D11 deferred — kept **Mac-side only** (in
|
||||
`.env`; the Spark never runs claude). Interactive launches keep the token-free GUI-Terminal path.
|
||||
*Sovereignty unchanged:* `claude -p` uses the subscription, no frontier API touches message payloads.
|
||||
- **D13 — Capture mode → central inbox + `/triage` gate, via a deterministic script (2026-06-16).**
|
||||
A message in a room's **capture thread** (detected by its `m.relates_to` thread root, configured
|
||||
per room as `capture_thread`), or a `/capture <text>` message in any room, is logged to
|
||||
`~/Projects/standards/INBOX.md` tagged for that room's project — then the existing `/triage`
|
||||
lands it in the repo. *Beat (deliberately rejected):* writing straight into a repo's
|
||||
`AGENTS.md`/`ROADMAP.md` unattended — keeps the human approval gate, and the Current-state-vs-
|
||||
ROADMAP call, where they belong (and AGENTS.md is load-bearing — "propose, don't silently
|
||||
rewrite"). *Beat:* `claude -p /capture` for the write — a one-line append needs no model, so
|
||||
`capture-note.sh` does it deterministically: no token, nothing leaves the Mac but the git push,
|
||||
and message text never reaches a frontier model (upholds the sovereignty constraint / D8). The
|
||||
bot confirms in-thread with the exact inbox line; `/capture` is the zero-config path, the thread
|
||||
just drops the prefix. Thread roots are minted by `seed-capture-threads.py`.
|
||||
|
||||
## Sovereignty constraint
|
||||
|
||||
@@ -210,12 +236,26 @@ once" is not done.
|
||||
- **Phase 3 (Spark Control) shipped 2026-06-16 in v0.21.0:** status badge + Update / Restart /
|
||||
Stop-Start / Logs tile; the Spark's dir is now a Gitea clone and deploy = the Update button.
|
||||
Detail in ROADMAP + `docs/spark-control-integration.md`; no matrix-bridge code change.
|
||||
- **No active build work.** Next moves are all optional / triggered:
|
||||
- **Capture mode (D13) built 2026-06-16 — pending deploy, not yet N=3.** `/capture <text>` in any
|
||||
room and per-room capture threads both log to `standards/INBOX.md` via `capture-note.sh`; bot
|
||||
confirms in-thread. All 11 rooms + all-projects already have their `capture_thread` roots seeded
|
||||
and their IDs are in the Mac's `config.toml`. **To go live:** (1) push code + `git`-deploy on the
|
||||
Spark (Update button); (2) refresh the Spark's `config.toml` — it's gitignored, so the Update
|
||||
button does *not* carry it: on the Spark run
|
||||
`scp mac-bridge:/Users/macpro/Projects/matrix-bridge/config.toml ~/matrix-bridge/config.toml`
|
||||
(or hand-add `capture_launcher` + the `capture_thread` lines), then Restart. Until the Spark has
|
||||
both the new code *and* config, **don't reply in the capture threads** (the old bot would treat a
|
||||
thread reply as a launch).
|
||||
- **Optional / triggered next moves:**
|
||||
- Badge reflects container liveness only, not Synapse connectivity — add a Docker `HEALTHCHECK`
|
||||
(bot-side liveness signal → read `{{.State.Health.Status}}`) when "running but silent" bites.
|
||||
- A `?`-ask in a repo `claude` has never opened may stall on the folder-trust gate — add a trust
|
||||
flag to `ask-claude.sh` if/when hit, not preemptively.
|
||||
- Capture defaults every line to `[idea][P2]`; add inline `[type]`/`[Pn]` parsing to
|
||||
`capture-note.sh` if reclassifying at `/triage` gets tedious.
|
||||
- Phase 4+ (intent-routing brain D8, thread continuity) — see ROADMAP; not scoped.
|
||||
- **Watch:** the Update button depends on modelo's Gitea ssh-config pin (`IdentitiesOnly yes`, see
|
||||
Infra facts) — flag it if that account is ever rebuilt.
|
||||
- **Repo:** `master` == `phase-1`, clean, pushed to Gitea. No test suite (pre-existing).
|
||||
- **Repo:** `master` == `phase-1`. Uncommitted: capture mode (`src/bot.py`, `scripts/capture-note.sh`,
|
||||
`scripts/seed-capture-threads.py`, `config.example.toml`, `AGENTS.md`) + gitignored `config.toml`.
|
||||
No test suite (pre-existing).
|
||||
|
||||
Reference in New Issue
Block a user