# Eldric AI OS 5.0.0-alpha.2 — Release Notes

_Focus of this release: the user-facing Edge. Every knob in the
chat shell — plugin toggles, conversation history, selected
model, sharing opt-ins — is now **cluster-side by default** so the
same session resumes from any workstation. Data-plugin fan-out is
wired end-to-end from sidebar checkbox to merged context in the
LLM prompt._

## Headline change — the chat sidebar is now the control plane

The `/chat` shell shipped in alpha.1 was a 30-line placeholder.
alpha.2 ships the real shell: a tabbed sidebar (Chats | Plugins)
that drives a full session model. Flipping a toggle on one device
shows up toggled on every other device logged into the same
cluster session.

**What this means end-to-end:**

```
 sidebar toggle ───► POST /api/v1/edge/plugins/toggle
                           │
                           ▼
                 <data_dir>/edge/sessions/<sid>.json
                           │
                           ▼                      ┌─► retrieval.data.local (syscall)
            POST /api/v1/edge/chat   fan-out ─────┤
                   {query, messages}              └─► retrieval.data.arxiv (extension bridge)
                           │
                           ▼
                  merged system message
                           │
                           ▼
                   cloud.chat.stream ──► SSE tokens to browser
```

## Plugin toggle model — one mechanism, eight categories

Everything visible in the chat sidebar is a plugin. Toggling is
immediate, persistent, and per-session:

| Type | Examples | What toggling does |
|---|---|---|
| `core`      | `core` | Always on — chat transport, bootstrap. Can't disable. |
| `render`    | markdown, code, charts, mermaid, graphviz | Wraps assistant output. |
| `data`      | `data.local`, `data.arxiv`, `data.nasa`, `data.esa`, `data.spacex` | Adds a source to the query fan-out. Multi-select is the point — enable 3 sources, queries fan out to 3. |
| `project`   | `project.current` | Scopes retrieval + writes to the active project. |
| `workgroup` | `workgroup.shared` | Includes the workgroup's shared KBs in retrieval. |
| `sharing`   | `sharing.results`, `sharing.usage`, `sharing.dream` | Opts in to contributing back (write side). |
| `tool`      | `tool.web`, `tool.code` | Authorises the LLM to invoke a tool. `tool.web` is wired to DuckDuckGo via a shipped extension. |
| `privacy`   | `session.local` | The single opt-out — flips state storage from cluster to browser localStorage. |

17 builtin plugins plus **every loaded extension** with
`category: data` or `category: tool` auto-surfaces as a togglable
plugin. No Edge rebuild needed to add a new data source.

## What persists on the cluster now

The cross-workstation resume promise is carried by three small
files per user session:

| State | Location | Survives |
|---|---|---|
| Plugin enabled-set | `<data_dir>/edge/sessions/<sid>.json` | kernel restart + workstation switch |
| Selected model | same file, `selected_model` field | kernel restart + workstation switch |
| Conversation history | `<data_dir>/edge/conversations/<sid>/<conv-id>.jsonl` | kernel restart + workstation switch |
| Dream profile (if `sharing.dream` on) | `<data_dir>/dream/profiles/<sid>.json` | indefinitely, until opt-out |
| Session id | browser `localStorage.eldric.sid` | device-local |

**Conversation files are JSONL**, one message per line,
append-only. A crashed write can't corrupt an earlier turn.
Listing + replay are plain directory scans.

## Extension-backed data plugins (no Edge rebuild)

The biggest leverage change in alpha.2: adding a new data source
to the chat sidebar is a manifest drop plus a `load` call. No
edit to `edge_module.cpp`, no rebuild, no restart.

```bash
# Drop a manifest — either a Model B external process or a
# Model A subprocess we spawn.
cat > ${ELDRIC_DATA_DIR}/extensions/pubmed.extension.yaml <<YAML
extension:
  name: pubmed
  display_name: PubMed
  category: data                        # ← this is what makes it a plugin
  model: B
  external_url: http://pubmed-svc:9600
  tools: [search]
YAML

# Tell the kernel.
curl -XPOST /api/v1/extensions/load -d '{"name":"pubmed"}'

# `data.pubmed` is now a togglable plugin in every session's chat
# sidebar. No restart, no redeploy, no user log-out.
```

