The shared checkout's venv is deleted and recreated by release cutovers; a cutover mid-run left every intake tick failing ENOENT on <checkout>/venv/bin/python. ~/.local/bin/hermes is repointed by the release recipe itself, so resolve it fresh on each call and keep the checkout venv as a fallback. Co-Authored-By: Claude Fable 5 <[email protected]>
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
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 (re-verified 2026-08-23)
The Pulse UI is mib007's Activity app (/activity, PULSE_ROUTE in
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/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> - read roots:
GET /api/workspaces/:wid/messages?channelId=&kind=post(root posts only; replies are nested inblocks.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. 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
| 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 atseen), 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.envnext to the script (orPULSE_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 inatorder. 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.
Prefix a post with 💬 (or chat:) to just talk — chat-only posts are
never turned into a ledger item or task and the bridge never replies to
them. The prefix list is configurable via PULSE_BRIDGE_INTAKE_OPTOUT
(comma-separated, case-insensitive).
- Ledger item —
shre-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'sop:addreaches the feed, so the outbound mirror adopts the user's post instead of creating a duplicate. - Kanban task —
hermes kanban create … --created-by pulse-bridge --idempotency-key pulse-<post id> --jsonon 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 tasksand start when a slot frees. - Acknowledgment — comment
🤖 picked up — ledger <id>, kanban <task>on the user's post. - Tracking — each poll reads
kanban list --jsononce; 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.
Intake caveats (accepted, reviewer-flagged)
- Status/ack comments are best-effort: a crash between a state save and its
comment can drop or (once) repeat a comment. Ledger and kanban stay
consistent —
shre-items doneon a closed item is a logged no-op and--idempotency-key pulse-<post id>adopts the existing task on retry. - Bridge-output filtering is structural first (envelope-native bridge posts
carry the service actor id
00000000-…-0001, agents carryactorType: "agent"), then content-based for legacy posts (display name,🤖prefix,ledger:tag line, threaded).
Limitations
- Mentions dispatch server-side — the envelope route runs the same
dispatchMentionside-effect comms did: the first@handlematching a workspace agent fires an AI reply; a matching workspace member gets acomment.mentionnotification. The bridge zwsp-neutralises every@in ledger-derived text and deliberately keeps the needs-you@rapidnirescalation 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 (DROPnow 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
actorDisplayNamepassthrough 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 future work.
- Reactions on posts are not mirrored back to the ledger.