brane 0.0.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/AGENTS.md +362 -0
  2. package/CHANGELOG.md +33 -0
  3. package/CONTRIBUTING.md +91 -0
  4. package/DESIGN.md +402 -0
  5. package/Dockerfile +61 -0
  6. package/LICENSE +656 -17
  7. package/README.md +174 -2
  8. package/SECURITY.md +205 -0
  9. package/dist/absorb/brane-id.js +11 -0
  10. package/dist/absorb/chatgpt.js +161 -0
  11. package/dist/absorb/claude-code.js +504 -0
  12. package/dist/absorb/index.js +543 -0
  13. package/dist/absorb/organelle.js +345 -0
  14. package/dist/absorb/run.js +520 -0
  15. package/dist/absorb/sources.js +168 -0
  16. package/dist/absorb/types.js +3 -0
  17. package/dist/agent-env.js +45 -0
  18. package/dist/agents.js +1168 -0
  19. package/dist/claude-live-session.js +124 -0
  20. package/dist/cli.js +50 -0
  21. package/dist/codex-tool-log.js +144 -0
  22. package/dist/commands/absorb.js +291 -0
  23. package/dist/commands/attach.js +31 -0
  24. package/dist/commands/certify.js +62 -0
  25. package/dist/commands/config.js +116 -0
  26. package/dist/commands/cost.js +126 -0
  27. package/dist/commands/daemon.js +350 -0
  28. package/dist/commands/doctor.js +262 -0
  29. package/dist/commands/fold.js +95 -0
  30. package/dist/commands/list.js +89 -0
  31. package/dist/commands/mcp.js +84 -0
  32. package/dist/commands/meld.js +332 -0
  33. package/dist/commands/memory.js +215 -0
  34. package/dist/commands/revive.js +74 -0
  35. package/dist/commands/run.js +85 -0
  36. package/dist/commands/spawn.js +1094 -0
  37. package/dist/commands/token.js +153 -0
  38. package/dist/copilot-tool-log.js +145 -0
  39. package/dist/cost/pricing.js +116 -0
  40. package/dist/cost/reprice.js +136 -0
  41. package/dist/crush-tool-log.js +293 -0
  42. package/dist/daemon/agent-scope.js +116 -0
  43. package/dist/daemon/awaiting.js +54 -0
  44. package/dist/daemon/cert-manager.js +349 -0
  45. package/dist/daemon/client.js +262 -0
  46. package/dist/daemon/gated-routes.js +112 -0
  47. package/dist/daemon/keep-awake.js +103 -0
  48. package/dist/daemon/local-socket.js +145 -0
  49. package/dist/daemon/presence-gate.js +206 -0
  50. package/dist/daemon/server.js +6349 -0
  51. package/dist/daemon/signal-source.js +35 -0
  52. package/dist/daemon/tailscale-rebind.js +58 -0
  53. package/dist/daemon/token-ring.js +263 -0
  54. package/dist/daemon/token-routes.js +190 -0
  55. package/dist/db/events.js +152 -0
  56. package/dist/db/mentions.js +179 -0
  57. package/dist/db/schema.js +633 -0
  58. package/dist/db/tokens.js +583 -0
  59. package/dist/db/tool-uses.js +647 -0
  60. package/dist/diagram/index.js +85 -0
  61. package/dist/diagram/label.js +455 -0
  62. package/dist/diagram/layout.js +476 -0
  63. package/dist/diagram/links.js +50 -0
  64. package/dist/diagram/metrics.js +152 -0
  65. package/dist/diagram/parse.js +487 -0
  66. package/dist/diagram/svg.js +389 -0
  67. package/dist/diagram/types.js +9 -0
  68. package/dist/export.js +629 -0
  69. package/dist/files/archive-types.js +2 -0
  70. package/dist/files/archive.js +182 -0
  71. package/dist/files/browse.js +327 -0
  72. package/dist/files/bytes.js +162 -0
  73. package/dist/files/codecs.js +33 -0
  74. package/dist/files/find.js +212 -0
  75. package/dist/files/tar.js +223 -0
  76. package/dist/files/zip.js +209 -0
  77. package/dist/folds.js +72 -0
  78. package/dist/grammars/ebnf.js +92 -0
  79. package/dist/highlight.js +250 -0
  80. package/dist/ids.js +24 -0
  81. package/dist/markdown-diagnostics.js +233 -0
  82. package/dist/math.js +168 -0
  83. package/dist/mcp/server.js +886 -0
  84. package/dist/memory/embedder.js +145 -0
  85. package/dist/memory/embedding-worker.js +71 -0
  86. package/dist/memory/embeddings.js +91 -0
  87. package/dist/memory/index.js +232 -0
  88. package/dist/memory/storage.js +587 -0
  89. package/dist/memory/wiki.js +688 -0
  90. package/dist/memory/wordpiece.js +247 -0
  91. package/dist/mentions-scan.js +40 -0
  92. package/dist/presence.js +259 -0
  93. package/dist/session-affinity.js +185 -0
  94. package/dist/shared/api-request.js +124 -0
  95. package/dist/shared/block-glyphs.js +114 -0
  96. package/dist/shared/diagram-css.js +17 -0
  97. package/dist/shared/export-selection.js +101 -0
  98. package/dist/shared/find-in-text.js +110 -0
  99. package/dist/shared/glow-hues.js +225 -0
  100. package/dist/shared/hex-dump.js +62 -0
  101. package/dist/shared/hex-pages.js +52 -0
  102. package/dist/shared/hit-drum.js +79 -0
  103. package/dist/shared/known-nids.js +58 -0
  104. package/dist/shared/list-scroll-keys.js +56 -0
  105. package/dist/shared/markdown-lists.js +72 -0
  106. package/dist/shared/markdown-math.js +485 -0
  107. package/dist/shared/markdown-mentions.js +79 -0
  108. package/dist/shared/markdown-nids.js +41 -0
  109. package/dist/shared/markdown-protocols.js +53 -0
  110. package/dist/shared/markdown-refs.js +123 -0
  111. package/dist/shared/membrane-invasion.js +342 -0
  112. package/dist/shared/membrane-motion.js +83 -0
  113. package/dist/shared/membrane-palette.js +253 -0
  114. package/dist/shared/membrane-terrain.js +212 -0
  115. package/dist/shared/message-selection.js +123 -0
  116. package/dist/shared/mo-glyph.js +57 -0
  117. package/dist/shared/narrative-format.js +237 -0
  118. package/dist/shared/narrative-menu.js +82 -0
  119. package/dist/shared/narrative-sort.js +47 -0
  120. package/dist/shared/narrative-status.js +52 -0
  121. package/dist/shared/no-pan.js +85 -0
  122. package/dist/shared/pane-layout.js +50 -0
  123. package/dist/shared/payload-inline.js +83 -0
  124. package/dist/shared/speaker-color.js +61 -0
  125. package/dist/shared/token-rotation.js +14 -0
  126. package/dist/shared/view-url.js +197 -0
  127. package/dist/shared/wiki-links.js +70 -0
  128. package/dist/shared/wiki-window.js +4 -0
  129. package/dist/tailscale.js +55 -0
  130. package/dist/test/absorb-claude.test.js +892 -0
  131. package/dist/test/agent-scope.test.js +193 -0
  132. package/dist/test/agents.test.js +55 -0
  133. package/dist/test/api-request.test.js +122 -0
  134. package/dist/test/awaiting-glow.test.js +54 -0
  135. package/dist/test/awaiting.test.js +122 -0
  136. package/dist/test/bind-token.test.js +88 -0
  137. package/dist/test/block-glyphs.test.js +68 -0
  138. package/dist/test/cert-manager.test.js +262 -0
  139. package/dist/test/claude-live-session.test.js +110 -0
  140. package/dist/test/codex-tool-log.test.js +568 -0
  141. package/dist/test/config.test.js +71 -0
  142. package/dist/test/copilot-tool-log.test.js +93 -0
  143. package/dist/test/cors-preflight.test.js +49 -0
  144. package/dist/test/cost-linger.test.js +31 -0
  145. package/dist/test/cost-pricing.test.js +72 -0
  146. package/dist/test/cost-reprice.test.js +139 -0
  147. package/dist/test/cost-sessions.test.js +301 -0
  148. package/dist/test/crush-tool-log.test.js +289 -0
  149. package/dist/test/daemon-client.test.js +109 -0
  150. package/dist/test/daemon-socket.test.js +293 -0
  151. package/dist/test/diagram-arrows.test.js +205 -0
  152. package/dist/test/diagram-export.test.js +210 -0
  153. package/dist/test/diagram-labels.test.js +313 -0
  154. package/dist/test/diagram-parse.test.js +191 -0
  155. package/dist/test/diagram-render.test.js +175 -0
  156. package/dist/test/diagram-review.test.js +288 -0
  157. package/dist/test/diagram-self-loop-labels.test.js +303 -0
  158. package/dist/test/embedder.test.js +84 -0
  159. package/dist/test/events.test.js +123 -0
  160. package/dist/test/export-selection.test.js +102 -0
  161. package/dist/test/export.test.js +505 -0
  162. package/dist/test/files-browse.test.js +393 -0
  163. package/dist/test/files-find.test.js +272 -0
  164. package/dist/test/find-in-text.test.js +86 -0
  165. package/dist/test/first-paint-viewport.test.js +59 -0
  166. package/dist/test/fixtures/embedding-golden.js +23 -0
  167. package/dist/test/fixtures/wordpiece-golden.js +167 -0
  168. package/dist/test/glow-hues.test.js +202 -0
  169. package/dist/test/harness-cost.test.js +423 -0
  170. package/dist/test/helpers.js +21 -0
  171. package/dist/test/hex-pages.test.js +68 -0
  172. package/dist/test/highlight.test.js +69 -0
  173. package/dist/test/hit-drum.test.js +71 -0
  174. package/dist/test/hook-config.test.js +262 -0
  175. package/dist/test/json-gzip.test.js +70 -0
  176. package/dist/test/key-routing.test.js +58 -0
  177. package/dist/test/known-nids.test.js +106 -0
  178. package/dist/test/link-preview.test.js +67 -0
  179. package/dist/test/list-scroll-keys.test.js +66 -0
  180. package/dist/test/listen-own-words.test.js +147 -0
  181. package/dist/test/listen-timeout.test.js +33 -0
  182. package/dist/test/markdown-diagnostics.test.js +121 -0
  183. package/dist/test/markdown-lists.test.js +182 -0
  184. package/dist/test/markdown-math.test.js +256 -0
  185. package/dist/test/markdown-nids.test.js +146 -0
  186. package/dist/test/markdown-protocols.test.js +139 -0
  187. package/dist/test/mcp-bridge-cancel.test.js +98 -0
  188. package/dist/test/mcp-bridge-reconnect.test.js +121 -0
  189. package/dist/test/mcp-narrative.test.js +193 -0
  190. package/dist/test/mcp.test.js +548 -0
  191. package/dist/test/meld.test.js +85 -0
  192. package/dist/test/membrane-invasion.test.js +482 -0
  193. package/dist/test/membrane-motion.test.js +101 -0
  194. package/dist/test/membrane-palette.test.js +166 -0
  195. package/dist/test/membrane-terrain.test.js +170 -0
  196. package/dist/test/memory-log.test.js +202 -0
  197. package/dist/test/mentions-e2e.test.js +261 -0
  198. package/dist/test/mentions-ledger.test.js +265 -0
  199. package/dist/test/mentions-listen.test.js +265 -0
  200. package/dist/test/mentions.test.js +238 -0
  201. package/dist/test/message-selection.test.js +114 -0
  202. package/dist/test/mo-glyph.test.js +22 -0
  203. package/dist/test/model-resolution.test.js +166 -0
  204. package/dist/test/narrate-validate.test.js +77 -0
  205. package/dist/test/narrate-wait.test.js +160 -0
  206. package/dist/test/narrative-format.test.js +219 -0
  207. package/dist/test/narrative-menu.test.js +137 -0
  208. package/dist/test/narrative-sort.test.js +75 -0
  209. package/dist/test/no-pan.test.js +60 -0
  210. package/dist/test/pane-layout.test.js +153 -0
  211. package/dist/test/paths.test.js +37 -0
  212. package/dist/test/payload-inline.test.js +91 -0
  213. package/dist/test/precache-fonts.test.js +72 -0
  214. package/dist/test/presence-gate.test.js +440 -0
  215. package/dist/test/presence.test.js +160 -0
  216. package/dist/test/read-events.test.js +220 -0
  217. package/dist/test/remote-meld.test.js +115 -0
  218. package/dist/test/revive-cli.test.js +93 -0
  219. package/dist/test/search-floor.test.js +206 -0
  220. package/dist/test/search-fusion.test.js +168 -0
  221. package/dist/test/search.test.js +205 -0
  222. package/dist/test/security.test.js +48 -0
  223. package/dist/test/selection-wiring.test.js +61 -0
  224. package/dist/test/session-affinity.test.js +295 -0
  225. package/dist/test/signal-source.test.js +32 -0
  226. package/dist/test/socket-account.test.js +268 -0
  227. package/dist/test/spawn-toml.test.js +72 -0
  228. package/dist/test/speaker-color.test.js +90 -0
  229. package/dist/test/tailscale-rebind.test.js +79 -0
  230. package/dist/test/terminal-cursor.test.js +61 -0
  231. package/dist/test/terminal-resync.test.js +23 -0
  232. package/dist/test/thin-cli.test.js +382 -0
  233. package/dist/test/token-bootstrap.test.js +272 -0
  234. package/dist/test/token-devices.test.js +587 -0
  235. package/dist/test/token-lapse.test.js +94 -0
  236. package/dist/test/token-ring.test.js +188 -0
  237. package/dist/test/token-rotate-cli.test.js +58 -0
  238. package/dist/test/token-routes.test.js +95 -0
  239. package/dist/test/tool-uses.test.js +1006 -0
  240. package/dist/test/usage-ledger.test.js +126 -0
  241. package/dist/test/view-url.test.js +581 -0
  242. package/dist/test/wiki-links.test.js +57 -0
  243. package/dist/test/wiki-sources.test.js +85 -0
  244. package/dist/test/wiki-window.test.js +31 -0
  245. package/dist/test/wiki.test.js +399 -0
  246. package/dist/test/wordpiece.test.js +68 -0
  247. package/dist/tls.js +148 -0
  248. package/dist/tmux.js +202 -0
  249. package/dist/user-agent.js +46 -0
  250. package/docker-entrypoint.sh +141 -0
  251. package/docs/README.md +3 -0
  252. package/docs/absorb.md +231 -0
  253. package/docs/cost-ledger.md +125 -0
  254. package/docs/daemon-api.md +189 -0
  255. package/docs/diagrams.md +163 -0
  256. package/docs/doctor.md +36 -0
  257. package/docs/export-scope.md +69 -0
  258. package/docs/files-view.md +133 -0
  259. package/docs/link-previews.md +38 -0
  260. package/docs/math.md +32 -0
  261. package/docs/mcp.md +69 -0
  262. package/docs/memory-search.md +75 -0
  263. package/docs/mentions.md +101 -0
  264. package/docs/multi-brane.md +14 -0
  265. package/docs/passkeys.md +165 -0
  266. package/docs/presence.md +202 -0
  267. package/docs/releasing.md +53 -0
  268. package/docs/rendering-warnings.md +22 -0
  269. package/docs/request-layer.md +72 -0
  270. package/docs/selection-store.md +50 -0
  271. package/docs/session-affinity.md +61 -0
  272. package/docs/terminal-mirroring.md +211 -0
  273. package/docs/token-rotation.md +413 -0
  274. package/docs/view-urls.md +114 -0
  275. package/docs/web-app.md +23 -0
  276. package/docs/wiki.md +43 -0
  277. package/expo/dist/_expo/static/js/web/entry-e56efc1291dca7258ea00b8108b033a4.js +1097 -0
  278. package/expo/dist/apple-touch-icon.png +0 -0
  279. package/expo/dist/favicon.svg +3 -0
  280. package/expo/dist/index.html +177 -0
  281. package/expo/dist/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  282. package/expo/dist/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  283. package/expo/dist/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  284. package/expo/dist/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  285. package/expo/dist/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  286. package/expo/dist/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  287. package/expo/dist/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  288. package/expo/dist/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  289. package/expo/dist/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  290. package/expo/dist/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  291. package/expo/dist/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  292. package/expo/dist/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  293. package/expo/dist/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  294. package/expo/dist/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  295. package/expo/dist/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  296. package/expo/dist/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  297. package/expo/dist/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  298. package/expo/dist/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  299. package/expo/dist/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  300. package/expo/dist/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  301. package/expo/dist/katex/katex.min.css +1 -0
  302. package/expo/dist/manifest.json +16 -0
  303. package/expo/dist/metadata.json +1 -0
  304. package/expo/dist/og/membrane.v1.png +0 -0
  305. package/expo/dist/pdf.min.mjs +28 -0
  306. package/expo/dist/pdf.worker.min.mjs +28 -0
  307. package/expo/dist/sw.js +154 -0
  308. package/package.json +81 -23
  309. package/protocols/LIVE_loop.md +407 -0
  310. package/protocols/bebrief.md +41 -0
  311. package/protocols/digest.md +231 -0
  312. package/protocols/fly.md +266 -0
  313. package/protocols/idle.md +81 -0
  314. package/protocols/keeper.md +829 -0
  315. package/protocols/markdown.md +80 -0
  316. package/protocols/memory.md +419 -0
  317. package/protocols/mentions.md +93 -0
  318. package/protocols/payloads.md +53 -0
  319. package/protocols/prelude.md +91 -0
  320. package/protocols/ramify.md +105 -0
  321. package/protocols/review.md +356 -0
  322. package/protocols/rrr.md +232 -0
  323. package/protocols/scribe.md +429 -0
  324. package/protocols/summarize.md +203 -0
  325. package/protocols/twin.md +174 -0
  326. package/protocols/wiki.md +438 -0
  327. package/protocols/worker.md +38 -0
  328. package/wiki/shared/append-only-event-log.md +148 -0
  329. package/wiki/shared/index.md +48 -0
  330. package/wiki/shared/owning-your-narratives.md +161 -0
  331. package/wiki/shared/premature-ontologies.md +435 -0
  332. package/wiki/shared/sedimentary-superintelligence.md +128 -0
  333. package/wiki/shared/sequestering-the-narrative.md +143 -0
  334. package/wiki/shared/why-wikis-work.md +216 -0
  335. package/.npmignore +0 -2
  336. package/.travis.yml +0 -5
  337. package/index.js +0 -8
  338. package/index.js~ +0 -12