The fan-out path in Edge tries `retrieval.<plugin-id>` as a native
syscall first; if none is registered, it dispatches to the loaded
extension's `/invoke` through the existing `extension.invoke_tool`
syscall. Either path, same `{"snippets":[{"text","source","score"}]}`
reply contract.

Two reference extensions ship in the baseline:

| Extension | Port | Fulfils | Source |
|---|---|---|---|
| `arxiv` | 9502 | `data.arxiv` plugin | `sdk/extension/examples/arxiv/` |
| `web_search` | 9503 | `tool.web` plugin | `sdk/extension/examples/web_search/` |

Both are ~80 lines of Python. Copy either as the starting
template for a new source.

## Per-query fan-out with source citations

`POST /api/v1/edge/chat` is the session-aware counterpart to
OpenAI-compat `/v1/chat/completions`. Before calling the LLM it
fans out parallel retrieval calls to every enabled `data.*`
plugin (and enabled `tool.*` plugins with retrieval backends),
merges the returned snippets into a synthesised `role:"system"`
message prepended to the conversation, and dispatches
`cloud.chat.stream`.

Every response carries an `X-Eldric-Fanout` header:

```
X-Eldric-Fanout: [
  {"id":"data.local","name":"Local KB","count":2,"note":"ok"},
  {"id":"data.arxiv","name":"arXiv",  "count":5,"note":"ok (ext:arxiv)"},
  {"id":"data.nasa", "name":"NASA",   "count":0,"note":"no-backend"}
]
```

The chat shell renders this as a "retrieved from: Local KB (2),
arXiv (5)" badge under the assistant message. Notes surface
backend-wiring gaps to the admin:

| Note | Meaning |
|---|---|
| `ok`                   | In-process `retrieval.<id>` syscall answered. |
| `ok (ext:<name>)`      | Extension at `<name>` answered via the bridge. |
| `no-backend`           | Plugin enabled but neither a syscall nor an extension binds the id. |
| `error: …`             | Transport / parse failure — detail in the string. |

## The privacy opt-out — `session.local`

Every other mechanism here assumes cluster-default storage, but
the user stays in control. Flipping the `session.local` plugin
on — visible in the sidebar's Privacy section in a
warning-coloured switch — does three things *immediately*:

- The current session file on the cluster is removed.
- The session's conversation directory is deleted.
- All further writes (toggles, model choice, messages) are
  skipped server-side; the shell mirrors state into the browser's
  `localStorage` instead.

Cross-workstation resume stops working while on — by design.
Flipping back off returns to cluster-default; previously scrubbed
history is gone (no undo, no half-states).

## `sharing.dream` wired — per-user dream profile

Toggling the `sharing.dream` plugin on now snapshots the session's
enabled-set into `<data_dir>/dream/profiles/<sid>.json`. Every
subsequent plugin toggle while dreaming is active refreshes the
snapshot. Toggling off deletes the profile. Nothing else happens
yet — Phase-3 ships the cycle body that reads the profile and
scopes ingest/publish accordingly (see `dream.scoping` help for
the full design).

Four new syscalls on the dream module:

```
dream.profile.update  {session_id, enabled[]}  → snapshot/refresh
dream.profile.get     {session_id}             → read
dream.profile.clear   {session_id}             → remove
dream.profiles.list   {}                       → enumerate opted-in
```

Atomic writes (temp + rename). `session_id` sanitised the same
way Edge sanitises filenames — non-`[A-Za-z0-9._-]` → `_`.

## Surface additions

**Edge routes (new):**

```
GET    /chat                                   ← real shell now
GET    /api/v1/edge/session                    profile + enabled plugins + selected model
GET    /api/v1/edge/plugins                    full catalog (builtin ∪ extension-derived)
POST   /api/v1/edge/plugins/toggle             {id,enabled?}
GET    /api/v1/edge/data-sources               enabled data.* plugins only
PUT    /api/v1/edge/session/model              {model}
GET    /api/v1/edge/conversations              list
POST   /api/v1/edge/conversations              create
GET    /api/v1/edge/conversations/messages?id= replay
POST   /api/v1/edge/conversations/messages     append
DELETE /api/v1/edge/conversations?id=          delete
POST   /api/v1/edge/chat                       session-aware chat + fan-out
```

**Kernel syscalls (new):**

```
extension.list                     filter by host/category/tenant/state
```

**Dream module syscalls (new):**

