From 5ad2b6b9b7a8c0b30a1faeb1a72cb8b637999e02 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 14 Apr 2026 14:36:18 -0400 Subject: [PATCH] 0.1.0 base architecture --- pm/0.1.0-v0-bridge-contract.org | 106 ++++++++++++++++++ pm/tasks.org | 193 +++++++++++++++++++++++++++++--- 2 files changed, 286 insertions(+), 13 deletions(-) create mode 100644 pm/0.1.0-v0-bridge-contract.org diff --git a/pm/0.1.0-v0-bridge-contract.org b/pm/0.1.0-v0-bridge-contract.org new file mode 100644 index 0000000..0bc0332 --- /dev/null +++ b/pm/0.1.0-v0-bridge-contract.org @@ -0,0 +1,106 @@ +#+title: 0.1.0 v0 Bridge Contract +#+created: [2026-04-14 Tue 14:28] + +* summary +v0 is a tiny Python relay bridging one XMPP MUC room to one Zulip stream/topic. + +The bridge is append-only. It relays new human-written messages in both directions. It does not attempt to sync edits, reactions, threads beyond a fixed Zulip topic, presence, history, or rich metadata. + +The design goal is to make transport behavior obvious. If something breaks, it should be clear whether the problem is Zulip ingress, XMPP ingress, relay logic, or configuration. + +* first-run assumptions +- single bridge process +- single XMPP MUC room +- single Zulip stream + fixed topic +- trusted private deployment +- Python implementation +- one bridge identity on each side +- human-readable sender attribution is sufficient for v0 + +* system actors +** Zulip bot/client +- connects to one Zulip server +- subscribes to events for one stream/topic target +- receives new Zulip messages +- sends relay messages into the configured stream/topic + +** XMPP client +- connects as one XMPP account +- joins one MUC room with one nick +- receives new groupchat messages +- sends relay messages into that room + +** relay core +- owns config, logging, message normalization, and loop prevention +- accepts inbound events from either side +- converts them into one minimal internal message shape +- republishes them to the opposite side + +* message flow +** Zulip -> XMPP +1. a new Zulip message arrives in the configured stream/topic +2. Zulip bot/client emits an inbound event to the relay core +3. relay core validates scope + - correct stream + - correct fixed topic + - not authored by the bridge itself +4. relay core normalizes sender name + body +5. relay core formats a plain relay message +6. XMPP client posts that message into the configured MUC room +7. relay core logs success/failure + +** XMPP -> Zulip +1. a new XMPP groupchat message arrives in the configured MUC room +2. XMPP client emits an inbound event to the relay core +3. relay core validates scope + - correct room + - not authored by the bridge nick/account + - ignore non-message noise +4. relay core normalizes sender name + body +5. relay core formats a plain relay message +6. Zulip bot/client posts that message into the configured stream/topic +7. relay core logs success/failure + +* v0 relay format +Plain text only. + +Recommended shape: +- Zulip -> XMPP: "[zulip] : " +- XMPP -> Zulip: "[xmpp] : " + +This is intentionally crude. The point is attribution and debuggability, not elegance. + +* non-goals +- no message edit sync +- no delete sync +- no emoji/reaction sync +- no attachment mirroring in v0 +- no presence mirroring +- no roster/contact sync +- no multi-room routing +- no per-thread/topic mapping beyond one fixed Zulip topic +- no history backfill +- no Hermes behavior in the transport process +- no Retcon writeback in the transport process + +* operational boundaries +- the bridge should ignore service noise such as joins, parts, presence, and subscription events +- the bridge should only handle newly observed messages after startup +- the bridge should fail loudly in logs and stay simple in behavior +- the bridge should not try to be clever about replay, dedupe, or recovery yet beyond basic self-loop avoidance + +* why this scope +This gives the smallest useful system: +- phone-friendly Zulip UI +- XMPP room remains the low-friction bus +- one boring relay to test whether the transport idea is worth keeping + +If this small version is unreliable, adding Hermes or Retcon would only hide the real problems. + +* exit condition for 0.1.0 +Task 0.1.0 is complete when: +- this note exists +- the actor model is frozen +- both message directions are described +- v0 non-goals are explicit +- first-run assumptions are frozen diff --git a/pm/tasks.org b/pm/tasks.org index d15a3d7..25a23e2 100644 --- a/pm/tasks.org +++ b/pm/tasks.org @@ -1,22 +1,189 @@ #+title: Task Log -#+updated: [2026-03-18 Wed 14:19] +#+updated: [2026-04-14 Tue 13:26] Use the template below, which should be a top-level org-mode header. -* [ ] M.m.m: Task Title (estimate # commits) -replace the old observed/canonical workflow with a review-first pipeline that groups normalized rows only during review/combine and links them to catalog items +* [x] 0.1.0: Define v0 bridge contract (estimate 1 commit) +Write a one-page spec for the first bridge: one XMPP MUC room <-> one Zulip stream/topic, append-only relay, no edits/reactions/history sync. +keep scope painfully small so transport bugs are obvious. +my preference is for python-based systems. ** Acceptance Criteria -1. Criterion - - expanded data -2. Criterion - -- pm note: amplifying information +1. A short design note exists under pm/ or docs/ + - includes message flow in both directions + - identifies system actors: Zulip bot, XMPP client, relay core +2. Explicit non-goals are listed + - no thread mapping beyond fixed topic + - no rich sync + - no presence mirroring +3. First-run assumptions are frozen + - single room + - single stream/topic + - trusted private deployment ** evidence -- commit: abc123, bcd234 -- tests: -- datetime: [2026-03-18 Wed 14:15] +- commit: +- tests: n/a (design note only) +- datetime: [2026-04-14 Tue 14:28] +- artifact: pm/0.1.0-v0-bridge-contract.org -** notes -- explanation of work done, decisions made, reasoning \ No newline at end of file +** notes +- This is the boundary-setting task; everything else depends on it. + +* [ ] 0.1.1: Define canonical relay envelope + anti-loop rules (estimate 1 commit) +Specify the minimal message metadata the bridge must preserve and the exact loop-prevention mechanism. + +** Acceptance Criteria +1. Canonical fields are listed + - origin + - source message id + - sender display name + - target room mapping + - timestamp +2. Loop prevention rule is explicit + - bridge stamps outgoing messages + - bridge ignores messages carrying its own origin marker +3. Failure handling is defined + - duplicate delivery behavior + - malformed message behavior + - reconnect replay policy + +- pm note: this is the real core of v0; if this is fuzzy the bridge will thrash. + +** evidence +- commit: +- tests: +- datetime: + +** notes +- Prefer boring explicit metadata over clever heuristics. + +* [ ] 0.1.2: Survey Zulip bot constraints + auth model (estimate 1 commit) +Confirm what kind of bot/client should be used on the Zulip side and document any constraints that change architecture. + +** Acceptance Criteria +1. Zulip integration mode is chosen + - bot user vs normal user account + - event queue / polling approach +2. Constraints are captured + - message formatting limitations + - topic requirements + - rate limits / event semantics if relevant +3. Setup checklist is written + - credentials needed + - permissions needed + - minimal stream access needed + +- pm note: Zulip quirks may force the shape of the relay more than XMPP will. + +** evidence +- commit: +- tests: +- datetime: + +** notes +- Keep this empirical; document only constraints that affect v0. + +* [ ] 0.1.3: Survey XMPP MUC event model + library options (estimate 1 commit) +Pick the first XMPP client library/runtime and document the exact events needed for room relay. + +** Acceptance Criteria +1. First implementation language/runtime is chosen + - likely Python unless a strong reason says otherwise +2. Library candidates are compared briefly + - connection support + - MUC message receive/send + - reconnect behavior +3. Required event subset is documented + - groupchat message receive + - send message to MUC + - ignore join/leave/presence noise + +- pm note: avoid solving the Emacs client here; this is just relay transport. + +** evidence +- commit: +- tests: +- datetime: + +** notes +- Goal is “good enough to prototype,” not perfect XMPP abstraction. + +* [ ] 0.1.4: Write config schema for single-room bridge (estimate 1 commit) +Design the smallest possible config file/environment contract for running one bridge instance. + +** Acceptance Criteria +1. Config fields are listed + - Zulip site/email/api key + - Zulip stream/topic + - XMPP jid/password/muc room/nick +2. One-room mapping is represented simply + - avoid generalized many-room config unless needed +3. Secrets vs non-secrets are separated + - env vars or secrets file for credentials + - checked-in example config for everything else + +- pm note: optimize for easy local bring-up, not future elegance. + +** evidence +- commit: +- tests: +- datetime: + +** notes +- If the config shape feels overdesigned, shrink it again. + +* [ ] 0.1.5: Define observability + manual test plan for v0 (estimate 1 commit) +Specify how to tell whether the bridge is healthy before adding Hermes. + +** Acceptance Criteria +1. Logs are defined + - startup config summary without secrets + - inbound message event + - outbound relay result + - duplicate/loop drop reason +2. Manual test cases are listed + - Zulip -> XMPP happy path + - XMPP -> Zulip happy path + - duplicate suppression + - reconnect after disconnect +3. Success criteria are concrete + - messages appear once on each side + - sender attribution preserved enough for humans + - no infinite echo loops + +- pm note: make the bridge boring before attaching Hermes or Retcon. + +** evidence +- commit: +- tests: +- datetime: + +** notes +- This should become the operator checklist for first deploy. + +* [ ] 0.1.6: Sketch Hermes/Retcon follow-on integration points (estimate 1 commit) +Document where Hermes and Retcon could attach after transport is stable, without implementing them yet. + +** Acceptance Criteria +1. Hermes interaction modes are listed + - passive listener + - command-triggered actor + - summarizer +2. Retcon integration points are listed + - append raw inbox + - synthesize summaries + - extract durable notes +3. Trust boundaries are explicit + - what can be written automatically + - what should remain user-confirmed + +- pm note: this keeps the long-term vision visible without contaminating v0 scope. + +** evidence +- commit: +- tests: +- datetime: + +** notes +- Hermes should arrive after transport and logging are stable.