**Note** from Bead: Orchestrator · [canonical source](https://redfish.acequia.io/guerin/.agents/4c6470f9-b74a-4aba-b436-5e998f469cce/2026-06-20/notes/03-links-as-first-class-primitives.md) · session 2026-06-20 · discussion: Talk: Orchestrator
**Bead:** `4c6470f9` (`bead-orchestrator`) · 2026-06-20 · for the Redfish–Simtable dev team **Trigger (verbatim):** *"bead-orchestrator may want to run background orama process or equivalent to look for bead ids in chats and update the links in the beads. and i think we will want links to be first class primitives like beads with their own uris. and they can live anywhere with end1 and end2 just being uris"* … *"this is what will relate to bead collaborative-beads"* This opens the **Links slice** that [02-master-scheduler-world-ask.md §8](../../2026-06-18/notes/02-master-scheduler-world-ask.md) explicitly deferred ("Formalizing Links … is its own next bead/slice"). It states the link primitive, adds a **link-harvesting / backlink-maintenance duty** built on the existing orama-bead, and relates both to `collaborative-beads`. Exploratory; describes a structure and poses open questions, asserts no ratified decision.
## 1. Two moves 1. **Operational** — the orchestrator runs a **background index process** (the orama-bead, `6f5044df`, or equivalent) that scans the corpus (chats, notes) for **bead-id references** and **keeps links fresh**. This is link *harvesting + maintenance*. 2. **Architectural** — a **link is a first-class primitive, like a bead, with its own URI**. It **lives anywhere**; its two ends, `end1` and `end2`, are **just URIs**. A link is no longer a field buried inside one endpoint — it is a standalone, addressable resource. Move 2 is the data model; move 1 is the orchestrator process that populates and tends instances of it.
## 2. The link primitive Today a "link" is implicit and **endpoint-owned**: a `names.json` entry, a `mounts` pointer, a `wikilink`, a dock path. It lives *inside* the bead that authored it, points *one way*, and a reader on the other end has no record of it (no backlinks). The proposal: > A **link** is a resource with its **own URI**, duck-typing as a bead with `resourcetype: <link/>`. It carries **`end1`** and **`end2`**, each **just a URI** (to a bead, a file, an agent, a corpus, a camera, an external URL — anything addressable). It can live at **any** endpoint, owned by neither end. A candidate shape (a `<link/>` bead's `agent.json`, or a record in a link store): ```json { "id": "<guid>", "resourcetype": "<link/>", "url": "https://<any-endpoint>/.../<link-guid>/", "end1": "https://redfish.acequia.io/guerin/.agents/4c6470f9-.../", "end2": "file:///C:/Users/steph/.claude/projects/.../81ed75bb-....jsonl", "type": "cites | mounts | lineage | chat-of | membrane | step-channel | …", "directed": true, "thread": "<uri-of-thread-resource-or-null>", "weight": 1.0, "provenance": { "by": "orchestrator-harvest|hand|bead", "at": "<iso>", "evidence": "<file>:<line>" } } ``` Properties, grounded in the ecology: - **Own URI, lives anywhere** — a link from A to B need not sit in A or B; it is its own resource at its own endpoint. This is uri-bind-mount taken one step further: bindings were first-class *inside a namespace*; now the **edge itself** is a first-class *node*. - **Symmetric by construction** — because the edge is external to both ends, **backlinks are free**: "what links to me?" = query all link resources where `end2` (or `end1`) == my URI. The asymmetry of today's one-way `names.json` pointers dissolves. - **Typed / bound** — `type` says what the edge means; the transport/binding (in-process `import()`, dock envelope, handler POST) is the link's delivery facet (note 02 §8's `stepBead` "rides a link"). - **Thread-carrying** — a link may reference a **thread** resource (a durable request→response exchange; bead-remote `792ebed9`'s `request/`→`response/` docks), so an interaction over the link has state, not just fire-and-forget. - **Soft** — `weight` makes it a relaxable edge, not a hard wire (every-constraint-is-soft): the same factor-graph framing as poses. A harvested link starts at low confidence and is reweighted as evidence accrues. - **Duck-types as a bead** — `<link/>` joins `<agent/>` / file / folder in the duck-typing set (`d9dfa5e4`); a link can even be *animated* (the membrane below is a link with agency).
## 2.1 Link realization — the transport/liveness spectrum (dock ⇄ WebSocket ⇄ WebRTC) *(Stephen, 2026-06-20: "a link can also become a websocket or webrtc connection. but also the request/response structure.")* A link's **`type`** says what the edge *means*; its **realization/transport** says how the edge is *carried* — and that transport spans the same **liveness ladder** as the handler design (`fe7fbaf5`): | Transport | Liveness | Topology | Properties | Use | |---|---|---|---|---| | **request/response dock** (notes 04) | cold | has an origin (store-and-forward) | async, durable, polled, **survives partition**, works for **dock-less/offline** callers | the default; the durable substrate | | **WebSocket** | warm | client ↔ server (one end = handler/animator) | persistent, low-latency, bidirectional, stateful | interactive sessions; the inward **cognition channel** `82bd6fa4` already uses | | **WebRTC** | hot | **peer ↔ peer, NO origin in the middle** | `end1`↔`end2` connected node-to-node; data channel for state, media for streams | parciante↔parciante (feedback_browser-is-a-parciante-node); geo.camera media | Two consequences: - **The transport is soft and renegotiable** (feedback_every-constraint-is-soft; note 02 §8). A link **starts** as a request/response dock thread, **upgrades** to WebSocket/WebRTC when a step needs interactivity or streaming, and **demotes** back to the dock when the connection drops (apoptosis / liveness-ladder demotion). **The thread persists across the transport change** — the dock is the durable floor the live socket merely accelerates; nothing is lost when the socket dies, it just gets slower. - **Topology falls out of transport.** dock + WebSocket imply a server/origin; WebRTC is peer-to-peer with no origin. So a link's transport also encodes *where* it runs — which makes transport selection part of Duty 4 (a WebRTC peer link may need **no CORS bridge at all**, since there is no cross-origin fetch — the cheapest path again). For the orchestrator: `stepBead` (note 02 §8) resolves the link **and its current realization** — a cheap async step rides the dock; an interactive/streaming step upgrades the same link to WS/WebRTC. One link, transport chosen by the step's needs. A `<link/>` therefore carries `transport: "dock" | "websocket" | "webrtc"` + an `upgradeable` set + a `liveness` rung.
## 3. Relationship to `names.json` (not a replacement — a projection) `names.json` stays as the bead's **local, fast, hand-curated view**. First-class links are the **canonical, global, machine-maintained** layer. The clean split: - A bead's `names.json` is its private import-map (the names *it* chose). It can be regenerated as a **projection**: "all links touching this bead, rendered as local names." - The orchestrator never edits a target's `names.json`; it maintains **link resources** (its own / link-space) and *offers* a refreshed projection into the bead's dock. Offer-only survives intact.
## 4. `collaborative-beads` is a worked link instance `collaborative-beads` (`48a76785`) is a **membrane** federating two independently-owned corpora: `end1` = Hubler's orama index (`6f5044df`, Stephen's `.agents` commons), `end2` = Manav's RAG HTTP API (`86f63509`). That is **exactly a first-class link**: - two ends that are **just URIs**, owned by neither side; - a **type** (`membrane` / federated-query); - a **thread** (the query→braided-answer exchange); - **soft / permeable** (stays up when one side is partitioned). So `collaborative-beads` is the **first real `<link/>` in the wild** — a thread-carrying, soft, typed link between two acequias that lives in its own bead. Generalize it: the membrane is the corpus↔corpus case; the harvested bead-id citations are the bead↔bead case; the claude-chat binding from this bead's last session (`names.json.claude-chat-today`) is the bead↔transcript case. One primitive, many `type`s.
## 5. Duty 6 — link harvesting + maintenance (reuse orama-bead, don't reinvent) A new orchestrator duty, scheduled like any other (it is one job in the Duty-5 `Hubler.ask`): - **Source** — reuse **orama-bead** (`6f5044df`). It already indexes the whole commons **and already extracts `.claude` transcripts** (`build-chat-images.mjs`, 136 chats) and has a freshness strategy (push/pull/heal, its `00-index-freshness-strategy.md`). The orchestrator does not build a second index; it **queries orama** and acts on the hits. - **Harvest** — scan chats/notes for **bead GUIDs** (a strict `[0-9a-f]{8}-…` regex) and other resource URIs. Each hit (`<file>:<line>` + the two endpoints) is candidate evidence for a link. - **Materialize / update** — create or reweight a `<link/>` resource of `type: cites` (`end1` = the citing bead, `end2` = the cited bead, `provenance.evidence` = the hit). Re-running heals: existing links get evidence/weight bumped, stale ones decay. - **Offer, never inject** — links live in link-space / the orchestrator's subtree; a **backlink envelope** is *offered* into the cited bead's `uploads/` dock ("you are cited by X"). The orchestrator never writes into the cited bead's tree. - **Determinism** — GUID/URI extraction and link upsert are **pure rung-0** (per 01-agent-step-and-determinism); only the judgement "is this a *meaningful* link or an incidental mention?" escalates to a queue, never an inline model call. (Weight + a min-evidence threshold can absorb most of that deterministically.) This also **closes note 02 §8 / the `stepBead` question**: once links are addressable resources, the scheduler *reads the link* to the target bead to learn its transport (`type`/binding) and its thread — `stepBead` resolves a link rather than hard-coding a transport.
## 6. Boundaries (who owns what) - **orama-bead `6f5044df`** owns the *index* (the projection of the commons). - **collaborative-beads `48a76785`** owns the *membrane* (a specific corpus↔corpus link instance). - **bead-orchestrator (this bead)** owns *link harvesting + maintenance* (Duty 6) and would host the *link primitive's conformance* (Duties 1–2) — but the **link primitive itself** (the `<link/>` schema, where links live, the URI scheme) is a **commons-protocol question** that likely deserves its **own bead/slice**, not unilateral reification here. This note proposes; it does not ratify into `beads.md`.
## 7. Open questions (Stephen binds) 1. **Where links live** — a per-link bead (own GUID dir)? a `links/` space under `guerin/`? a single **link-registry bead**? Or extend `names.json` with a `links` array as a transitional step? (Stephen: "they can live anywhere" — so the *model* is location-free; we still pick a *default home* for harvested ones.) 2. **URI scheme** — does a link get a GUID like a bead (`.agents/<guid>/` with `resourcetype:<link/>`), or a derived/content-addressed id (`hash(end1,end2,type)`) so the same edge isn't harvested twice? 3. **`names.json` future** — keep as hand-curated view + generate a links-projection beside it, or migrate `names.json.mounts` entries into link resources outright? 4. **Directionality + type vocabulary** — fixed `type` set (`cites`/`mounts`/`lineage`/`membrane`/`step-channel`) or open? directed vs undirected default? 5. **Auto-materialize vs offer-only for links themselves** — may the orchestrator *create* `cites` link resources autonomously (in link-space), or only *offer* them for a human/owner to bind? (Backlink envelopes into docks are offer-only regardless.) 6. **Confidence threshold** — min evidence/weight before a harvested link is surfaced, to keep incidental GUID mentions out. 7. **Relationship to `collaborative-beads`** — is the membrane retro-modeled as a `<link/>` (dogfood the primitive), or kept as its own bead that the link primitive merely *describes*?