```
dream.profile.update / get / clear / profiles.list
```

**Data module:**

- `search_vectors_core_(body)` factored out — used by both the
  `/api/v1/vector/search` HTTP route and the new
  `retrieval.data.local` syscall.
- `retrieval.data.local` syscall registered — defines the
  `retrieval.<plugin-id>` contract every data plugin fulfils.

## Documentation

New or substantially rewritten help topics:

| Topic | File |
|---|---|
| `edge.chat.ui`        | `modules.d/help/edge/chat-ui.help.md` |
| `edge.plugins`        | `modules.d/help/edge/plugins.help.md` |
| `edge.conversations`  | `modules.d/help/edge/conversations.help.md` |
| `dream.scoping`       | `modules.d/help/dream/scoping.help.md` |

Kernel help-topic lookup now serves bundled-module help files
(alpha.1 silently returned empty markdown for any topic whose
owning module had no manifest).

**EldricOS.md** gained three new subsections under Edge Plugin
Architecture:

- *Cluster-side session state + conversation history (5.0)*
- *Data-plugin fan-out + extension backends (5.0)*
- *Native + mobile client wiring (TODO — one spec, multiple targets)*

The last one is the contract every non-web client (macOS Swift
GUI, iOS, future Android and Windows desktop) must honor to
become a first-class citizen. 13 endpoints, 7 rules, one SDK
planned for Phase-4.

## Client status

| Client | Status |
|---|---|
| Web (`modules.d/edge/chat.html`) | **Reference implementation — alpha.2** |
| macOS Swift GUI (`gui/Eldric/`) | Pending rewire to the 5.0 Edge endpoints |
| iOS (`EldricMobile/`) | Pending rewire |
| Android | Not started |
| Windows desktop | Not started |

Until the Phase-4 Eldric Client SDK lands, each client
reimplements the 13 endpoints manually against the spec in
EldricOS.md.

## Commits that ship in this release (22)

**Control-plane arc (10 commits):**
```
84249048  5.0 kernel: edge chat shell + plugin toggle model
ffa730ee  5.0 edge: persist session plugin toggles + ship sharing.dream
290637bd  5.0 edge: cluster-side conversation history + session.local opt-out
db4c79ea  5.0 edge: wire chat shell to cluster-side conversation store
58446d83  5.0 edge/data: data-plugin fan-out → retrieval syscall → cloud.chat
1c342378  5.0 edge: extension-backed data plugins — arxiv + tool.web
eea59307  5.0 edge: auto-surface loaded extensions as data/tool plugins
88ecf44a  5.0 dream: wire sharing.dream plugin → per-user profile snapshot
adbaf43b  5.0 edge: persist selected model cluster-side
33eb325a  5.0 docs: alpha.2 release notes + native-client wiring spec
```

**Completion + hardening arc (12 commits):**
```
fe753b7e  5.0 dream: cycle resolves opted-in scopes at ingest
298d5bc2  5.0 cloud: echo (debug) backend — zero-config UX demos
ac5bef3d  5.0 test: alpha.2 smoke harness — 33 checks, ~10s, stdlib only
41fbdd75  5.0 edge: session.local scrubs the dream profile too
23c43d6e  5.0 dream: ingest + checkpoint phases wired to syscalls
2fa7d8fa  5.0 edge: conversation pagination — ?before= + ?limit=
1f413c70  5.0 edge: SSE event stream at /api/v1/edge/events
0ff87a7a  5.0 kernel: /api/v1/catalog enriches with install_state
93dbe522  5.0 sdk: NASA APOD reference extension — 4.x→5.0 port template
a466e32d  5.0 agent: fall back to cloud.chat when inference.chat absent
f583b9d4  5.0 edge: conversation search + markdown/json export
03209e0b  5.0 edge: plugin/conversation/chat usage counters → /metrics
f5600f2d  5.0 kernel: extension manifest schema — stricter validation
1424b1de  5.0 scripts: deploy-repo.sh (saved, not runnable yet)
3bdb9111  5.0 edge: conversation rename + audit trail for privacy toggles
```

## Spec-audit closure arc (8 additional commits)

Re-reading EldricOS.md against the code surfaced four Phase-1
deliverables that were compiled-in but not exposed, plus ops
runbook + repo gaps. All eight commits validated by the harness.

