Files
pulse-bridge/README.md
T
Nirav PatelandClaude Fable 5 54b705b073 v1 intake: Pulse posts become ledger items + dispatched kanban tasks
- new top-level non-bridge posts in the activity channel -> shre-items
  pipeline item (tag pulse-intake) + hermes kanban task (idempotency-key
  pulse-<post id>, created-by pulse-bridge) + ack comment
- outbound dedupe: ledger->post map seeded with the user's post id before
  the op:add hits the feed, so the mirror adopts the post
- status tracking via one 'kanban list --json' per poll: running/blocked/
  done comments on the post, ledger stage build -> done
- cap 3 concurrent intake tasks; overflow queued with a comment and
  promoted when slots free; intake state in the same atomic state.json
- first intake run only sets the checkpoint (no history ingestion)

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YECpkAwQUwgu7NVy91R8fW
2026-08-22 15:00:35 -04:00

5.8 KiB

pulse-bridge v0

Mirrors the estate's shared work ledger (~/.local/bin/shre-items, JSONL store at ~/.shre/open-items/items.jsonl) into Pulse — the 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)

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:

  • create post: POST /api/workspaces/:wid/comms/channels/:cid/messages {content, type: "text", userName} → returns the row (id = post id)
  • 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.

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.

Mapping

ledger op Pulse action
add new post: emoji by kind + title, detail, tag line (#kind #stage #tags surface: project: ledger:<id>)
update comment on the mapped post: stage → review, ⛔ failed at verify, attempt 2 — <note>, kind → …
update to kind: needs-you comment @nir NEEDS YOU: …
close comment ✅ done — <why> or 🗑 dropped — <why>

First run seeds posts for currently-open items only (shre-items list --json); history before the bridge is not replayed.

State & config

  • State: ~/.shre/pulse-bridge/state.json — checkpoint (max at seen), boundary-second (id,op,at) dedupe set (the feed window is inclusive), ledger-id → post-id map, resolved channel id. Written atomically (tmp + os.replace).
  • Config: bridge.env next to the script (or PULSE_BRIDGE_ENV); environment variables override. See the file for keys (poll interval, mib base URL, workspace, channel, mention handle, token file).
  • Poll cadence: every 30 s the daemon runs shre-items feed --since <checkpoint> and processes new records in at order. A failed record stops the batch before the checkpoint advances past it, so it is retried next cycle.

launchd

ai.shre.pulse-bridge — KeepAlive daemon, logs to ~/.shre/pulse-bridge/bridge.log:

cp ai.shre.pulse-bridge.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.shre.pulse-bridge.plist

v1 intake (reverse direction)

New top-level posts in the activity channel written by a human (not the bridge, not agent accounts, not 🤖-prefixed, not mirror posts carrying a ledger: tag line) become executed, tracked background work:

  1. Ledger itemshre-items add --kind pipeline --stage queued --tag pulse-intake, title = first 80 chars of the post, detail = full post + post id. The ledger→post map is seeded with the USER'S post id before the item's op:add reaches the feed, so the outbound mirror adopts the user's post instead of creating a duplicate.
  2. Kanban taskhermes kanban create … --created-by pulse-bridge --idempotency-key pulse-<post id> --json on the default board; the Hermes embedded-gateway dispatcher picks it up (~60 s). Cap: max 3 concurrent intake-spawned tasks; overflow posts get ⏳ queued behind N tasks and start when a slot frees.
  3. Acknowledgment — comment 🤖 picked up — ledger <id>, kanban <task> on the user's post.
  4. Tracking — each poll reads kanban list --json once; on a status change it comments (▶️ running / ⛔ blocked / ✅ completed — <result>) and advances the ledger item (running → stage build; done → closed with --why "kanban <id> completed").

Intake state (intake_checkpoint epoch-ms + post↔ledger↔task map) lives in the same atomically-written state.json. First intake run only sets the checkpoint to the newest existing post — history is never ingested.

v0/v1 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.
  • 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.
  • 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.