Capture: parse leading type keyword; record master-deploy + Element lessons
capture-note.sh reads an optional leading bug:/feature:/chore:/idea: keyword as the inbox type (default idea, always P2). AGENTS.md: capture mode marked live; two durable lessons — the Update button deploys origin/master so commits must land there, and Element intercepts /capture (the thread is the trigger).
This commit is contained in:
@@ -71,7 +71,11 @@ deterministic, no Claude call (capture mode, D13).
|
||||
`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
|
||||
the Spark Control dashboard (Phase 3) runs exactly this and streams the output: push to Gitea,
|
||||
then click Update. *(Fallback if Gitea is ever unreachable: scp the files from the Mac —
|
||||
then click Update. **Commit to `master`, not a side branch** — Update pulls `origin/master`, so a
|
||||
commit only on another branch deploys *stale* code with no error (cost a debugging round on
|
||||
2026-06-16: capture mode was pushed to `phase-1` while Update kept pulling the old `master`).
|
||||
Also: `config.toml` is **gitignored**, so Update does *not* carry config changes — refresh it on
|
||||
the Spark separately (`scp mac-bridge:…/config.toml ~/matrix-bridge/config.toml`) before Update. *(Fallback if Gitea is ever unreachable: scp the files from the Mac —
|
||||
`scp mac-bridge:/Users/macpro/Projects/matrix-bridge/{Dockerfile,docker-compose.yml,docker-entrypoint.sh,requirements.txt,config.toml,.env} .`
|
||||
and `scp -r mac-bridge:/Users/macpro/Projects/matrix-bridge/src .`, then rebuild.)*
|
||||
|
||||
@@ -172,8 +176,11 @@ Condensed from the scoping workshop. Each: the call, why, what it beat.
|
||||
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`.
|
||||
bot confirms in-thread with the exact inbox line. The item type comes from an optional leading
|
||||
keyword the user types (`bug:` / `feature:` / `chore:` / …; default `idea`, always `P2`). Thread
|
||||
roots are minted by `seed-capture-threads.py`. *In practice the thread is the only good trigger:*
|
||||
Element intercepts any `/`-prefixed message as a client command, so the `/capture <text>` fallback
|
||||
needs a "Send as message" / `//capture` dance — fine as a code path, not the daily UX (2026-06-16).
|
||||
|
||||
## Sovereignty constraint
|
||||
|
||||
@@ -232,30 +239,27 @@ once" is not done.
|
||||
(`~/matrix-bridge`, a Gitea clone tracking `master`): host networking, `restart: unless-stopped`,
|
||||
read-only mounts of `.env`/`config.toml`/SSH key. Runs as `@agent` in 11 project rooms + an
|
||||
all-projects fan-out room. Interactive (plain msg → phone) and ask (`?`-prefix → answer posted
|
||||
back; D12) both proven. Phase 2: owner-confirmed N=3 routing.
|
||||
back; D12) both proven at N=3; capture (D13) is live (see below). Phase 2: owner-confirmed routing.
|
||||
- **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.
|
||||
- **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).
|
||||
- **Capture mode (D13) LIVE 2026-06-16 — proven on 1 room, N=3 pending.** A reply in a room's
|
||||
capture thread logs to `standards/INBOX.md` via `capture-note.sh` and confirms in-thread. All 11
|
||||
rooms + all-projects have seeded `capture_thread` roots (IDs in the Mac's `config.toml`). A
|
||||
leading keyword sets the type — `bug:` / `feature:` / `chore:` / `idea:` (etc.); no keyword →
|
||||
`idea`; priority is always `P2` (set the real one at `/triage`). **Element note:** the typed
|
||||
`/capture` fallback is mostly dead — Element grabs any `/`-message as a client command ("Unknown
|
||||
Command"); the no-slash **thread** is the path (`//capture …` forces a literal send if ever
|
||||
needed). Deploying capture-style code? See the master-deploy gotcha under Commands → Deploy.
|
||||
- **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.
|
||||
- Capture priority is always `P2`; add a priority keyword/token to `capture-note.sh` if setting
|
||||
it at `/triage` gets tedious. Old `phase-0` branch still exists — delete if it bothers you.
|
||||
- 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`. 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).
|
||||
- **Repo:** single branch `master` (the vestigial `phase-1` was deleted 2026-06-16; capture mode was
|
||||
briefly stranded on it — see Deploy). Clean, pushed to Gitea. No test suite (pre-existing).
|
||||
|
||||
+23
-4
@@ -7,14 +7,16 @@
|
||||
# target repo) drains it like any other captured item.
|
||||
#
|
||||
# Deterministic on purpose: no LLM, no token, nothing leaves the Mac except the git push to
|
||||
# Gitea. The "smarts" (real type/priority, repo-routing, phrasing) stay at /triage, where the
|
||||
# human is — and routing message text through a frontier model would break the sovereignty
|
||||
# boundary (D13/D8). Defaults every capture to a raw [idea][P2]; /triage reclassifies.
|
||||
# Gitea. The "smarts" (priority, repo-routing, phrasing) stay at /triage, where the human is —
|
||||
# routing message text through a frontier model would break the sovereignty boundary (D13/D8).
|
||||
# Type comes from an optional leading keyword the user types ("bug: ...", "feature: ...");
|
||||
# anything without one defaults to a raw [idea]. Priority is always P2; /triage sets the real one.
|
||||
#
|
||||
# Why a login shell (-l): git config / PATH live in ~/.zprofile, which a non-login SSH shell
|
||||
# skips — the same seam as launch-claude.sh / ask-claude.sh.
|
||||
|
||||
set -e
|
||||
setopt extended_glob # for whitespace-run trimming below
|
||||
|
||||
standards="$HOME/Projects/standards"
|
||||
inbox="$standards/INBOX.md"
|
||||
@@ -32,7 +34,24 @@ if [[ ! -f "$inbox" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
line="- [ ] ($project) [idea][P2] $note — via matrix, $(date +%F)"
|
||||
# Optional leading type keyword ("bug: ...", "feature: ...", etc.) → that type; default idea.
|
||||
# Only fires when the text before the first colon is exactly one known keyword, so a note that
|
||||
# merely contains a colon ("ratio is 3:1") is left untouched. Types: standards/guides/capture.md.
|
||||
type="idea"
|
||||
if [[ "$note" == *:* ]]; then
|
||||
cand="${(L)note%%:*}" # lowercase the text before the first colon
|
||||
cand="${cand##[[:space:]]#}" # trim surrounding whitespace
|
||||
cand="${cand%%[[:space:]]#}"
|
||||
case "$cand" in
|
||||
bug|feature|idea|chore|skill|agent|project)
|
||||
type="$cand"
|
||||
note="${note#*:}" # drop "keyword:"
|
||||
note="${note##[[:space:]]#}" # trim leading whitespace
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
line="- [ ] ($project) [$type][P2] $note — via matrix, $(date +%F)"
|
||||
|
||||
print -r -- "$line" >> "$inbox"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user