```
7999f250  5.0 data: expose Matrix Memory maintenance + observability surface
6791db29  5.0 kernel: rich Brain tool metadata on extension manifests
2aa1d558  5.0 kernel: relax manifest category whitelist + auth smoke test
60c2be80  5.0 router: HTTP fallback for headless controller discovery
81808042  5.0 docs: kernel.installing — repo.eldric.ai admin runbook
a3a666f6  5.0 docs: kernel.smoke-test — harness runbook for ops
c8df5c43  5.0 test: ELDRIC_EDGE_BASE — point smoke harness at a remote install
d740ed5e  5.0 kernel: enrich /health with version / uptime / auth / module_count
d04a7c92  5.0 packaging: bump to 2.alpha2 release + fix tool_specs YAML→JSON fidelity
a32f6b5b  5.0 docs: audit help cross-links + fix 2 broken See-also refs
```

**repo.eldric.ai live.** `https://repo.eldric.ai/5.0/` serves
- `eldric-aios-5.0.0-2.alpha2.fc43.x86_64.rpm` (1.4 MB CPU baseline)
- `eldric-aios-cuda-5.0.0-2.alpha2.fc42.x86_64.rpm` (512 MB GPU add-on,
  `GGML_CUDA=ON`, sm_75..89)

both signed with a 4096-bit RSA GPG key (`repo@eldric.ai`, 5-year
expiry). The CPU RPM declares a `Supplements:` weak dep on
`cuda-drivers`, so `dnf install eldric-aios` on a GPU host auto-pulls
the cuda add-on and the `/usr/bin/eldric-aios` wrapper execs the
CUDA binary when `/dev/nvidia0` is present. Full round-trip
validated end-to-end on a fresh Fedora 42 + RTX 4070 Ti host and
a fresh Fedora 43 CPU-only host.

**Smoke harness: 86 checks + a sibling help-link auditor.**
`scripts/audit-help-links.py` spawns a short-lived kernel, scrapes
the topic registry, walks every `.help.md` for broken `See also`
refs. CI drop-in next to the primary harness.

- **Matrix Memory surface** — `/api/v1/memory/{health,matrices,
  checkpoint,verify}` wired to the already-compiled
  `MatrixMemoryService`. The class shipped in alpha.1 but the
  HTTP surface had only store + recall.
- **Rich Brain tool metadata** — `tool_specs:` map on manifests
  carrying `description`, `input_schema` (JSON Schema),
  `examples`, `usage_policy`, `resource_hints` per tool. Per
  EldricOS.md §7982 the Brain needs these to learn tools at
  runtime without retraining. Surfaced on `extension.list`.
- **Manifest category relaxed** — category still required but no
  longer enum-gated; legacy names (`inference_adapter`,
  `oa_papers`, `private_knowledge`, `customer_backend`) load
  cleanly, name-traversal still blocked.
- **Router HTTP fallback** — headless `eldric-routerd` on a
  separate host now discovers workers via
  `GET /api/v1/workers` when the syscall isn't loaded.
- **`/health` enriched** — `version`, `uptime_s`, `auth`,
  `module_count` in one round-trip.
- **`ELDRIC_EDGE_BASE=<url>`** — harness runs against any
  remote install; skips the four filesystem-bound tests,
  exercises the other 18 identically over HTTP.
- **repo.eldric.ai live** — on 10.19.0.31, TLS via Let's
  Encrypt, GPG-signed RPMs (4096-bit RSA `repo@eldric.ai`),
  7 baseline extension manifests under `/catalog/`.
  `dnf install eldric-aios` works on any Fedora 42+ host.
  Admin runbook at help topic `kernel.installing`.
- **`kernel.smoke-test` runbook** — 16-group table of what the
  harness actually exercises + how to run remote-mode.

Harness grew 66 → **81 checks** (`scripts/smoke-test-alpha2.py`,
~15 s, stdlib only, CI-drop-in).

## Extended additions after the initial release-notes commit

The first alpha.2 notes covered the control-plane arc. The
hardening / completion arc above adds (all validated by the
`scripts/smoke-test-alpha2.py` harness — 81 checks, <15 s):

- **Dream cycle actually consumes the profile.** `trigger_cycle()`
  resolves every opted-in session's scope at ingest and fires
  `retrieval.<plugin-id>` per enabled data plugin; `checkpoint`
  writes consolidated payloads to matrix memory only for scopes
  with `sharing.results` enabled. Phase-3 just replaces the
  still-stubbed extract/probe/distill phases with LLM calls.