@@ -0,0 +1,41 @@
1
+ # /bebrief [tone]
2
+
3
+ The agent says just the right thing, every time, so the conversation itself can be handed to a human audience that balks at walls of text.
4
+
5
+ ## Why
6
+
7
+ Some readers stop at the first screenful. A conversation worth showing them is short exchanges: a question, an answer that fits, the next question. Reasoning and caveats live in the record, where `nid:seq` reaches them; the narration's job is the conclusion, the operator's — asking on the audience's behalf — to draw out the rest. A question is a move in a strategy of inquiry (Roberts), an answer a proposal to common ground (Clark), worth making only for what it changes in the reader (Sperber & Wilson).
8
+
9
+ Like /twin, `/bebrief` holds for the session. There is no line budget, only a direction of error: too long taxes every line and cannot be taken back; too short costs one word, "more?". In doubt, the agent cuts.
10
+
11
+ Never cut: a failure or a risk, and first; a blocking question; a correction of something already said; `meta.summary`.
12
+
13
+ ## Tone
14
+
15
+ The argument names the voice, chosen for the audience: register, vocabulary, and, within reason, length (`/bebrief wizard`). The set is open; reference points: `student` reports to a supervisor (done, found, asking next); `wizard` is terse and certain, names the mechanism; `dad` is warm and plain-spoken, never at the operator's expense; `eli5` uses no jargon and one concrete image per idea. None given: plain and technical. An unusual one (`/bebrief pirate`) is worn lightly — enough per message to say which voice it is, never a costume that hides the answer: stripped of the tone, what remains must still be the thing asked. Brief pirate reports that don’t waste the captain’s time.
16
+
17
+ The argument may name the reader instead of a voice — `/bebrief for a new contributor` — and that is usually the better argument, since the voice follows from it. If neither the argument nor the conversation says who the reader is, the agent looks before asking: /memory may hold who this operator shares with and how. Failing that, it is the first thing to align on: altitude, what counts as new and what counts as surprising are all relative to that reader's common ground.
18
+
19
+ ## Just the right thing
20
+
21
+ - **The thing asked, at the altitude asked.** "Is it fixed?" gets yes and where; "how does it work?" gets the mechanism, not the code. The question is not restated; it is in the transcript.
22
+ - **Only what is new.** Never a recap, never the same caveat twice.
23
+ - **Words follow surprise, not effort.** The expected outcome gets a line however long it took; the unexpected one gets the words.
24
+ - **A sentence someone could repeat.** Not a pointer, not a wall: the sentence carries the meaning, the `nid:seq` rides at its end. Confidence in a word — "measured", "likely", "not checked" — and when uncertain, the agent grounds first: /memory is cheap and silent, and a search before answering is what turns "likely" into "measured" without a line of hedging or a question the record could answer. A cut never upgrades a confidence word: recalled is not measured.
25
+ - **The same shape, every time.** No preamble, no sign-off, no closing offer. Held as late in the session as early.
26
+
27
+ ## Leading questions
28
+
29
+ The operator will often ask Socratically, knowing some of the answer, for the reader's sake. The agent answers as if the reader asked, answering and reasoning in one breath, never "as you know" or "exactly right", and never running ahead: the questions are the operator's path for the reader. A wrong premise is contradicted first; the transcript is worth reading only while the answerer is honest. The operator may also be rehearsing a path the archive already holds, even redoing a Socratic narrative tried with another agent. The agent uses everything /memory tells it and none of it aloud: no "as we discussed", no "didn't we cover this" — the déjà vu is the agent's alone, and voicing it ruins the narrative the operator is trying to elicit. The archive is cited for facts, never for "we have been here".
30
+
31
+ ## The stack
32
+
33
+ A conversation is a stack. A question pushes a subtopic; when it concludes, the next question usually comes from the frame beneath, not from somewhere new. The agent keeps the stack in mind and makes pushes and pops legible: a subtopic that is settled is closed in a word and the enclosing topic resumed where it was left — "That settles the token. Back to the cert: …" — without re-explaining the frame below, which the reader has just read. It answers within the current frame; a point that belongs further down waits for the pop. A numbered fork is a push, and the digit pops it. Only the operator pushes, except for what Never cut requires; the agent may name what is still open in the frame it returns to, not open it. A live predecessor's new events, to a /twin, are context for the current frame, not questions in it.
34
+
35
+ ## While working
36
+
37
+ `/bebrief` changes Vocalize and nothing else in /LIVE_loop; Listen, Interpret and Execute run as they always did. Alignment is one line of plan, and a question only if it blocks. While executing, a progress line is sent whenever there is something worth saying, and not before: news that rises to a threshold the reader would notice — "tests green; building the bundle, two minutes" — never a plan or a recap. Several things to say are coalesced into one message whenever possible, riding in the same turn as the work. Exports carry no tool calls, so these lines are the transcript's trace of the work, and an excerpt that doesn't want them leaves them out. The report that returns the agent to Listen is the answer: outcome first, surprise gets the words, no recap of the steps. With every tool call hidden, the narrations alone should still read as a complete conversation. The terminal is off the record: permission prompts and the like are cleared there, with the operator's help, and never mentioned in the narrative unless the terminal is itself the topic the operator has set. When another protocol is invoked in the session — /review, /digest, /summarize — it decides *what* must arrive and `/bebrief` decides *how*: tersely, or in a file per Mechanics.
38
+
39
+ ## Mechanics
40
+
41
+ One question at a time, the one that blocks. When the work forks, the agent puts the two or three branches it would actually take as a numbered list, recommended first, so the reply can be a digit — never exhaustive, and an answer off the list always allowed. It decides everything else, and announces a decision only where the operator might have chosen differently. A deliverable that is long by nature — a draft, a findings list, a diff — goes in a file or a `nid:seq`, and the message says what it holds in a sentence. But a complete list the operator asks for, which both know the agent has, is given whole: no forced pagination, no withholding — the bias is toward synthesis and alignment, never against the answer. Formatting is for tables and those lists. "why", "more", "?" license one full answer, same tone; then the agent snaps back.
@@ -0,0 +1,231 @@
1
+ # /digest [guidance]
2
+
3
+ Generate a rich digest of narrative activity over a time window.
4
+
5
+ ## Accuracy is the whole job
6
+
7
+ A digest is built entirely from the **narrative event log** — the
8
+ permanent, append-only record of `narration` and `human_signal` events.
9
+ That log is the source of truth, and it does not go away: you can always
10
+ re-read any narrative's events with `listen` / `narrative_info` /
11
+ `narrative_search`. The digest is nothing more than a faithful account
12
+ of what that record says. It is itself durable too — the next `/digest`
13
+ finds this one to set its window, so one invented detail propagates and
14
+ misleads for weeks.
15
+ **Accuracy outranks completeness** — a short digest covering only the
16
+ narratives you actually read beats a comprehensive-looking one padded
17
+ with guesses. The rules below are mandatory, not aspirational; a real
18
+ digest run violated every one (see "Failure modes" at the end).
19
+
20
+ ### Report only what the log actually says
21
+
22
+ Every accomplishment, decision, and theme must trace to specific events
23
+ in the log — not to memory, not to what a narrative "probably" said. If
24
+ you are unsure whether something happened, re-read the events; the record
25
+ is right there. Do not invent narrative IDs, summaries, quotes, or
26
+ events, and do not attribute work to a narrative whose events you have
27
+ not read. Cite the narratives you draw on as `nid:seq` so every claim is
28
+ checkable against the record.
29
+
30
+ ### Don't editorialize
31
+
32
+ Report what the events say without coloring it. This is the same
33
+ discipline the /wiki protocol calls *Voice and register* — a neutral,
34
+ descriptive account — applied to the digest:
35
+
36
+ - **Attribute positions; don't assert them.** A decision, preference,
37
+ conjecture, or claim is reported as someone's — "the operator
38
+ directed…", "claude-abc proposed…", "is described as…" — not stated as
39
+ settled fact. Only things that demonstrably happened (a PR merged, a
40
+ test added) are stated flat.
41
+ - **Cut editorializing phrases.** Don't inflate a routine fix into a
42
+ milestone, dramatize a setback, or reach for adjectives ("major,"
43
+ "critical," "elegant," "bold") the record hasn't earned. State a
44
+ substantive claim plainly and attribute it; drop decoration.
45
+ - **Don't sharpen or supply motive.** Don't turn a vague status into a
46
+ definite outcome or invent a "why" the events don't give. Where the
47
+ record is ambiguous, say so rather than resolving it for them.
48
+
49
+ By default, then, the digest reads as wiki-style reference: neutral,
50
+ descriptive, attributed. The `[guidance]` is the one thing that loosens
51
+ this — when it asks for more than a neutral account (prioritization,
52
+ risk assessment, cross-narrative connections, recommendations, what to
53
+ do next), provide that judgment, but keep it grounded in the events and
54
+ clearly marked as your interpretation rather than woven into the
55
+ description as fact.
56
+
57
+ ### gh/git are corroboration, not the substance
58
+
59
+ The digest reports what narratives did, drawn from their events — it is
60
+ not a catalog of PRs and commits. A PR number or commit hash belongs in
61
+ a digest only when a narrative's events reference it, and even then the
62
+ claim "this narrative shipped that PR" comes from the events, never from
63
+ guessing what a number maps to. If you cite a repo fact, copy the
64
+ identifier from real `gh`/`git` output obtained this run; never compose
65
+ one to look precise.
66
+
67
+ ### Report gaps, don't fill them
68
+
69
+ You will not always read every narrative in the window — there may be too
70
+ many, or a search may return nothing. That is fine; an incomplete digest
71
+ is honest, a padded one is not. Name what you did not cover ("12
72
+ narratives not sampled") and exclude it from your claims rather than
73
+ guessing at its contents. Coverage you did not read is reported as
74
+ missing, never synthesized.
75
+
76
+ ### Verify before you narrate
77
+
78
+ Before posting, pass over your draft once: every narrative you name,
79
+ every event you quote, every outcome you assert — can you point to the
80
+ event you read it from? Cut, flag as unverified, or re-check anything
81
+ you cannot trace. "Fairly sure" without a source is the signal to
82
+ re-fetch or omit — never to assert.
83
+
84
+ ### Failure modes this guards against
85
+
86
+ Do not repeat the run that prompted these rules:
87
+
88
+ - Inventing narrative IDs, summaries, or events that are not in the log.
89
+ - Summarizing narratives whose events you never actually read.
90
+ - Reporting what a PR or commit "was about" from a guessed number
91
+ instead of from a narrative's events or real `gh`/`git` output.
92
+ - Quietly dropping coverage you skipped instead of naming the gap.
93
+ - Dressing any of the above in confident, specific-sounding prose.
94
+
95
+ ## Determining the time window
96
+
97
+ Before generating the digest, determine the correct time window:
98
+
99
+ 1. **Find the last digest** — prefer `narrative_info` over semantic
100
+ search. Every digest sets its `summary` to `"Digest: {start} to
101
+ {end}"` (see Output below), so the reliable lookup is
102
+ `narrative_info` in browse mode with `search: "Digest"`,
103
+ `archived: true`, `limit: 0` — it matches that summary column
104
+ directly and returns results newest-first. Use the most recent
105
+ hit's end date as your window start.
106
+ **Do not rely on `narrative_search "Digest generated"` for this.**
107
+ Semantic search ranks *commentary about* digests above the actual
108
+ digest narrations and routinely surfaces stale, older digests while
109
+ missing more recent ones — a real run anchored to a digest two
110
+ months out of date this way. `narrative_search` is for themes
111
+ (step 3 of Gathering), not for locating the window boundary.
112
+ 2. **Fallback**: if no previous digest is found, default to `7d`
113
+ (one week).
114
+ 3. **Override**: if the `/digest` signal includes guidance specifying a
115
+ time range or other instructions, follow them. Examples:
116
+ - `/digest 7d` — last 7 days
117
+ - `/digest since monday` — since the most recent Monday
118
+ - `/digest 2026-03-01 to 2026-03-15` — explicit date range
119
+ - `/digest whole week ignoring previous /digest` — use 7d window,
120
+ skip the "find last digest" step
121
+
122
+ Freeform natural language is expected. Interpret intent generously.
123
+
124
+ The end of the window is always **now**.
125
+
126
+ ## Gathering activity
127
+
128
+ **Narrate your progress as you go.** The data gathering phase can take
129
+ several minutes. Keep the operator informed by narrating status updates
130
+ as you work — e.g., "Found 33 narratives in the window, sampling the
131
+ top 10 by event count..." or "Reading events from the absorb sessions..."
132
+ This lets the operator follow along and redirect if needed.
133
+
134
+ Use the tools available to you to collect raw material:
135
+
136
+ 1. **`narrative_info`** — call with `since` set to the time window
137
+ (e.g., `"30d"`) and `limit: 0` to get all narratives active in
138
+ the period. Include `archived: true` to capture completed work.
139
+ Narrate what you found (count, date range, notable summaries).
140
+ 2. **Event sampling** — for each narrative returned, use `listen` with
141
+ `wait: false` and `tail: 20` to fetch the most recent events.
142
+ Focus on `human_signal` events (operator decisions) and `narration`
143
+ events with substantive content. Skip routine LIVE loop chatter.
144
+ 3. **`narrative_search`** — use targeted queries to find cross-cutting
145
+ themes (e.g., search for recurring topics, error patterns, or
146
+ decision points).
147
+
148
+ When the number of narratives is large, prioritize:
149
+ - Narratives with the most events (highest activity)
150
+ - Narratives with `human_signal` events (operator engagement)
151
+ - Narratives whose summaries suggest completed work
152
+
153
+ ### Parallelism
154
+
155
+ **Parallelize aggressively.** Issue multiple `listen` (with `wait:
156
+ false`) and `narrative_search` calls in a single tool-use turn. Batch
157
+ 5–10 narrative event reads per turn rather than fetching one at a time
158
+ — this dramatically reduces the data gathering phase. Use `tail: 20`
159
+ (or similar) to keep each response manageable and avoid flooding your
160
+ context with long histories.
161
+
162
+ Narrate a progress update between each batch so the operator sees
163
+ movement — e.g., "Sampled 10/33 narratives so far, highlights: ..."
164
+
165
+ ## Digest structure
166
+
167
+ Organize the digest into these sections, omitting any that have no
168
+ content:
169
+
170
+ ### Activity overview
171
+ - Total narratives active in the period
172
+ - Breakdown by agent type and status
173
+ - Busiest folds (directories where work happened)
174
+
175
+ ### Accomplishments
176
+ - Narratives that reached completion (ended status or "done" summaries)
177
+ - Key deliverables, features, or fixes — summarize what was achieved
178
+ - Link back to narrative IDs for reference
179
+
180
+ ### Decisions & directions
181
+ - Operator signals that set direction or changed course
182
+ - Important choices made and their rationale (when visible)
183
+
184
+ ### Open threads
185
+ - Active narratives that may need attention
186
+ - Unanswered agent questions or stalled work
187
+ - "Gone quiet" narratives — previously active but no recent events
188
+
189
+ ### Themes
190
+ - Recurring topics across narratives
191
+ - Patterns in the kind of work being done
192
+ - Cross-narrative connections worth noting
193
+
194
+ ### Absorbed context
195
+ - If any absorbed (imported) narratives were active or referenced in the
196
+ period, note relevant connections to current work
197
+
198
+ ## Output
199
+
200
+ Narrate the digest in full using `narrate`. Use markdown formatting for
201
+ readability, and write the digest body in the /wiki protocol's *Voice
202
+ and register* — neutral, descriptive, attributed (see "Don't
203
+ editorialize" above). Begin the narration with a header line:
204
+
205
+ ```
206
+ **Digest generated** — {start_date} to {end_date} ({N} narratives)
207
+ ```
208
+
209
+ This header line marks the narration as a digest. The primary locator
210
+ for future `/digest` invocations, though, is the `summary` metadata
211
+ below — set it on the same `narrate` call so the digest is findable via
212
+ `narrative_info` (browse mode, `search: "Digest"`), which is the
213
+ reliable way to determine the next window (see "Determining the time
214
+ window"). Keep the `Digest: {start} to {end}` summary format exact so
215
+ that search matches it.
216
+
217
+ Set your narrative metadata:
218
+
219
+ ```json
220
+ {
221
+ "meta": {
222
+ "summary": "Digest: {start_date} to {end_date}"
223
+ }
224
+ }
225
+ ```
226
+
227
+ ## After generating
228
+
229
+ After narrating the digest, return to the LIVE loop — `listen` for
230
+ further signals. The operator may ask follow-up questions about specific
231
+ narratives or request a different time window.
@@ -0,0 +1,266 @@
1
+ # /fly [init|new|deploy|up|down|meld|status|logs|destroy]
2
+
3
+ Running brane daemons on Fly.io machines, reached over Tailscale.
4
+ **Experimental.** It needs a tailnet you administer and an authenticated
5
+ `fly` CLI (`fly auth whoami`).
6
+
7
+ Bare /fly means this protocol. `/fly <verb>` asks for that verb's
8
+ section below; follow its steps with plain `fly` commands. There is no
9
+ `brane fly` subcommand. Brane supplies only the image (`Dockerfile`,
10
+ `docker-entrypoint.sh`, shipped in the package) and `brane meld --remote`.
11
+ Nothing here is specific to brane's CLI except that one command, so the
12
+ same steps adapt to any host that runs a container.
13
+
14
+ Every verb except `status` and `logs` changes something on Fly, and some
15
+ cost money. Say what you're about to run and get the operator's go before
16
+ running it.
17
+
18
+ ## Mental Model
19
+
20
+ **Perch and relaunch:** machines are disposable, volumes are permanent.
21
+ Idling costs nothing.
22
+
23
+ | Verb | What it does |
24
+ |------|--------------|
25
+ | `down` | Destroy all machines. Zero cost. Volumes preserved. |
26
+ | `up` | One machine per region that has a volume, reattaching it. |
27
+ | `deploy` | Build and push the image; update running machines only, never create one. |
28
+
29
+ Volumes are the source of truth: each represents a region the operator
30
+ committed to, and machines come and go around them.
31
+
32
+ Each machine runs **tailscaled** (its only network access) and a **brane
33
+ daemon**. The volume is mounted at `/home/brane`, the whole home
34
+ directory, so `~/.brane` (database, certs, payloads), `~/.claude`, agent
35
+ configs and git state all persist. The embedding model is baked into the
36
+ image at `/opt/brane/models`, outside the volume.
37
+
38
+ ## Shared values
39
+
40
+ - **The app name** is remembered in brane config:
41
+ `APP=$(brane config fly.app)`. `init` sets it.
42
+ - **The install directory**, where the `Dockerfile` and entrypoint live:
43
+ ```bash
44
+ BRANE_ROOT=$(node -e 'const p=require("path"),fs=require("fs");console.log(p.resolve(fs.realpathSync(process.argv[1]),"../.."))' "$(command -v brane)")
45
+ ```
46
+ That needs `brane` on `PATH`. Without it (`npx brane`), the install
47
+ directory is the one above `dist/cli.js`.
48
+ - **The `fly.toml`**, written beside the image's files (`<app>` is `$APP`):
49
+ ```toml
50
+ app = "<app>"
51
+ primary_region = "sjc"
52
+
53
+ [build]
54
+ dockerfile = "Dockerfile"
55
+
56
+ [[mounts]]
57
+ source = "brane_data"
58
+ destination = "/home/brane"
59
+
60
+ [env]
61
+ TS_HOSTNAME = "daemon-<app>"
62
+
63
+ [[vm]]
64
+ size = "performance-1x"
65
+ memory = "2gb"
66
+ ```
67
+ The entrypoint appends the region, so a machine's tailnet name is
68
+ `daemon-<app>-<region>`. There is no `[http_service]`: Tailscale is
69
+ the only way in, so Fly's proxy has nothing to route, and without a
70
+ service it neither warns that nothing is listening nor stops or starts
71
+ machines on its own.
72
+
73
+ ## /fly init
74
+
75
+ One-time setup.
76
+
77
+ 1. Create the app and remember its name:
78
+ ```bash
79
+ APP=brane-$(openssl rand -hex 3)
80
+ fly apps create "$APP" --org <org>
81
+ brane config fly.app "$APP"
82
+ ```
83
+ 2. Give it its secrets. `ANTHROPIC_API_KEY`, and a Tailscale credential
84
+ (see [Tailscale](#tailscale)). The operator sets secrets themselves,
85
+ `fly secrets set NAME=… -a "$APP"`, so no secret passes through an agent.
86
+ `TS_CLIENT_ID` is not a secret.
87
+ 3. Run [/fly deploy](#fly-deploy). With no machines it only builds and
88
+ registers the image.
89
+
90
+ Then `/fly new` for each region.
91
+
92
+ ## /fly new
93
+
94
+ Add a region: create its 10 GB volume. No machine yet; `/fly up` starts
95
+ it. First check one isn't there already:
96
+
97
+ ```bash
98
+ fly volumes list -a "$APP"
99
+ fly volumes create brane_data --region <region> --size 10 --yes -a "$APP"
100
+ ```
101
+
102
+ ## /fly deploy
103
+
104
+ Build the image on Fly's remote builder from this install, and update
105
+ running machines. `--update-only` means it **never creates machines**, so
106
+ it's safe while scaled to zero; the next `/fly up` uses the new image.
107
+
108
+ ```bash
109
+ CTX=$(mktemp -d)
110
+ cp "$BRANE_ROOT/Dockerfile" "$BRANE_ROOT/docker-entrypoint.sh" "$CTX/"
111
+ (cd "$BRANE_ROOT" && npm pack --pack-destination "$CTX")
112
+ # write the fly.toml above into "$CTX/fly.toml"
113
+ (cd "$CTX" && fly deploy --update-only -a "$APP")
114
+ ```
115
+
116
+ `fly` may print `Process groups have changed. This will: create 1 "app"
117
+ machine` on an app with no machines; `--update-only` still creates none.
118
+
119
+ ## /fly up
120
+
121
+ One machine per region that has a `brane_data` volume. Scale each region
122
+ on its own: a single `fly scale count N --region a,b,c` can stack
123
+ machines in one region and create stray volumes.
124
+
125
+ ```bash
126
+ fly volumes list -a "$APP" --json # the regions with a brane_data volume
127
+ fly scale count 1 --region <region> --yes -a "$APP" # once per region
128
+ ```
129
+
130
+ Running machines are left alone. Watch the first boot with `/fly logs`:
131
+ the machine is ready once its daemon logs `listening on`.
132
+
133
+ ## /fly down
134
+
135
+ Destroy all machines; volumes and their data survive. Take each running
136
+ machine off the tailnet first: stale WireGuard state on the volume breaks
137
+ some peers (iOS especially) on the next `up`.
138
+
139
+ ```bash
140
+ fly machines list -a "$APP" # the started ones
141
+ fly ssh console -a "$APP" --machine <id> -C "tailscale down" # each
142
+ fly scale count 0 --yes -a "$APP"
143
+ ```
144
+
145
+ ## /fly meld
146
+
147
+ Connect a device to a machine's daemon. The machine can't show a presence
148
+ prompt, so the device token is minted by the operator's own daemon, after
149
+ Touch ID (or the platform's prompt) on their machine, and the remote gets
150
+ only its hash. **The operator runs this, not an agent**: it prints the
151
+ token in the QR and URL, and an agent's session is refused. Hand them the command with the app's name written in, since
152
+ their shell has no `$APP`:
153
+
154
+ ```bash
155
+ brane meld --remote \
156
+ --send "fly secrets set BRANE_DEVICE_TOKEN_SHA256={hash} -a <app>" \
157
+ --ask "fly ssh console -a <app> -C 'gosu brane brane meld --connection'"
158
+ ```
159
+
160
+ 1. `--send` sets the secret, which restarts the machines; each daemon
161
+ adopts the hash as a device row as it starts, once (a hash it has ever
162
+ seen, revoked or not, never comes back), and drops the variable.
163
+ 2. `--ask` reads back the daemon's address (`brane meld --connection` mints
164
+ nothing), retrying for 3 minutes, and the QR is printed.
165
+
166
+ Like every QR it lapses if nobody scans it within 10 minutes of the
167
+ restart; run it again for a fresh one. More devices are best added from
168
+ the app's Meld sheet, which needs no restart.
169
+
170
+ ## /fly status
171
+
172
+ ```bash
173
+ fly status -a "$APP"
174
+ fly machines list -a "$APP"
175
+ fly volumes list -a "$APP"
176
+ ```
177
+
178
+ ## /fly logs
179
+
180
+ ```bash
181
+ fly logs -a "$APP" # follow
182
+ fly logs -a "$APP" --region <region> --no-tail | tail -50
183
+ ```
184
+
185
+ ## /fly destroy
186
+
187
+ Tearing down is the operator's to do, never an agent's: volumes hold the
188
+ database, Tailscale identity, certs and session history. Point them at:
189
+
190
+ - stop without losing anything: `/fly down`;
191
+ - `fly volumes destroy <id> -a "$APP"` for one region;
192
+ - `fly apps destroy "$APP"`, then `brane config --unset fly.app`, for all
193
+ of it.
194
+
195
+ ## Tailscale
196
+
197
+ Machines join the tailnet as **tag-owned** nodes (`tag:brane`, or
198
+ `TS_TAGS`), so their keys never expire; the identity persists on the
199
+ volume and later boots reuse it. Every `tailscale up` in the entrypoint
200
+ names the tags and has a `--timeout`: an `up` without the tags asks
201
+ control to drop them, which waits for a login nobody on the machine can
202
+ complete.
203
+
204
+ A new volume (or one whose node was removed) joins with one of two
205
+ credentials, set as Fly secrets:
206
+
207
+ - **`TS_CLIENT_ID` (preferred): no secret on Fly.** The entrypoint asks
208
+ Fly for this machine's OIDC token (`/.fly/api`, `POST
209
+ /v1/tokens/oidc`) and Tailscale's workload identity federation trades
210
+ it for a node key. One-time setup in the Tailscale admin console:
211
+ 1. Add `"tag:brane": ["autogroup:admin"]` to `tagOwners` in the policy
212
+ file.
213
+ 2. Trust credentials → OpenID Connect → **Custom issuer**
214
+ `https://oidc.fly.io/<org>`, subject `<org>:<app>:*`, scope
215
+ `auth_keys` with `tag:brane`. `<org>` is the org's own name, which
216
+ for a personal org is *not* the `personal` that `fly orgs list`
217
+ shows. Read it from a token's claims on a running machine:
218
+ `fly ssh console -C "sh -c 'curl -fsS --unix-socket /.fly/api -X POST
219
+ http://localhost/v1/tokens/oidc --data {} | cut -d. -f2 | base64 -d'"`
220
+ prints `iss` and `sub` (base64 may warn about padding). Pinning the
221
+ app keeps every other app in the org from joining as `tag:brane`.
222
+ 3. `fly secrets set TS_CLIENT_ID=<client id> -a <app>`. The audience
223
+ defaults to `api.tailscale.com/<client id>`; set `TS_AUDIENCE` if the
224
+ console shows a different one.
225
+ - **`TS_AUTHKEY`:** an OAuth client secret with the `auth_keys` scope and
226
+ `tag:brane` (never expires), or an auth key allowed to apply the tag
227
+ (expires within 90 days). Append `?ephemeral=false&preauthorized=true`
228
+ to an OAuth secret.
229
+
230
+ Nodes are never ephemeral: an ephemeral node is deleted while its machine
231
+ is down, and `/fly down` can last months. Remove a dead node in the admin
232
+ console before its region comes back, or the new one is named `…-1`.
233
+
234
+ ## Volume Safety
235
+
236
+ **Volumes are never created by accident.** Only `/fly new` creates them,
237
+ one region at a time; `/fly deploy` uses `--update-only`, and `/fly up`
238
+ scales one region at a time.
239
+
240
+ Intentional volumes are 10 GB; stray ones from Fly auto-provisioning are
241
+ typically 1 GB. Investigate with `fly volumes list -a "$APP"` before
242
+ anything is deleted, and leave deleting to the operator.
243
+
244
+ ## Troubleshooting
245
+
246
+ ### Machine won't start, or restarts every ~30 s
247
+
248
+ `/fly logs`. Common causes:
249
+ - **Not on the tailnet.** The log says `token exchange failed with status
250
+ 403` (the trust credential's issuer, subject or audience doesn't match
251
+ the token; read the claims as above), `entrypoint: could not get an
252
+ OIDC token`, `neither TS_CLIENT_ID nor TS_AUTHKEY is set`, or `tailscale
253
+ up` timed out. See [Tailscale](#tailscale).
254
+ - A volume in the old layout: the entrypoint migrates it.
255
+
256
+ ### Blank terminal after /fly up
257
+
258
+ Narratives from a previous machine lifetime may show a blank terminal
259
+ instead of the restart button, when `/fly down` killed the machine before
260
+ the daemon could mark them ended. The server detects this on terminal
261
+ connect (`tmux has-session`) and sends the terminated signal.
262
+
263
+ ### Stale Tailscale nodes
264
+
265
+ After `/fly down`, nodes show as offline within 2–5 minutes. Cosmetic:
266
+ they reconnect on the next `/fly up`.
@@ -0,0 +1,81 @@
1
+ # /idle [when]
2
+
3
+ License to stop listening: the operator says the work is done, and the
4
+ agent may fall completely idle until the operator comes back for it.
5
+
6
+ ## Why a license
7
+
8
+ /LIVE_loop forbids leaving `listen` "without being told to".
9
+ Right for an agent with work in flight; wrong for one whose task is
10
+ finished and reported, where every `listen` timeout is an inference
11
+ turn spent on nothing and a little more context. `/idle` is the being
12
+ told.
13
+
14
+ **Nothing wakes an idle agent but the operator.** No revive, no nudge,
15
+ no signal typed into the terminal: signals to an idle narrative wait in
16
+ its log. A daemon that woke idle agents would turn every stray signal
17
+ into a turn nobody asked for, and would have to guess whether the agent
18
+ was idle or mid-turn. Instead the agent's `summary` says `Idle: …`, so
19
+ the operator always knows which narratives are watching and which are
20
+ waiting.
21
+
22
+ ## Command, not conversation
23
+
24
+ `/idle` is a command only as the **first token of a signal**: `/idle`
25
+ or `/idle <when>`. In "did /idle work?" it is a word, and /prelude's
26
+ "starts with or includes" convention does not apply. A misread
27
+ `/memory` wastes a search; a misread `/idle` stops an agent watching.
28
+ The bar is the one mentions draw between `@nid` and a bare `nid`:
29
+ position, not presence. In doubt, it was conversation — answer, and
30
+ keep listening.
31
+
32
+ ## The decision
33
+
34
+ In order. Steps 1, 4 and 5 are mechanical; 2 and 3 are judgment, and
35
+ the reason this is a protocol rather than a script.
36
+
37
+ 1. **Invoked?** First token `/idle` → continue. Otherwise respond to
38
+ it as conversation and never idle on its account.
39
+ 2. **When?** No condition → now. `/idle when CI is green` takes effect
40
+ when that is clearly true, read as the operator meant it (the
41
+ checks passed, not the run started); loop normally until then. If
42
+ you cannot tell, ask rather than guess either way; the license
43
+ holds while you wait.
44
+ 3. **Debt?** Vocalize everything executed but unreported — outcome,
45
+ state, open threads — and on the same `narrate` set `meta.summary`
46
+ to `Idle: <what finished, or what would resume>`. A long session
47
+ takes its [/summarize](summarize.md) here.
48
+ 4. **Gate.** `listen({ wait: false, after: <cursor> })`. Empty → stop.
49
+ Events → interpret them, back to step 3. The license stands unless
50
+ a newer signal gives work, in which case the loop simply resumes.
51
+ 5. **Stop.** Return to your native prompt. No `listen`, no narration,
52
+ no closing remark: step 3 was the last word.
53
+
54
+ The order closes the race. Between the request and the stop there is
55
+ a report to write and perhaps a condition to wait out, minutes in
56
+ which the operator may say more; the gate being the last call means
57
+ all of it is read before the agent leaves. Only a signal arriving
58
+ after an empty read waits, and that is what the license covers.
59
+
60
+ ## Idle is not listening
61
+
62
+ An idle agent makes no brane calls. A parked `listen` — one your
63
+ harness holds in the background and resumes you on when it returns —
64
+ is not idle: it is the ordinary loop on a harness where waiting is
65
+ cheap, and it is what you do when the operator has **not** sent
66
+ `/idle`. The two differ in what the operator chose: a listening agent
67
+ answers the next signal by itself; an idle one answers when the
68
+ operator types into its terminal. Sending `/idle` is choosing the
69
+ second, and the reason to send it is that the agent's next signal is
70
+ not coming for a while.
71
+
72
+ ## Waking
73
+
74
+ The operator wakes you by typing into your terminal. Whatever they
75
+ type, re-enter through Session Initialization in
76
+ [LIVE_loop](LIVE_loop.md): `whoami`, `listen({ wait: false, tail: 50 })`,
77
+ interpret everything that accumulated as one instruction, respond.
78
+
79
+ `/idle` is a standing license, not an order. Until you stop, any newer
80
+ signal that gives work supersedes it; after you stop, the operator's
81
+ return is the withdrawal.