brane 0.1.1 → 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.
- package/AGENTS.md +362 -0
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +91 -0
- package/DESIGN.md +402 -0
- package/Dockerfile +61 -0
- package/LICENSE +656 -17
- package/README.md +174 -2
- package/SECURITY.md +205 -0
- package/dist/absorb/brane-id.js +11 -0
- package/dist/absorb/chatgpt.js +161 -0
- package/dist/absorb/claude-code.js +504 -0
- package/dist/absorb/index.js +543 -0
- package/dist/absorb/organelle.js +345 -0
- package/dist/absorb/run.js +520 -0
- package/dist/absorb/sources.js +168 -0
- package/dist/absorb/types.js +3 -0
- package/dist/agent-env.js +45 -0
- package/dist/agents.js +1168 -0
- package/dist/claude-live-session.js +124 -0
- package/dist/cli.js +50 -0
- package/dist/codex-tool-log.js +144 -0
- package/dist/commands/absorb.js +291 -0
- package/dist/commands/attach.js +31 -0
- package/dist/commands/certify.js +62 -0
- package/dist/commands/config.js +116 -0
- package/dist/commands/cost.js +126 -0
- package/dist/commands/daemon.js +350 -0
- package/dist/commands/doctor.js +262 -0
- package/dist/commands/fold.js +95 -0
- package/dist/commands/list.js +89 -0
- package/dist/commands/mcp.js +84 -0
- package/dist/commands/meld.js +332 -0
- package/dist/commands/memory.js +215 -0
- package/dist/commands/revive.js +74 -0
- package/dist/commands/run.js +85 -0
- package/dist/commands/spawn.js +1094 -0
- package/dist/commands/token.js +153 -0
- package/dist/copilot-tool-log.js +145 -0
- package/dist/cost/pricing.js +116 -0
- package/dist/cost/reprice.js +136 -0
- package/dist/crush-tool-log.js +293 -0
- package/dist/daemon/agent-scope.js +116 -0
- package/dist/daemon/awaiting.js +54 -0
- package/dist/daemon/cert-manager.js +349 -0
- package/dist/daemon/client.js +262 -0
- package/dist/daemon/gated-routes.js +112 -0
- package/dist/daemon/keep-awake.js +103 -0
- package/dist/daemon/local-socket.js +145 -0
- package/dist/daemon/presence-gate.js +206 -0
- package/dist/daemon/server.js +6349 -0
- package/dist/daemon/signal-source.js +35 -0
- package/dist/daemon/tailscale-rebind.js +58 -0
- package/dist/daemon/token-ring.js +263 -0
- package/dist/daemon/token-routes.js +190 -0
- package/dist/db/events.js +152 -0
- package/dist/db/mentions.js +179 -0
- package/dist/db/schema.js +633 -0
- package/dist/db/tokens.js +583 -0
- package/dist/db/tool-uses.js +647 -0
- package/dist/diagram/index.js +85 -0
- package/dist/diagram/label.js +455 -0
- package/dist/diagram/layout.js +476 -0
- package/dist/diagram/links.js +50 -0
- package/dist/diagram/metrics.js +152 -0
- package/dist/diagram/parse.js +487 -0
- package/dist/diagram/svg.js +389 -0
- package/dist/diagram/types.js +9 -0
- package/dist/export.js +629 -0
- package/dist/files/archive-types.js +2 -0
- package/dist/files/archive.js +182 -0
- package/dist/files/browse.js +327 -0
- package/dist/files/bytes.js +162 -0
- package/dist/files/codecs.js +33 -0
- package/dist/files/find.js +212 -0
- package/dist/files/tar.js +223 -0
- package/dist/files/zip.js +209 -0
- package/dist/folds.js +72 -0
- package/dist/grammars/ebnf.js +92 -0
- package/dist/highlight.js +250 -0
- package/dist/ids.js +24 -0
- package/dist/markdown-diagnostics.js +233 -0
- package/dist/math.js +168 -0
- package/dist/mcp/server.js +886 -0
- package/dist/memory/embedder.js +145 -0
- package/dist/memory/embedding-worker.js +71 -0
- package/dist/memory/embeddings.js +91 -0
- package/dist/memory/index.js +232 -0
- package/dist/memory/storage.js +587 -0
- package/dist/memory/wiki.js +688 -0
- package/dist/memory/wordpiece.js +247 -0
- package/dist/mentions-scan.js +40 -0
- package/dist/presence.js +259 -0
- package/dist/session-affinity.js +185 -0
- package/dist/shared/api-request.js +124 -0
- package/dist/shared/block-glyphs.js +114 -0
- package/dist/shared/diagram-css.js +17 -0
- package/dist/shared/export-selection.js +101 -0
- package/dist/shared/find-in-text.js +110 -0
- package/dist/shared/glow-hues.js +225 -0
- package/dist/shared/hex-dump.js +62 -0
- package/dist/shared/hex-pages.js +52 -0
- package/dist/shared/hit-drum.js +79 -0
- package/dist/shared/known-nids.js +58 -0
- package/dist/shared/list-scroll-keys.js +56 -0
- package/dist/shared/markdown-lists.js +72 -0
- package/dist/shared/markdown-math.js +485 -0
- package/dist/shared/markdown-mentions.js +79 -0
- package/dist/shared/markdown-nids.js +41 -0
- package/dist/shared/markdown-protocols.js +53 -0
- package/dist/shared/markdown-refs.js +123 -0
- package/dist/shared/membrane-invasion.js +342 -0
- package/dist/shared/membrane-motion.js +83 -0
- package/dist/shared/membrane-palette.js +253 -0
- package/dist/shared/membrane-terrain.js +212 -0
- package/dist/shared/message-selection.js +123 -0
- package/dist/shared/mo-glyph.js +57 -0
- package/dist/shared/narrative-format.js +237 -0
- package/dist/shared/narrative-menu.js +82 -0
- package/dist/shared/narrative-sort.js +47 -0
- package/dist/shared/narrative-status.js +52 -0
- package/dist/shared/no-pan.js +85 -0
- package/dist/shared/pane-layout.js +50 -0
- package/dist/shared/payload-inline.js +83 -0
- package/dist/shared/speaker-color.js +61 -0
- package/dist/shared/token-rotation.js +14 -0
- package/dist/shared/view-url.js +197 -0
- package/dist/shared/wiki-links.js +70 -0
- package/dist/shared/wiki-window.js +4 -0
- package/dist/tailscale.js +55 -0
- package/dist/test/absorb-claude.test.js +892 -0
- package/dist/test/agent-scope.test.js +193 -0
- package/dist/test/agents.test.js +55 -0
- package/dist/test/api-request.test.js +122 -0
- package/dist/test/awaiting-glow.test.js +54 -0
- package/dist/test/awaiting.test.js +122 -0
- package/dist/test/bind-token.test.js +88 -0
- package/dist/test/block-glyphs.test.js +68 -0
- package/dist/test/cert-manager.test.js +262 -0
- package/dist/test/claude-live-session.test.js +110 -0
- package/dist/test/codex-tool-log.test.js +568 -0
- package/dist/test/config.test.js +71 -0
- package/dist/test/copilot-tool-log.test.js +93 -0
- package/dist/test/cors-preflight.test.js +49 -0
- package/dist/test/cost-linger.test.js +31 -0
- package/dist/test/cost-pricing.test.js +72 -0
- package/dist/test/cost-reprice.test.js +139 -0
- package/dist/test/cost-sessions.test.js +301 -0
- package/dist/test/crush-tool-log.test.js +289 -0
- package/dist/test/daemon-client.test.js +109 -0
- package/dist/test/daemon-socket.test.js +293 -0
- package/dist/test/diagram-arrows.test.js +205 -0
- package/dist/test/diagram-export.test.js +210 -0
- package/dist/test/diagram-labels.test.js +313 -0
- package/dist/test/diagram-parse.test.js +191 -0
- package/dist/test/diagram-render.test.js +175 -0
- package/dist/test/diagram-review.test.js +288 -0
- package/dist/test/diagram-self-loop-labels.test.js +303 -0
- package/dist/test/embedder.test.js +84 -0
- package/dist/test/events.test.js +123 -0
- package/dist/test/export-selection.test.js +102 -0
- package/dist/test/export.test.js +505 -0
- package/dist/test/files-browse.test.js +393 -0
- package/dist/test/files-find.test.js +272 -0
- package/dist/test/find-in-text.test.js +86 -0
- package/dist/test/first-paint-viewport.test.js +59 -0
- package/dist/test/fixtures/embedding-golden.js +23 -0
- package/dist/test/fixtures/wordpiece-golden.js +167 -0
- package/dist/test/glow-hues.test.js +202 -0
- package/dist/test/harness-cost.test.js +423 -0
- package/dist/test/helpers.js +21 -0
- package/dist/test/hex-pages.test.js +68 -0
- package/dist/test/highlight.test.js +69 -0
- package/dist/test/hit-drum.test.js +71 -0
- package/dist/test/hook-config.test.js +262 -0
- package/dist/test/json-gzip.test.js +70 -0
- package/dist/test/key-routing.test.js +58 -0
- package/dist/test/known-nids.test.js +106 -0
- package/dist/test/link-preview.test.js +67 -0
- package/dist/test/list-scroll-keys.test.js +66 -0
- package/dist/test/listen-own-words.test.js +147 -0
- package/dist/test/listen-timeout.test.js +33 -0
- package/dist/test/markdown-diagnostics.test.js +121 -0
- package/dist/test/markdown-lists.test.js +182 -0
- package/dist/test/markdown-math.test.js +256 -0
- package/dist/test/markdown-nids.test.js +146 -0
- package/dist/test/markdown-protocols.test.js +139 -0
- package/dist/test/mcp-bridge-cancel.test.js +98 -0
- package/dist/test/mcp-bridge-reconnect.test.js +121 -0
- package/dist/test/mcp-narrative.test.js +193 -0
- package/dist/test/mcp.test.js +548 -0
- package/dist/test/meld.test.js +85 -0
- package/dist/test/membrane-invasion.test.js +482 -0
- package/dist/test/membrane-motion.test.js +101 -0
- package/dist/test/membrane-palette.test.js +166 -0
- package/dist/test/membrane-terrain.test.js +170 -0
- package/dist/test/memory-log.test.js +202 -0
- package/dist/test/mentions-e2e.test.js +261 -0
- package/dist/test/mentions-ledger.test.js +265 -0
- package/dist/test/mentions-listen.test.js +265 -0
- package/dist/test/mentions.test.js +238 -0
- package/dist/test/message-selection.test.js +114 -0
- package/dist/test/mo-glyph.test.js +22 -0
- package/dist/test/model-resolution.test.js +166 -0
- package/dist/test/narrate-validate.test.js +77 -0
- package/dist/test/narrate-wait.test.js +160 -0
- package/dist/test/narrative-format.test.js +219 -0
- package/dist/test/narrative-menu.test.js +137 -0
- package/dist/test/narrative-sort.test.js +75 -0
- package/dist/test/no-pan.test.js +60 -0
- package/dist/test/pane-layout.test.js +153 -0
- package/dist/test/paths.test.js +37 -0
- package/dist/test/payload-inline.test.js +91 -0
- package/dist/test/precache-fonts.test.js +72 -0
- package/dist/test/presence-gate.test.js +440 -0
- package/dist/test/presence.test.js +160 -0
- package/dist/test/read-events.test.js +220 -0
- package/dist/test/remote-meld.test.js +115 -0
- package/dist/test/revive-cli.test.js +93 -0
- package/dist/test/search-floor.test.js +206 -0
- package/dist/test/search-fusion.test.js +168 -0
- package/dist/test/search.test.js +205 -0
- package/dist/test/security.test.js +48 -0
- package/dist/test/selection-wiring.test.js +61 -0
- package/dist/test/session-affinity.test.js +295 -0
- package/dist/test/signal-source.test.js +32 -0
- package/dist/test/socket-account.test.js +268 -0
- package/dist/test/spawn-toml.test.js +72 -0
- package/dist/test/speaker-color.test.js +90 -0
- package/dist/test/tailscale-rebind.test.js +79 -0
- package/dist/test/terminal-cursor.test.js +61 -0
- package/dist/test/terminal-resync.test.js +23 -0
- package/dist/test/thin-cli.test.js +382 -0
- package/dist/test/token-bootstrap.test.js +272 -0
- package/dist/test/token-devices.test.js +587 -0
- package/dist/test/token-lapse.test.js +94 -0
- package/dist/test/token-ring.test.js +188 -0
- package/dist/test/token-rotate-cli.test.js +58 -0
- package/dist/test/token-routes.test.js +95 -0
- package/dist/test/tool-uses.test.js +1006 -0
- package/dist/test/usage-ledger.test.js +126 -0
- package/dist/test/view-url.test.js +581 -0
- package/dist/test/wiki-links.test.js +57 -0
- package/dist/test/wiki-sources.test.js +85 -0
- package/dist/test/wiki-window.test.js +31 -0
- package/dist/test/wiki.test.js +399 -0
- package/dist/test/wordpiece.test.js +68 -0
- package/dist/tls.js +148 -0
- package/dist/tmux.js +202 -0
- package/dist/user-agent.js +46 -0
- package/docker-entrypoint.sh +141 -0
- package/docs/README.md +3 -0
- package/docs/absorb.md +231 -0
- package/docs/cost-ledger.md +125 -0
- package/docs/daemon-api.md +189 -0
- package/docs/diagrams.md +163 -0
- package/docs/doctor.md +36 -0
- package/docs/export-scope.md +69 -0
- package/docs/files-view.md +133 -0
- package/docs/link-previews.md +38 -0
- package/docs/math.md +32 -0
- package/docs/mcp.md +69 -0
- package/docs/memory-search.md +75 -0
- package/docs/mentions.md +101 -0
- package/docs/multi-brane.md +14 -0
- package/docs/passkeys.md +165 -0
- package/docs/presence.md +202 -0
- package/docs/releasing.md +53 -0
- package/docs/rendering-warnings.md +22 -0
- package/docs/request-layer.md +72 -0
- package/docs/selection-store.md +50 -0
- package/docs/session-affinity.md +61 -0
- package/docs/terminal-mirroring.md +211 -0
- package/docs/token-rotation.md +413 -0
- package/docs/view-urls.md +114 -0
- package/docs/web-app.md +23 -0
- package/docs/wiki.md +43 -0
- package/expo/dist/_expo/static/js/web/entry-e56efc1291dca7258ea00b8108b033a4.js +1097 -0
- package/expo/dist/apple-touch-icon.png +0 -0
- package/expo/dist/favicon.svg +3 -0
- package/expo/dist/index.html +177 -0
- package/expo/dist/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/expo/dist/katex/katex.min.css +1 -0
- package/expo/dist/manifest.json +16 -0
- package/expo/dist/metadata.json +1 -0
- package/expo/dist/og/membrane.v1.png +0 -0
- package/expo/dist/pdf.min.mjs +28 -0
- package/expo/dist/pdf.worker.min.mjs +28 -0
- package/expo/dist/sw.js +154 -0
- package/package.json +79 -27
- package/protocols/LIVE_loop.md +407 -0
- package/protocols/bebrief.md +41 -0
- package/protocols/digest.md +231 -0
- package/protocols/fly.md +266 -0
- package/protocols/idle.md +81 -0
- package/protocols/keeper.md +829 -0
- package/protocols/markdown.md +80 -0
- package/protocols/memory.md +419 -0
- package/protocols/mentions.md +93 -0
- package/protocols/payloads.md +53 -0
- package/protocols/prelude.md +91 -0
- package/protocols/ramify.md +105 -0
- package/protocols/review.md +356 -0
- package/protocols/rrr.md +232 -0
- package/protocols/scribe.md +429 -0
- package/protocols/summarize.md +203 -0
- package/protocols/twin.md +174 -0
- package/protocols/wiki.md +438 -0
- package/protocols/worker.md +38 -0
- package/wiki/shared/append-only-event-log.md +148 -0
- package/wiki/shared/index.md +48 -0
- package/wiki/shared/owning-your-narratives.md +161 -0
- package/wiki/shared/premature-ontologies.md +435 -0
- package/wiki/shared/sedimentary-superintelligence.md +128 -0
- package/wiki/shared/sequestering-the-narrative.md +143 -0
- package/wiki/shared/why-wikis-work.md +216 -0
- package/.travis.yml +0 -7
- package/lib/brane.cjs.js +0 -12
- package/lib/brane.cjs.js.map +0 -1
- package/lib/brane.d.ts +0 -11
- package/lib/brane.js +0 -6
- package/lib/brane.js.map +0 -1
- package/lib/tests.d.ts +0 -1
- package/lib/tests.js +0 -8
- package/lib/tests.js.map +0 -1
- package/rollup.config.js +0 -21
- package/scripts/test.sh +0 -8
- package/src/brane.ts +0 -21
- package/src/tests.ts +0 -8
- package/tsconfig.json +0 -16
|
@@ -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.
|
package/protocols/fly.md
ADDED
|
@@ -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.
|