- **Echo (debug) backend.** Pick model `eldric-echo` and
  `/api/v1/edge/chat` pretty-prints what the LLM would receive
  (including the retrieval-context system message). Zero deps.
- **Session persistence completeness.** `session.local` opt-in now
  also scrubs the dream profile; model choice persists
  cluster-side (`selected_model` in the session file). A user
  moving between workstations finds their plugins, their model,
  their conversations, and their dream state exactly as they
  left them.
- **Live catalog reload.** Edge serves SSE at
  `/api/v1/edge/events`; kernel publishes `extension.loaded` /
  `extension.unloaded`. Clients see newly-loaded data plugins
  without a page refresh.
- **Conversation pagination, search, export, rename.** Full CRUD
  surface for long chat histories — `?before=`, `?limit=`,
  `?q=`, `?format=md|json`, `PATCH {id,title}`.
- **Catalog enrichment.** `/api/v1/catalog` now reports
  `install_state` per entry (`not_installed` / `available` /
  `loaded`) so dashboards render install/enable/disable with one
  request.
- **Agent fallback.** `agent.invoke` tries `inference.chat` first,
  falls back to `cloud.chat`; default model is `eldric-echo` so a
  zero-config install answers out of the box.
- **Usage metrics.** `/metrics` now carries
  `eldric_edge_plugin_toggles_total`,
  `eldric_edge_conversations_created_total`,
  `eldric_edge_chat_requests_total` with plugin labels but never
  user content.
- **Audit trail for privacy toggles.** `session.local` and
  `sharing.*` flips land in the hash-chained audit log;
  ephemeral toggles (`data.*`, `render.*`, `tool.*`) don't.
- **Manifest schema tightening.** Stricter validation in
  `parse_manifest` — required version + category, name safety
  charset (blocks traversal), Model A entry_point required,
  license tier whitelisted. Bad manifests rejected at scan
  rather than at load.
- **Reference extension template.** NASA APOD ports as the
  reference 4.x→5.0 extension — ~60 LOC Python Model B, fulfils
  the `retrieval.<id>` contract, auto-surfaces as `data.nasa_apod`
  when loaded. Template for the rest of the 4.x science surface.
- **Smoke harness.** `scripts/smoke-test-alpha2.py` — 66
  assertions across every path this release touches. Stdlib only.
  CI-drop-in.
- **Deploy script.** `scripts/deploy-repo.sh` for
  `repo.eldric.ai` (10.19.0.31, `/data/repo/eldric`). Saved for
  when NAT + DNS land; the pre-flight SSH probe fails fast until
  then.

## Upgrade from alpha.1

Drop-in. The `/data/eldric` layout is compatible — alpha.2 adds
two new subdirectories (`edge/sessions/`, `edge/conversations/`,
plus `dream/profiles/` if any user opts in) but doesn't touch
existing alpha.1 paths (`vectors/`, `memory/`, `models/`,
`cloud/`, `sources.jsonl`, `extensions/`). RPM layout unchanged.
`dnf upgrade` picks up the new baseline chat shell at
`/usr/share/eldric/modules.d/edge/chat.html`; overlay
customisations continue to work.

Existing alpha.1 clients (OpenAI SDK pointing at
`/v1/chat/completions`) keep working — that path stays stateless
and OpenAI-compat. Session-aware behaviour is opt-in via
`/api/v1/edge/chat`.

## What's still Phase-3

- **Dream cycle body** consuming the profile: per-user ingest
  scoped to enabled data plugins, publish gated by `sharing.*`.
- **Identity service** behind `/api/v1/edge/session`: the current
  profile is hardcoded `anon/default/viewer`. Phase-3 wires real
  users, tenants, projects, workgroups, and plugs them into the
  scope plumbing that's already in place.
- **Scoped skills / agents** — the Phase-3.5 extension that turns
  user-authored skill and agent declarations into additional
  plugin categories (spec in `dream.scoping`).
- **Eldric Client SDK** in Swift / Kotlin / Rust / TypeScript so
  each non-web client is ~100 lines of UI glue.
- **Authentication policy default flip** — `--auth=enforce` still
  ships off by default; alpha.3 ships an `eldric-setup` wizard
  that provisions the first admin token.
