migrate write+read path from mib007 comms to the unified message envelope
Pulse's Activity UI (verified live on :5520, Vite dev serving the checkout at 7293d857) now reads AND writes /api/workspaces/:ws/messages, so the bridge follows: - writes: POST /messages with server-side idempotencyKey (ledger:<id> for roots, (id,op,at) for feed comments, pulse-* keys for intake replies); replay returns the ORIGINAL message, so the v0 find-existing recovery scan and drop-on-500 comment policy are gone (5xx now retries safely, only 400/404/410 drop) - reads: intake ingests envelope roots (kind=post), normalized to the old comms row shape at the boundary; comms GET would miss envelope-native user posts entirely - state carries over unchanged: backfill 0103 + mirror trigger 0104 preserve comms ids, so the ledger→post map and channel id stay valid and old threads accept replies (verified via /messages/threads/<old-id>) - self-recognition: envelope posts carry no userName override, so intake now skips the service actor id (00000000-…-0001) structurally - codex P2 fixed: adopt unmapped v0-era posts (no idempotency key) by ledger:<id> body scan before creating, so an old insert-then-error survivor cannot be duplicated Known cosmetic tradeoff (documented in README): no byline override on the envelope route — new posts render under the service actor, not "Ledger". E2E on the live feed: post 715f52ec, stage comment 74d19219, needs-you @rapidnir comment 2f898b75 + comment.mention notification row for rapidnir-admin, close comment 9f0a5097; timed-out create retried into the same row (idempotency proven, count=1). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01YECpkAwQUwgu7NVy91R8fW
This commit is contained in:
co-authored by
Claude Fable 5
parent
f168ad6f4c
commit
eb0033b952
@@ -1,4 +1,4 @@
|
||||
# pulse-bridge v0
|
||||
# pulse-bridge v1
|
||||
|
||||
Mirrors the estate's shared work ledger (`~/.local/bin/shre-items`,
|
||||
JSONL store at `~/.shre/open-items/items.jsonl`) into **Pulse** — the
|
||||
@@ -6,25 +6,33 @@ social-feed surface of the local mib007 instance (`http://127.0.0.1:5520`,
|
||||
launchd `ai.shre.mib007`). Ledger items become posts, stage changes become
|
||||
threaded comments, needs-you escalations mention the user.
|
||||
|
||||
## Where Pulse actually lives (as discovered 2026-08-22)
|
||||
## Where Pulse actually lives (re-verified 2026-08-23)
|
||||
|
||||
The Pulse UI is mib007's **Activity** app (`/activity`, `PULSE_ROUTE` in
|
||||
`ui/src/platform/lib/app-routes.ts`). Its feed is backed by the **comms**
|
||||
tables, not the unified message envelope: the envelope work (`feed_items`
|
||||
view, migrations 0101-0111) is in shreai PR #163 and is **not merged** into
|
||||
the running mib007 (local checkout is at migration 0100). The
|
||||
`/api/workspaces/:id/feed` route proxies to shre-feed (:5436), which is
|
||||
**down** locally. So the write path Pulse really uses is:
|
||||
`ui/src/platform/lib/app-routes.ts`). Since the unified message envelope
|
||||
landed (migrations 0101-0111 applied; `feed_items` view live) the Activity
|
||||
feed **reads AND writes the envelope** (`useActivity.ts` stage 3), so the
|
||||
bridge does too:
|
||||
|
||||
- create post: `POST /api/workspaces/:wid/comms/channels/:cid/messages`
|
||||
`{content, type: "text", userName}` → returns the row (`id` = post id)
|
||||
- create post: `POST /api/workspaces/:wid/messages`
|
||||
`{channelId | channelSlug, body, idempotencyKey}` → returns the message
|
||||
(`id` = post id; a replayed idempotencyKey returns the ORIGINAL message)
|
||||
- comment/reply: same endpoint with `threadId: <post message id>`
|
||||
- channel: the Activity app reads/writes the channel named `activity`
|
||||
(creates it if missing) — same behaviour here.
|
||||
- read roots: `GET /api/workspaces/:wid/messages?channelId=&kind=post`
|
||||
(root posts only; replies are nested in `blocks.replies`) — the same
|
||||
query the Activity UI issues
|
||||
- channel: the envelope channel with slug `activity`. Comms-era channel and
|
||||
message **ids are preserved** in the envelope (backfill 0103 + mirror
|
||||
trigger 0104), so v0 state (channel id, ledger→post map) carried over
|
||||
unchanged and old threads still accept replies.
|
||||
|
||||
Auth: mib007 service token (`~/.shre/service-tokens.json`, key `mib007`),
|
||||
board-level, valid from loopback. mib007 runs in `authenticated` mode, so
|
||||
requests without it are rejected.
|
||||
requests without it are rejected. **Attribution caveat:** the envelope
|
||||
attributes writes to the authenticated actor and has no `userName` override,
|
||||
so bridge posts no longer carry the "Ledger" byline — they render under the
|
||||
service actor (UI fallback "User"). Legacy posts keep their mirrored
|
||||
`actorDisplayName: "Ledger"`.
|
||||
|
||||
## Mapping
|
||||
|
||||
@@ -100,32 +108,28 @@ checkpoint to the newest existing post — history is never ingested.
|
||||
comment can drop or (once) repeat a comment. Ledger and kanban stay
|
||||
consistent — `shre-items done` on a closed item is a logged no-op and
|
||||
`--idempotency-key pulse-<post id>` adopts the existing task on retry.
|
||||
- Bridge-output filtering is content-based (user name, `🤖` prefix,
|
||||
`ledger:` tag line, threaded); renaming the poster or ignore list while
|
||||
old posts are inside the intake window could ingest bridge output. The
|
||||
comms API has no structured message metadata to mark authorship.
|
||||
- Bridge-output filtering is structural first (envelope-native bridge posts
|
||||
carry the service actor id `00000000-…-0001`, agents carry
|
||||
`actorType: "agent"`), then content-based for legacy posts (display name,
|
||||
`🤖` prefix, `ledger:` tag line, threaded).
|
||||
|
||||
## v0/v1 limitations
|
||||
## Limitations
|
||||
|
||||
- **No real user-mention primitive.** mib007 comms has *agent* @mentions
|
||||
only (the first `@handle` in a message that matches a workspace agent
|
||||
triggers an AI reply). There is no user mention/notification hook, so
|
||||
needs-you escalations are the literal text `@nir NEEDS YOU: …`.
|
||||
- **Mention path is broken on this instance (found during smoke):** ANY
|
||||
`@handle` in a message makes `POST …/messages` **500 after the row is
|
||||
inserted** — the mention agent-lookup queries a nonexistent `url_key`
|
||||
column on `agents` (`comms.ts` ~line 226; error in mib007 stderr log).
|
||||
Retrying such a failure duplicates the message. The bridge therefore
|
||||
(a) zwsp-neutralises **every** `@` it emits, including its own
|
||||
`@nir` escalation (renders identically in the UI), and (b) treats an
|
||||
HTTP error on a *comment* as non-retryable (`DROP … comment not
|
||||
retried` in the log) — comments are best-effort; posts still retry.
|
||||
- **Mentions dispatch server-side** — the envelope route runs the same
|
||||
`dispatchMention` side-effect comms did: the first `@handle` matching a
|
||||
workspace agent fires an AI reply; a matching workspace member gets a
|
||||
`comment.mention` notification. The bridge zwsp-neutralises every `@` in
|
||||
ledger-derived text and deliberately keeps the needs-you `@rapidnir`
|
||||
escalation live so the human is actually notified. The historical
|
||||
insert-then-500 mention crash is fixed AND harmless now: writes carry
|
||||
idempotency keys, so a retry returns the original message instead of
|
||||
duplicating it (`DROP` now only happens on 400/404/410 — thread target
|
||||
gone).
|
||||
- **No byline override.** See the attribution caveat above — posts render
|
||||
under the service actor, not "Ledger". Needs a mib007-side
|
||||
`actorDisplayName` passthrough for service tokens to restore.
|
||||
- Updates for items that pre-date the bridge and were never seeded
|
||||
(closed before first run) are skipped with a log line — there is no
|
||||
post to comment on.
|
||||
- One workspace, one channel. Multi-workspace fan-out is v1.
|
||||
- Fork-links (post → ledger deep link and back) and reverse intake
|
||||
(posting in Pulse creating/annotating ledger items) are **v1**.
|
||||
- One workspace, one channel. Multi-workspace fan-out is future work.
|
||||
- Reactions on posts are not mirrored back to the ledger.
|
||||
- If the envelope migration (shreai #163) lands and Pulse moves to
|
||||
`feed_items`, the write path here must be revisited.
|
||||
|
||||
Reference in New Issue
Block a user