diff --git a/pm/0.1.0-v0-bridge-contract.org b/pm/arch.org similarity index 100% rename from pm/0.1.0-v0-bridge-contract.org rename to pm/arch.org diff --git a/pm/relay-model.org b/pm/relay-model.org new file mode 100644 index 0000000..2a8c7ee --- /dev/null +++ b/pm/relay-model.org @@ -0,0 +1,89 @@ +#+title: 0.1.1 Relay Envelope and Anti-Loop Rules +#+created: [2026-04-14 Tue 14:40] +#+updated: [2026-04-14 Tue 14:55] + +* envelope +#+begin_src python +from dataclasses import dataclass +from typing import Literal, Optional + +Origin = Literal["zulip", "xmpp"] +Direction = Literal["zulip_to_xmpp", "xmpp_to_zulip"] + +@dataclass(frozen=True) +class RelayEnvelope: + envelope_id: str + origin: Origin + source_message_id: str + sender_display_name: str + target_mapping: str + timestamp: str + body_text: str + sender_platform_id: Optional[str] = None + raw_direction: Optional[Direction] = None + receipt_time: Optional[str] = None +#+end_src + +Required for v0: +| field | purpose | +|---------------------+-----------------------| +| origin | source platform | +| source_message_id | duplicate detection | +| sender_display_name | relay attribution | +| target_mapping | fixed bridge route | +| timestamp | event ordering / logs | + +* relay output +| direction | plain-text shape | +|---------------+--------------------------| +| Zulip -> XMPP | [zulip] : | +| XMPP -> Zulip | [xmpp] : | + +Optional machine stamp: +- bridge_id +- relayed_from +- relayed_source_id + +* loop guards +1. self-authored + - Zulip sender == bridge account + - XMPP sender == bridge nick/jid +2. stamped bridge message + - inbound carries this bridge instance stamp +3. recent-seen duplicate + - key = (origin, source_message_id) + +Order: +1. scope check +2. self/stamp check +3. duplicate check +4. normalize + relay once +5. record seen key after success + +* failure policy +| case | action | +|--------------------------------+--------------------------------| +| empty body after normalization | drop + log | +| unknown origin | drop + log | +| unmapped target | drop + log | +| no usable id | synthesize fallback id, log it | +| non-chat event | drop + log | +| duplicate upstream event | drop, info/debug log | + +* reconnect policy +- no history backfill +- only newly delivered events +- in-memory TTL cache suppresses short replay bursts +- restart may re-emit old events; acceptable for v0 + +* non-goals +- edits / deletes / reactions +- attachment mirroring +- presence / roster sync +- multi-room routing +- Hermes / Retcon integration + +* exit condition +- envelope shape is frozen +- loop guards are explicit +- duplicate / malformed / reconnect behavior is explicit diff --git a/pm/tasks.org b/pm/tasks.org index 25a23e2..903208b 100644 --- a/pm/tasks.org +++ b/pm/tasks.org @@ -1,9 +1,7 @@ #+title: Task Log #+updated: [2026-04-14 Tue 13:26] -Use the template below, which should be a top-level org-mode header. - -* [x] 0.1.0: Define v0 bridge contract (estimate 1 commit) +* [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. @@ -22,7 +20,7 @@ my preference is for python-based systems. - trusted private deployment ** evidence -- commit: +- commit: 5ad2b6b - tests: n/a (design note only) - datetime: [2026-04-14 Tue 14:28] - artifact: pm/0.1.0-v0-bridge-contract.org @@ -30,7 +28,7 @@ my preference is for python-based systems. ** 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) +* [X] 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 @@ -52,8 +50,9 @@ Specify the minimal message metadata the bridge must preserve and the exact loop ** evidence - commit: -- tests: -- datetime: +- tests: n/a (design note only) +- datetime: [2026-04-14 Tue 14:40] +- artifact: pm/0.1.1-relay-envelope-and-loop-rules.org ** notes - Prefer boring explicit metadata over clever heuristics.