Place the Huginn organ cut map (Cuts 11-15, remapped)
Imagination (Opus) remapped Cuts 11-15 against Huginn 8c5f784, with the read-side and body-plane cuts landing ahead of them. Cut 11 splits into 11a/11b and Cut 13 into 13a/13b. Cut 12 is proposed for parking. Five operator questions are open (Q-H1, Q-H2, Q-H4, Q-H5, Q-H6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4f784d04d3
commit
b6c2ca112b
1 changed files with 560 additions and 0 deletions
560
notes/huginn-organ-cut.md
Normal file
560
notes/huginn-organ-cut.md
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
# Huginn wired in: cut map for Cuts 11-15
|
||||
|
||||
Date: 2026-09-29. Imagination output (Opus). Nothing here is committed, and no
|
||||
repository or live service was written to. Self places this map. Its natural
|
||||
home is a section of `F:\Projects\Epiphany\notes\eureka-pipeline-state-cut.md`
|
||||
that replaces the old Cuts 11-15 (lines 5490-5853). Mark those lines as history
|
||||
the same day, so that no reader sees two live designs.
|
||||
|
||||
**Objective (operator, 2026-09-29):** "Huginn wired in". Eureka's pipeline
|
||||
runs on Huginn's typed state. In scope: Cuts 11, 13, 14 and 15. Cut 12 is
|
||||
recommended for parking (see Q-H5). Out of scope: Cut 16 (the proof
|
||||
campaign), the Verse-presence cut (Q23 A), a public or stranger-ready skill,
|
||||
and the landing site.
|
||||
|
||||
**What lands ahead of these cuts** (mapped elsewhere, not re-mapped here):
|
||||
|
||||
- BP-1: CultLib content plane. It is in Hands on `cultmesh/rust-content-plane`, with three commits pushed.
|
||||
- BP-2: the pin move.
|
||||
- RS-3: `Mind::query(selection, semantic)`. It adds `cultnet-rs` to `huginn-mind`, and deletes `PipelineQuery` and `QUERY_LIMIT_MAX`.
|
||||
- BP-3: `HuginnMindResponse::Deferred`, `MAX_DEFERRED_BODY_BYTES`, and a deferral budget of 256 MiB.
|
||||
|
||||
Every Huginn anchor below is against `eureka/memory-organ` at `8c5f784`, which
|
||||
is clean. RS-3 and BP-3 move `query.rs`, `wire.rs` and `serve.rs`, so **each
|
||||
brief re-takes its anchors after they land.**
|
||||
|
||||
## Pins
|
||||
|
||||
| Repo | Branch | HEAD | Used for |
|
||||
|---|---|---|---|
|
||||
| Huginn | `eureka/memory-organ` | `8c5f784` | Cuts 11 and 13 |
|
||||
| Epiphany | `codex/eureka-pipeline-state` | `4f784d04` | the leaf's text fields (`epiphany-pipeline/src/lib.rs:510-578`), read only |
|
||||
| Eureka skill | `main` | `19487f0` | Cut 15 (`~/.claude/skills/eureka`) |
|
||||
| gamecult-ops | `main` | `8554e60` | Cut 14 |
|
||||
| Idunn | (current) | `5bbb461` | read only. The route-continuity target and cut map are the dependency for 14-I |
|
||||
|
||||
## Probe facts (run 2026-09-29, read-only except as noted)
|
||||
|
||||
- **P1. Qdrant on Yggdrasil** is `qdrant 1.17.1`, on `127.0.0.1:6333`.
|
||||
- It holds exactly two collections, both voidbot's:
|
||||
- `voidbot_repository_source_chunks`: 116,877 points;
|
||||
- `voidbot_discord_history_chunks`: 40,788 points.
|
||||
- Both are 1,024-dimensional, `Cosine`, `on_disk: true`, with keyword payload indexes (`sourceId`, `corpusKind`, `repoName`, `pathPrefixes`, …).
|
||||
- **`config.metadata` is null on both.** They were restored, not created by voidbot's code. So voidbot's embedder-compatibility check (`VoidBot/packages/rag/src/qdrant-vector-store.ts:458-477`) has nothing to compare against in production.
|
||||
- **P2. Raven's Ollama is live, and voidbot already uses it.**
|
||||
- `/srv/voidbot/retrieval/voidbot.env` reads `RAG_OLLAMA_BASE_URL=http://10.77.0.4:11434` and `RAG_OLLAMA_MODEL=qwen3-embedding:0.6b`. So the 2026-09-26 cutover has happened.
|
||||
- `gamecult-ops/inventory.md:669` and `runbooks/voidbot-retrieval-recovery-yggdrasil.md:40-62` still name Nightwing. They are stale.
|
||||
- Nightwing (`10.77.0.3:11434`) did not answer.
|
||||
- Epiphany's old Yggdrasil-local endpoint (`10.77.0.1:11435`) did not answer.
|
||||
- **P3. What Raven serves.** Ollama `0.21.1`. Tags:
|
||||
- `qwen3-embedding:0.6b`: digest `ac6da0dfba84…`, Q8_0;
|
||||
- `bge-m3:latest`.
|
||||
- One embed call per model, from Yggdrasil, returned **1,024 dimensions for both**. Each call took **5-6 s cold**. `/api/ps` reports a context of 4,096.
|
||||
- Side effect: this probe loaded `bge-m3` into Raven's VRAM until its 5-minute keep-alive expired.
|
||||
- **P4. How voidbot uses Qdrant**, which is the convention to reuse (`qdrant-vector-store.ts`):
|
||||
- The point id is the first 16 bytes of `sha256(chunk id)`, shaped into a v5-style UUID (`:264-280`). The real id travels in the payload.
|
||||
- Collections are created with `metadata {managedBy, corpusKind, embedderId, vectorLength, schemaVersion}` (`:203-216`).
|
||||
- **`embedderId` is `ollama:<baseUrl>:<model>`** (`ollama-embedder.ts:29-31`). Moving the host therefore reads as a model change, which is the hazard the 2026-09-26 memory warns about. Huginn keys compatibility on **model name plus digest**, not on the URL.
|
||||
- Query text is prefixed `Instruct: …\nQuery: …` when an instruction is configured (`:42-48`).
|
||||
- **P5. WireGuard exists.**
|
||||
- Starfire has the adapter `wg-gamecult-starfire` at `10.77.0.2`, and it is Up.
|
||||
- Yggdrasil has `wg-gamecult` at `10.77.0.1/24`, reachable from Starfire.
|
||||
- `10.77.0.1:17872/udp` is unbound. Ports in use today: 17871 (Odin, via nginx), 17873, 17876 and 17890 (Idunn's host-actuator hub, which is not in R21's list).
|
||||
- **ufw admits UDP on wg only per port:** 3075, 17871, 17876 (from `10.77.0.2` only) and 17890. **17872 needs a rule.**
|
||||
- Starfire's 17877 RUDP requester is not listening today. So a Rust RUDP client on Windows over WireGuard has a precedent, but it is not proven live right now.
|
||||
- **P6. `crates/eureka-state` is an empty stub:** `src/lib.rs` has 0 lines, and `Cargo.toml` has no dependencies.
|
||||
- **P7. The daemon serves one mind per process.**
|
||||
- `serve.rs:91-111` takes exactly `--state-root`, `--instance` and `--bind`.
|
||||
- `Mind::open` takes a lifetime-exclusive redb lock (`mind.rs:127-139`).
|
||||
- `IndexSink::committed` runs **synchronously inside `Daemon::handle`, before the reply** (`daemon.rs:74-83`).
|
||||
- `run` is a single-threaded poll loop (`serve.rs:257-297`). Any blocking call made there stalls every session.
|
||||
- **P8. The hand-off already works one side at a time.** Admission derives:
|
||||
- the source side's withdrawal when `from_instance == mind` (`admission.rs:331-346`);
|
||||
- the target side's stewardship when `to_instance == mind` (`:347-353`).
|
||||
So a hand-off is two ordinary admissions, one per mind.
|
||||
- **P9. Idunn v2's contract for a routed stateful target**, read from source:
|
||||
- The state slot kinds are only `cultcache-file` and `external-file` (`Idunn/src/deployment.rs:182-187`). **There is no `cultcache-directory`.**
|
||||
- A dependency on non-Idunn infrastructure is `kind = "external-operator-binding"`. It is satisfied by the binding's `external_capabilities` (`deployment.rs:1359-1383`, `deployment_plan.rs:146-249`). `shared-infrastructure` needs a managed provider with Odin correlation.
|
||||
- The route challenge for `rudp` is a CultNet `SnapshotRequest` for `gamecult.runtime_presence_health.v2`, keyed by the target (`drivers.rs:5524-5545`). The service must answer it with signed presence, signed with the Idunn activation credential. It must also adopt the process write lease.
|
||||
- Huginn implements none of this.
|
||||
- **No shared Rust implementation exists.** `cultnet-rs` has the contracts and signers (`runtime_authority_contracts.rs`, 3,309 lines). Each Rust service writes its own participant: Ghostlight's `idunn_health.rs` is 2,001 lines, and Odin has its own.
|
||||
- Unit names are `idunn-<target>-<sha256>` per incarnation.
|
||||
- There are **391 failed `idunn-*` transient units** on Yggdrasil today.
|
||||
- **P10. No backup runs on Yggdrasil.**
|
||||
- `gamecult-authority-backup.service` is not installed. The only backup timer is `dpkg-db-backup.timer`.
|
||||
- The script `scripts/backup-gamecult-authority-yggdrasil.sh:74-76` would refuse at its first step anyway: it freezes `epiphany.service` and `epiphany-swarm.service`, which are absent (`freeze_unit` returns 1 when a unit is inactive).
|
||||
- Its path list does not name any Idunn target's state root either, including `odin` and `streampixels-service`.
|
||||
- **P11. How Claude Code reaches MCP servers today** (`~/.claude.json`):
|
||||
- `voidbot` at `:17875` is the **Yggdrasil-hosted** MCP, reached through the SSH tunnel (`start-yggdrasil-tunnel.ps1:24`).
|
||||
- `aquasynth_dings` at `:17878` runs locally.
|
||||
- The target's "Shape decided" says `eureka-state` is a local **stdio** server. That governs here. The HTTP convention recorded in memory is not a ruling.
|
||||
|
||||
**Not probed:**
|
||||
|
||||
- whether `cultnet-rs`'s RUDP client works on Windows;
|
||||
- whether Claude Code accepts a tool schema that `$ref`s a remote `$id`;
|
||||
- whether Qdrant accepts `.` in a collection name;
|
||||
- whether `ureq` without TLS covers both adapters;
|
||||
- which connection id Idunn's RUDP challenge uses.
|
||||
|
||||
Each is assigned to a cut's first step below.
|
||||
|
||||
## Standing rulings, checked against the maps
|
||||
|
||||
| Ruling | Where it is recorded | Holds? |
|
||||
|---|---|---|
|
||||
| An instance owns its mind; a service owns the state | target 14 and 15; map rulings 14 and 15 | yes |
|
||||
| The organ depends on Qdrant directly, not on voidbot | target 16; map 16 | yes |
|
||||
| The organ shares voidbot's Qdrant, with the coupling declared | map 21 (Q9 A) | yes. "Declared" means an Idunn dependency, which the interim deploy (Q-H1 A) cannot express. The runbook states the coupling until 14-I. |
|
||||
| Identity is declared, not authenticated | map 18 (Q6 A) | yes |
|
||||
| The workstation reaches the organ over WireGuard | map 19 (Q7 A) | yes. P5 confirms the mesh; the ufw rule is Cut 14's. |
|
||||
| Eureka has no Persona | target 6; map 9 | yes |
|
||||
| Embeddings: Raven's Ollama (operator, 2026-09-26, for voidbot) | memory `nightwing-embedding-migration-2026-09-26.md` | **the target disagrees.** Target ruling 16 still says "Epiphany's own service embeds against a Yggdrasil-local endpoint … only voidbot uses Nightwing". Both endpoints are dead (P2). Self sweeps that sentence. Huginn uses the endpoint voidbot uses, with the same model. |
|
||||
| `open_items` and `history` are deleted, not kept as presets | read-side map (2026-09-17) | yes. Cut 13's old seven tools shrink to four. |
|
||||
| Q13 A: the client depends on `huginn-mind` for types only | map ~3931 | yes |
|
||||
| No committed mutation harness | operator, 2026-09-22 | yes. Old Cut 11 and 12's "Mutations:" lines are void. Soul runs `cargo-mutants` on each diff. |
|
||||
|
||||
## 0b. Model page
|
||||
|
||||
One row per persistent or long-lived kind these cuts touch. **A bold cell is empty or disputed.**
|
||||
|
||||
| Kind | What names it | What happens to it over time | Who decides |
|
||||
|---|---|---|---|
|
||||
| **Index point**: one per indexable document | A v5-shaped UUID from `sha256(document id)` (P4's function, reused). The payload holds `doc_id`, `kind`, `root`, `ordinal` and `text_sha256`. | Written after the document's batch commits, off the serve loop. Never updated, because documents are immutable. Never deleted, except when its collection is rebuilt. A missing point is recreated by reconciliation. | Only the daemon's index worker writes one. `huginn-mind::index_text` decides what the text is. Qdrant decides nothing. |
|
||||
| **Collection**, one per mind | `huginn_mind_<instance>`. Its metadata is `{managed_by: "huginn", instance, model, model_digest, dimensions, index_text_version}`. | Created on the first reconciliation. Rebuilt whole when its metadata disagrees with the running configuration. **A collection whose mind is retired has no owner.** With one instance this is a recorded follow-up, not a cut. | The daemon serving that instance. It never touches a collection whose `managed_by` or `instance` is not its own. |
|
||||
| **Index text version** | `huginn_mind::index::INDEX_TEXT_VERSION` | Bumped whenever `index_text` changes. A bump makes every collection disagree, so each is rebuilt. | `huginn-mind` |
|
||||
| **Embedding model binding** | Model name plus the digest from `/api/tags`. Never the base URL (P4). | Changes when the operator changes the configuration, or when Raven re-pulls the model and the digest changes. Either one rebuilds the collection. | The operator, through the unit's arguments. The daemon verifies the binding at each reconciliation. |
|
||||
| Index health (behind or unreachable) | none: in-memory in the worker | Lost on restart, and recomputed by reconciliation at startup. Never persisted, never written to the mind. | The worker |
|
||||
| Pending semantic reply | `(session, message_id)` in the serve loop | Lives until the worker answers or the session times out. Lost on restart, and the client asks again. | `serve` |
|
||||
| **Eureka instance** | A slug. **The operator names it (Q-H4).** | Created by the first admission of its `instance` document, which Self writes at the first campaign. Never renamed: a rename is a new mind. | The operator names it. Admission enforces it (A1, `require_instance`). |
|
||||
| MCP registration | `eureka-state` at user scope in `~/.claude.json`, with the environment variables `EUREKA_INSTANCE` and `HUGINN_ENDPOINT` | One stdio child per Claude Code session. It dies with the session and holds nothing. | The operator, because it is configuration |
|
||||
| Deferred body (BP-3), client side | The manifest's `contentHash` | Fetched and verified per call, then dropped. Never written by `eureka-state`. | `cultnet_rs::fetch_content` owns verification. |
|
||||
| **Deployed daemon identity** | Interim (Q-H1 A): the systemd unit `huginn.service` on Yggdrasil, with runtime id `huginn-<slug>` (`daemon.rs:54-56`). Later: `idunn-huginn-<sha>`. | Interim: systemd restarts the unit, which is continuity. **The operator installs a new binary, which is deployment, and no brake gates it (Q-H1).** | Interim: the operator. After 14-I: Idunn. |
|
||||
| Mind store | `/var/lib/gamecult/huginn/minds/<slug>/mind.redb` | Preserved forever. It is the only truth. **No backup owner runs (P10), so this is Q-H2.** | The daemon is the single writer, through the redb lock. |
|
||||
| Firewall admission | A ufw rule: `10.77.0.1 17872/udp on wg-gamecult from 10.77.0.2` | Installed once. It dies in 14-I if Idunn's route owns it. | Interim: the operator |
|
||||
| Hand-off / imported mind | The leaf `hand_off` key | Admitted separately into each mind (P8). Import has no consumer (Q-H5). | `huginn-mind` admission |
|
||||
|
||||
## Operator questions (one batch)
|
||||
|
||||
**Q-H1. How does Huginn get onto Yggdrasil this week?**
|
||||
|
||||
- **A. An interim operator-installed systemd unit, retired into Idunn by a named later cut (14-I).**
|
||||
- The operator builds the binary in the pinned Rust image, at an exact revision, on Yggdrasil.
|
||||
- It binds `10.77.0.1:17872` directly, with no nginx in the path.
|
||||
- The state root is chosen now so that 14-I is a unit swap and no data moves.
|
||||
- Costs:
|
||||
- it contradicts the old authority map's "no operator script deploys Huginn directly";
|
||||
- it has no deployment brake;
|
||||
- the Qdrant coupling is written in a runbook rather than declared to Idunn.
|
||||
- **B. Wait for Idunn.** Huginn is deployed only through Idunn, after two things land: route continuity's route-proof class (its Q1 b) and a shared Rust runtime participant in `cultnet-rs`. "Wired in" does not close this week.
|
||||
- **C. Idunn now, against today's contract.**
|
||||
- Huginn writes a third copy of the service-side presence, activation and lease code (Ghostlight's copy is 2,001 lines).
|
||||
- It also has to answer the route challenge and pass Odin-correlated readiness.
|
||||
- Its RUDP route goes through nginx UDP, whose reload loop is exactly what route continuity exists to fix. Every reload breaks RUDP sessions (route-continuity target).
|
||||
- All of this is reworked when that campaign lands.
|
||||
- **Recommended: A,** with 14-I named, dated and blocked on route continuity plus the shared participant.
|
||||
- Neither B nor C gives a working organ on a path that will survive.
|
||||
- C would build the thing route continuity is deleting.
|
||||
- A's authority gap is real. The operator, not a map, should accept it.
|
||||
- **Depends on it:** Cut 14's whole shape, whether this week closes, and whether 14-I exists.
|
||||
|
||||
**Q-H2. Who backs up the mind?** The target requires a named backup owner. None runs (P10).
|
||||
|
||||
- **A. Revive the authority backup.**
|
||||
- Edit the freeze list: drop the two absent Epiphany units and add `huginn.service`.
|
||||
- Add `var/lib/gamecult/huginn` to the path list.
|
||||
- Install the timer.
|
||||
- Side effect: daily backups resume for voidbot, Bifrost and Idunn configuration, including a daily pause of five containers.
|
||||
- **B. A Huginn-only timer:** freeze `huginn.service`, tar `minds/`, thaw. This makes a second backup mechanism.
|
||||
- **C. Ship with no backup,** and record it as a blocking follow-up.
|
||||
- **Recommended: A.** One owner of backup, and the script is already that owner. It is simply not running. **The operator must accept the side effects, and should know that Idunn targets' state (`odin`, `streampixels-service`) is in no backup either.**
|
||||
- **Depends on it:** Cut 14's gamecult-ops diff, and one verification step.
|
||||
|
||||
**Q-H4. What is the Eureka instance called, and how many are there?**
|
||||
|
||||
- **A. One instance for all Claude Code Eureka work,** for example `eureka`, with host `starfire`.
|
||||
- **B. One per workstation or per operator,** for example `metacrat.starfire`.
|
||||
- **Recommended: A.** Ruling 14 lets one instance steward several repos. Stewardship, not the instance, is what separates repos.
|
||||
- **Depends on it:** the unit's `--instance`, the state path, the collection name, and the MCP environment. It is cheap now and a new mind later.
|
||||
|
||||
**Q-H5. Park Cut 12?** (the consumer audit)
|
||||
|
||||
- Nothing that "wired in" needs calls a cross-mind hand-off or an import:
|
||||
- there is one instance (Q-H4);
|
||||
- no second mind exists;
|
||||
- Epiphany's adoption is the second campaign.
|
||||
- Old Cut 12's `hand_off(&mut Mind from, &mut Mind to)` **cannot be built as specified**:
|
||||
- each mind is locked by its own daemon process (P7);
|
||||
- "both minds or neither" would need a cross-process protocol.
|
||||
- The hand-off already works as two ordinary admissions, one per mind, each derived and idempotent (P8).
|
||||
- **Recommended: park it.** The map keeps a note: the hand-off is two admissions, sender first. Import waits for a consumer. No tag is needed, because no code exists.
|
||||
- **Depends on it:** whether Cut 12 is built at all.
|
||||
|
||||
**Q-H6. What does Eureka keep in typed state, and what stays in prose?**
|
||||
|
||||
- **A. A hybrid.**
|
||||
- Typed documents hold the questions, rulings, `cut_spec` (Hands' contract), `cut_report`, verdicts, findings, follow-ups and resolutions.
|
||||
- The status header becomes a query.
|
||||
- The committed map keeps only what has no kind: body facts, the model page, rationale, and the subtraction ledger narrative.
|
||||
- Per-cut specs live only as `cut_spec`, so no spec exists twice.
|
||||
- **B. Typed state for records only.** Specs stay in the prose map, and `cut_spec` goes unused.
|
||||
- **C. Retire the markdown map entirely** (old Cut 15).
|
||||
- **Recommended: A.**
|
||||
- B leaves the kind the leaf spent most of its bounds on without a consumer.
|
||||
- C discards body facts and the model page, which no kind carries. Step 0b is what stopped re-cutting.
|
||||
- **Depends on it:** Cut 15's text, and the output format of every future Imagination pass, this one included.
|
||||
|
||||
## Cut order
|
||||
|
||||
```
|
||||
[BP-1 → BP-2 → RS-3 → BP-3] (in flight, other maps)
|
||||
│
|
||||
BP-2 ──► 11a Huginn: the index write side (text, adapters, worker, reconcile)
|
||||
│ (after BP-2; may land before or after RS-3, serially on the branch)
|
||||
▼
|
||||
BP-3 ──► 11b Huginn: semantic read (rank the hits through the one evaluator; non-blocking)
|
||||
▼
|
||||
13a Huginn: envelope moves to huginn-mind; eureka-state client core
|
||||
▼
|
||||
13b Huginn: the MCP surface, Windows build and install
|
||||
│
|
||||
11a ──► 14 gamecult-ops: the interim unit, ufw, runbook, backup (Q-H1 A, Q-H2); the operator deploys
|
||||
▼
|
||||
15 Eureka skill: typed-state wiring (Q-H6); after 13b is registered and 14 is live
|
||||
---
|
||||
12 parked (Q-H5) 14-I Idunn path, blocked (see the end)
|
||||
```
|
||||
|
||||
## Cut 11a. The index write side
|
||||
|
||||
- **Repo/branch:** Huginn, `eureka/memory-organ`, after BP-2. The worktree is named by Self.
|
||||
- **First:** Hands checks, in `ureq` 3's own Cargo manifest, that `default-features = false` gives plain HTTP with no TLS. If it does not, Hands uses `reqwest` with `blocking` and no TLS, and reports which.
|
||||
- **Deletes first:**
|
||||
- `NoIndex` (`daemon.rs:20-27`) as the binary's index. It survives only in tests, or goes if the new worker's mock serves them.
|
||||
- The "No crate opens that connection yet" line in `README.md`.
|
||||
|
||||
**Adds, `huginn-mind/src/index.rs`.** This is pure: no network and no JSON.
|
||||
|
||||
- `pub const INDEX_TEXT_VERSION: u32`.
|
||||
- `pub struct IndexEntry { id: PipelineRef, text: String, text_sha256: String, ordinal: u64 }`.
|
||||
- `pub fn index_text(&PipelineDocument) -> Option<String>`. It reads the leaf's fields (`lib.rs:510-578`), joined by newlines and bounded at `INDEX_TEXT_MAX_BYTES` (16 KiB, cut at a char boundary):
|
||||
|
||||
| Kind | Fields |
|
||||
|---|---|
|
||||
| campaign | `title` |
|
||||
| target | `invariants[].statement`, `not_in_scope`, `canonical_implementations` |
|
||||
| question | `title`, `question`, `options[].text` |
|
||||
| ruling | `title`, `ruling`, `operator_quote` |
|
||||
| cut_spec | `title`, `first`, `adds`, `keeps_moves` |
|
||||
| cut_report | `commits[].subject`, `deviations` (`what`, `why`), `undone`, `promises[].text` |
|
||||
| verdict | `claims[].claim` |
|
||||
| finding | `claim`, `failure_scenario`, `evidence[].result` |
|
||||
| follow_up | `item`, `why_it_can_wait` |
|
||||
| resolution | `rationale`, **withdrawn ones included** (the Q17 B obligation) |
|
||||
| hand_off | `reason` |
|
||||
| instance, stewardship | none |
|
||||
|
||||
- `impl Mind { pub fn index_entries(&self, only: Option<&[PipelineRef]>) -> Vec<IndexEntry> }`. The ordinal comes from the writing receipt (RS-1).
|
||||
- Owner: `huginn-mind`, because "which fields are text" is a rule about the leaf's documents, and RS-3 names Cut 11 its owner.
|
||||
- Live consumer: the daemon's worker.
|
||||
- Protected invariant: the text is derived, never stored, and never includes receipts, provenance or status.
|
||||
- Why no existing owner serves: the leaf owns shape, not retrieval text, and the daemon owns no rule.
|
||||
|
||||
**Adds, `huginn-daemon/src/index/`.** Three files: `mod.rs`, `ollama.rs`, `qdrant.rs`. The daemon depends on `ureq` (see First) and `serde_json`, and `serde_json` is used only in the two adapter files: that is the xenos boundary.
|
||||
|
||||
- **Ports**, which are narrow traits so tests mock them:
|
||||
- `Embedder { model_identity() -> ModelIdentity{name, digest, dimensions}; embed(&[String]) -> Vec<Vec<f32>> }`;
|
||||
- `VectorIndex { describe(name) -> Option<CollectionMeta>; recreate(name, meta); ids(name) -> BTreeSet<String>; upsert(name, points) }`.
|
||||
|
||||
`search` is 11b's.
|
||||
- **The worker** is one thread that owns both ports and receives jobs over a channel.
|
||||
- **`IndexSink::committed` builds the entries** (local and cheap) **and only enqueues them.** It never blocks and never waits on the network. This is the fix for P7.
|
||||
- **At startup, one reconciliation:**
|
||||
1. read the model identity;
|
||||
2. `describe` the collection;
|
||||
3. if it is absent, or its metadata disagrees, `recreate` it;
|
||||
4. if the metadata names another `managed_by` or `instance`, refuse and log, and never recreate;
|
||||
5. take `ids`, subtract, then embed and upsert what is missing, in batches of 32, with `wait=true`.
|
||||
- **On failure:** log, keep the failed entries in memory, and retry with a backoff that starts at 30 s and is capped at 10 min.
|
||||
- **Options:** `--qdrant-url`, `--ollama-url` and `--embedding-model` join `parse_options` (`serve.rs:96`). **All three are required**, so there is no index-less mode. An unreachable index is a degraded projection, not a configuration.
|
||||
- **The query instruction constant** for qwen3 lives beside the Ollama adapter. 11b uses it.
|
||||
|
||||
**Authority map.**
|
||||
|
||||
- Owner: the typed mind owns truth, and the worker owns the projection.
|
||||
- Inputs: `IndexEntry`s built by `huginn-mind`, and the configuration.
|
||||
- Outputs: Qdrant points and collection metadata.
|
||||
- Derived state: the whole collection and the worker's in-memory health.
|
||||
- Forbidden writers: nothing else writes `huginn_mind_*`; voidbot never does; admission never waits on the index; the mind never stores index state; there is no `pending_index` document (old Cut 11 is superseded).
|
||||
- Shared paths: `IndexSink::committed` (live writes) and startup reconciliation share one embed-and-upsert routine.
|
||||
- Deletion line: `NoIndex` leaves the binary.
|
||||
|
||||
**Verification** (Yggdrasil, stopgap, Rust image): `ygg-verify.sh F:/Projects/Huginn <sha> rust 'cargo test --locked --workspace'`, then `cargo mutants --in-diff` on the cut's range.
|
||||
|
||||
| Test (behaviour) | Rule |
|
||||
|---|---|
|
||||
| `an_admission_replies_while_the_embedder_is_blocked` (the mock embedder waits on a barrier; `handle` returns `Committed` before the barrier is released) | The index never delays or refuses admission |
|
||||
| `reconcile_indexes_exactly_the_indexable_documents_the_collection_lacks` | The projection converges by set difference |
|
||||
| `a_model_digest_change_rebuilds_the_collection` (same name, different digest) | Compatibility is keyed on model and digest, **never on the URL**. At least one fixture pair must differ only in URL and expect **no** rebuild. |
|
||||
| `a_collection_owned_by_another_instance_is_never_recreated` | No cross-mind writes |
|
||||
| `index_text_ignores_admission_facts_and_indexes_withdrawn_resolutions` | Text is derived from the document alone, and Q17's obligation holds |
|
||||
| `point_ids_are_a_pure_function_of_the_document_id` (a fixed vector, computed by P4's algorithm) | Stable ids across restarts, with voidbot's convention |
|
||||
|
||||
- **Negative greps:**
|
||||
- `rg -n "ureq|reqwest|serde_json|qdrant|ollama" crates/huginn-mind/src` is empty;
|
||||
- `rg -n "thread::spawn" crates/huginn-daemon/src` hits only `index/`.
|
||||
- **Live adapter check (operator-approved, because it writes to the shared Qdrant):** a scratch instance `verify.<rand>` on Yggdrasil admits two documents; its collection appears with P1-style parameters and the metadata above; then the collection is deleted by hand.
|
||||
|
||||
**Estimate:** about 650 lines added, most of them tests. +`ureq`, +`serde_json` (daemon only). −`NoIndex` from the binary.
|
||||
|
||||
**Build budget:** the Huginn workspace, on Yggdrasil only, with one release build for the live check.
|
||||
|
||||
**Hands context:** about 150k tokens: `daemon.rs`, `serve.rs` and `mind.rs` headers plus the new code.
|
||||
|
||||
## Cut 11b. Semantic read
|
||||
|
||||
- **Repo/branch:** Huginn, after RS-3 and BP-3. Anchors are re-taken on their tip.
|
||||
- **Deletes first:**
|
||||
- the refusal "semantic query: the index is not wired (Cut 11)", at both sites (`query.rs:296-299`, and the daemon test at `daemon.rs:534-541`, re-taken);
|
||||
- the test `semantic_query_refuses_typed_until_wired`. Its rule dies; the replacements are below.
|
||||
|
||||
**Adds.**
|
||||
|
||||
- **`huginn-mind`:** `Mind::rank(&self, selection: &Selection, hits: &[(PipelineRef, f32)]) -> Result<PipelineSelectionPage, MindRefusal>`. Its rules:
|
||||
1. A selection with a `cursor`, together with `semantic`, is refused `SelectionInvalid` (`field: "cursor"`), because semantic answers are not pageable.
|
||||
2. The rows are restricted to hit ids present in the image as of `head`. A hit with no document is dropped and logged by the caller, never fabricated.
|
||||
3. RS-3's own path runs: `cultnet_rs::validate`, the value door, then `select` over the restricted rows. **The one evaluator decides membership. Qdrant never filters.**
|
||||
4. Order is score descending, with ties broken by ordinal.
|
||||
5. Truncate to `limit`.
|
||||
6. `next: None`.
|
||||
- **`huginn-daemon`:**
|
||||
- A `Query` with `semantic` is not answered inline. `serve` stores `(session, message_id, request)` and sends a `Search { text, top_k }` job to the worker.
|
||||
- The worker embeds with the query instruction and searches the collection, with no filter and an oversample of `min(top_k × 4, 200)`.
|
||||
- The loop drains worker replies on each poll, calls `rank`, and replies on the stored session.
|
||||
- When the worker is unhealthy, or the call fails: `Refused(Unavailable { detail })`, and the detail names the endpoint.
|
||||
- **`VectorIndex` gains `search`.**
|
||||
|
||||
**Authority map:** the owner of membership is `rank`, over RS-3's evaluator. Qdrant supplies only a candidate set and scores. Forbidden: lowering a `FieldPredicate` into a Qdrant filter, because that would be a second evaluator; RS-3 §8 had left this to Cut 11, and this cut refuses it.
|
||||
|
||||
**Verification:**
|
||||
|
||||
| Test | Rule |
|
||||
|---|---|
|
||||
| `semantic_hits_pass_through_the_selections_own_predicates` (a hit of an excluded kind is absent) | One evaluator |
|
||||
| `semantic_results_are_ordered_by_score_not_ordinal` | Ranking |
|
||||
| `a_hit_without_a_document_is_dropped` | Qdrant is not truth |
|
||||
| `an_unreachable_embedder_is_refused_unavailable_never_an_empty_page` | Honest availability |
|
||||
| `another_session_is_answered_while_a_semantic_search_is_outstanding` (blocked mock embedder) | The loop never waits on the network |
|
||||
| `cursor_with_semantic_is_refused` | Not pageable |
|
||||
|
||||
- **Live (operator-approved):** on the scratch instance, a paraphrase query returns a document that shares no exact words with it, and the hit resolves through the same page type.
|
||||
|
||||
**Estimate:** about 350 lines added. No new dependencies.
|
||||
|
||||
**Hands context:** about 150k tokens.
|
||||
|
||||
## Cut 13a. The envelope moves to its owner; the client core
|
||||
|
||||
- **Repo/branch:** Huginn, after 11b.
|
||||
- **Moves (ownership only; one commit that builds on its own):**
|
||||
- `huginn-daemon/src/envelope.rs` (223 lines) moves to `huginn-mind/src/envelope.rs`: `encode_request`, `decode_request`, the response pair, `OperationFailure` and `FAILURE_SCHEMA`.
|
||||
- `huginn-mind` gains `base64`. It already has `cultnet-rs` through RS-3.
|
||||
- The daemon imports the module from `huginn-mind`.
|
||||
- Rule: **one spelling of the operation envelope.** The client and the daemon cannot drift apart.
|
||||
- Verified by the daemon's existing envelope tests, unchanged, run from their new home.
|
||||
- **Adds, `crates/eureka-state` (library part).**
|
||||
- `Cargo.toml`: `huginn-mind` (types, envelope), `cultnet-rs`, `anyhow`, `serde`, `serde_json`.
|
||||
- `HuginnClient { endpoint: SocketAddr, instance: Slug, timeout }`:
|
||||
- its one method is `call(HuginnMindRequest) -> Result<HuginnMindResponse, ClientError>`;
|
||||
- the transport is `CultNetRudpSocketTransportConnection` on `CULTNET_OPERATION_CONNECTION_ID`, channel `schema`, as the daemon expects (`serve.rs:283`);
|
||||
- it resolves `Deferred` through `cultnet_rs::fetch_content`, capped at BP-3's `MAX_DEFERRED_BODY_BYTES` (imported, not copied);
|
||||
- `ClientError::Unavailable { endpoint, detail }` is the only transport error.
|
||||
- Owner: none over state; the client is a transport shim.
|
||||
- Forbidden: opening a store, calling `Mind::open`, `admit` or `admit_prepared`, deriving status, or writing any file.
|
||||
|
||||
**Verification** (Yggdrasil):
|
||||
|
||||
| Test | Rule |
|
||||
|---|---|
|
||||
| `every_operation_round_trips_against_a_live_daemon` (an in-process daemon on `127.0.0.1:0` with a temp mind) | Pipeline smoke |
|
||||
| `a_closed_port_is_unavailable_naming_the_endpoint_within_the_timeout` | Loud unavailability. The timeout is a function of the configured value, so probe two values. |
|
||||
| `a_deferred_answer_resolves_to_the_same_page_as_a_direct_one` | BP-3's client side |
|
||||
| `a_foreign_instance_is_an_answer_not_an_error` | Refusals are data |
|
||||
|
||||
- **Negative grep:** `rg -n "Mind::open|\.admit\(|admit_prepared|OwnedRedb|std::fs::(write|create)" crates/eureka-state/src` is empty.
|
||||
|
||||
**Estimate:** about 300 lines added. The move is net 0.
|
||||
|
||||
**Hands context:** about 120k tokens.
|
||||
|
||||
## Cut 13b. The MCP surface, the build and the install
|
||||
|
||||
- **Repo/branch:** Huginn, after 13a.
|
||||
- **First:**
|
||||
1. Hands builds a 20-line scratch rmcp server whose tool schema carries a `$defs`-internal `$ref`, and confirms `claude mcp` lists and calls it. It is not committed.
|
||||
2. Hands confirms the `claude mcp add` syntax from `claude mcp add --help`.
|
||||
- **Adds.**
|
||||
- The binary `eureka-state` (`src/main.rs`) uses `rmcp = "2.2.0"`, the version Epiphany's lockfile already resolves, with `server`, `macros` and `transport-io`, plus `tokio` (`rt`, `macros`, `io-std`) and `schemars`.
|
||||
- **Configuration comes from the environment:** `EUREKA_INSTANCE` and `HUGINN_ENDPOINT` (`rudp://10.77.0.1:17872`). Missing or invalid configuration does not stop startup. `whoami` reports it, and every other tool refuses it, typed. An MCP server that dies at startup vanishes silently from Claude Code.
|
||||
- **Four tools, 1:1 with the wire.** The old `open_items`, `rulings_in_force`, `stewardship` and `history` tools are deleted designs. Their recipes move to the skill (Cut 15).
|
||||
|
||||
| Tool | Input | Output |
|
||||
|---|---|---|
|
||||
| `whoami` | `{}` | `{ instance, endpoint, reachable, status: Option<MindStatus> }` |
|
||||
| `admit` | `{ faculty, agent, session, documents }` | `PipelineAdmissionOutcome`. `instance` and `provenance.tool = "eureka-state"` are filled in by the server, **never taken from input**. |
|
||||
| `view` | `{ id }` | `Option<PipelineDocumentView>` |
|
||||
| `query` | `{ selection, semantic? }` | `PipelineSelectionPage` |
|
||||
|
||||
- Refusals are successful tool results carrying the typed refusal. `Unavailable` is a result with `isError: true` and the typed body. Malformed input is `invalid_params`.
|
||||
- **Input schemas are self-contained:** schemars output with the CultLib selection schema bundled into `$defs`, and no remote `$id` reference.
|
||||
|
||||
**Verification.**
|
||||
|
||||
- On Yggdrasil, a JSON-RPC exchange piped into the Linux build: `initialize`, `notifications/initialized`, then `tools/list`, which lists 4 tools and contains no `$ref` outside `#/$defs`. Then `whoami` against a closed port, which gives `reachable: false` and no JSON-RPC error.
|
||||
- Test `admit_ignores_any_instance_in_input`. The rule: declared identity is configuration, not a per-call argument.
|
||||
- **Windows (Starfire; Windows-only work, one job, nothing else running):** `cargo install --locked --path crates/eureka-state --root C:\Users\Meta\.eureka`. Hands reports the binary path and the build log's target directory.
|
||||
- **Registration is the operator's:** `claude mcp add --scope user eureka-state -e EUREKA_INSTANCE=<Q-H4> -e HUGINN_ENDPOINT=rudp://10.77.0.1:17872 -- C:\Users\Meta\.eureka\bin\eureka-state.exe`, in the syntax confirmed by First.
|
||||
- **Operator only, after Cut 14:** `whoami` from a Claude Code session answers `reachable: true`. This is the only proof of the Windows RUDP path over WireGuard.
|
||||
|
||||
**Estimate:** about 400 lines added. +1 binary, +`rmcp`, +`tokio`.
|
||||
|
||||
**Hands context:** about 180k tokens. rmcp's API is the unknown.
|
||||
|
||||
## Cut 14. Interim deployment (under Q-H1 A and Q-H2 A)
|
||||
|
||||
- **Repos:** gamecult-ops `main`, plus one line in Huginn's `README.md` for the run command. Depends on 11a, because the options exist from then.
|
||||
- **Deletes first:** from `scripts/backup-gamecult-authority-yggdrasil.sh:74-76`, the absent `epiphany-swarm.service` and `epiphany.service` in the freeze loop (under Q-H2 A).
|
||||
|
||||
**Adds, gamecult-ops.**
|
||||
|
||||
- **`systemd/huginn.service`:**
|
||||
- `User=huginn`, with a system user created by the runbook;
|
||||
- `ExecStart=/opt/gamecult/huginn/current/huginn-daemon --state-root /var/lib/gamecult/huginn --instance <Q-H4> --bind 10.77.0.1:17872 --qdrant-url http://127.0.0.1:6333 --ollama-url http://10.77.0.4:11434 --embedding-model qwen3-embedding:0.6b`;
|
||||
- `Restart=on-failure`;
|
||||
- `After=`/`Requires=wg-quick@wg-gamecult.service`, because the bind needs the interface;
|
||||
- **no ordering on Qdrant or Raven**, because the index is a projection;
|
||||
- `StateDirectory`/`ReadWritePaths` limited to `/var/lib/gamecult/huginn`;
|
||||
- `ProtectSystem=strict`;
|
||||
- `MemoryMax=768M`, which covers BP-3's 256 MiB deferral budget plus the mind.
|
||||
|
||||
Hands confirms the WireGuard unit's real name with `systemctl list-units` before writing `Requires=`.
|
||||
- **`runbooks/huginn-yggdrasil.md`:**
|
||||
- Authority: the mind file is truth; the collection is disposable; Qdrant is shared with voidbot, and **this is the undeclared coupling ruling 21 wanted declared**; the embedder is Raven.
|
||||
- Release body: build in `rust@sha256:4c2fd73e…` (the Odin binding's pinned image) at an exact revision, with `cargo build --locked --release -p huginn-daemon`. Install to `/opt/gamecult/huginn/releases/<sha>/`, then flip `current`.
|
||||
- Install: user, directories, unit, and the ufw rule `ufw allow in on wg-gamecult from 10.77.0.2 to 10.77.0.1 port 17872 proto udp comment 'Huginn mind RUDP from Starfire'`.
|
||||
- Verification.
|
||||
- Recovery: stop the unit, delete `huginn_mind_<slug>` to force a rebuild, restore `minds/` from backup.
|
||||
- **Retirement:** this unit, the ufw rule and the release section die in 14-I.
|
||||
- **`inventory.md`:** allocate 17872 to Huginn, and add a Huginn line to the Yggdrasil services list. The stale Nightwing embedding text (P2) belongs to the embedding migration's owner. Hands does not rewrite it. Hands reports it.
|
||||
- **Backup (under Q-H2 A):**
|
||||
- add `var/lib/gamecult/huginn` to the tar list;
|
||||
- add `huginn.service` to the freeze loop;
|
||||
- install and enable `gamecult-authority-backup.timer`, which is operator-run.
|
||||
|
||||
**Authority map (interim).**
|
||||
|
||||
- Owner: the operator owns deployment and continuity until 14-I. Huginn owns its state.
|
||||
- Forbidden writers: nothing but the daemon writes `/var/lib/gamecult/huginn`. Nothing but the runbook's release step writes `/opt/gamecult/huginn`.
|
||||
- Deletion line: 14-I.
|
||||
|
||||
**Verification (operator only; Hands does not deploy):**
|
||||
|
||||
1. The unit reaches `active`.
|
||||
2. The daemon's stderr says it is serving `10.77.0.1:17872`.
|
||||
3. From Starfire, `eureka-state` `whoami` answers `reachable: true`.
|
||||
4. **Restart preserves state:** admit, run `systemctl restart huginn`, then `view` the document.
|
||||
5. **Negative:** a `whoami` from Raven (`10.77.0.4`) times out, because ufw admits Starfire only.
|
||||
6. **Negative:** stop the unit. The next `admit` is `Unavailable` naming the endpoint, and no file appears on Starfire.
|
||||
7. The collection `huginn_mind_<slug>` exists, with its metadata.
|
||||
8. **Backup:** run the backup service once. `tar --list` shows `var/lib/gamecult/huginn/minds/<slug>/mind.redb`.
|
||||
|
||||
**Estimate:** about 150 lines of ops configuration and prose. +1 unit, +1 ufw rule, +1 port, +1 backup path, and backups resume.
|
||||
|
||||
## Cut 15. Skill wiring (under Q-H6 A)
|
||||
|
||||
- **Repo:** `GameCult/Eureka`, `main` at `19487f0` (`~/.claude/skills/eureka`). Depends on 13b being registered and 14 being live.
|
||||
- **The old Cut 15's anchors are stale:** `SKILL.md` is now 533 lines, and "Mind Steward" is now Life. Hands re-takes them. The sections to change:
|
||||
|
||||
| Where | Change |
|
||||
|---|---|
|
||||
| `SKILL.md:8-19` (the substrate paragraph) | Eureka keeps questions, rulings, cut specs, reports, verdicts, findings, follow-ups and resolutions as typed documents in the instance's mind (Huginn), through the `eureka-state` tools. The committed target and map keep what has no kind. Delete "committed docs … memory files" as the store. |
|
||||
| §0 (`:58-72`) | Self calls `whoami` first. If the organ is unreachable, stop and say so. Then Self admits `stewardship` (if absent), `campaign` and `target`. |
|
||||
| §0b and §1 (`:74-165`) | Imagination admits `question`s and `cut_spec`s. The map document keeps body facts, the model page and rationale, and **has no per-cut spec sections**. "Commit the map" applies to that prose only. |
|
||||
| §2 (`:167-189`) | A ruling is admitted with `answers`, `choice` and `operator_quote`. Supersession is a `resolution`, never an edit. |
|
||||
| §3 (`:190-300`) | The Hands brief names the `cut_spec` id and the rulings in force. Hands admits a `cut_report`. `cargo-mutants` survivor triage goes into `cut_report.deviations`, or into a follow-up. |
|
||||
| §4 (`:304-335`) | Soul admits one `verdict` plus its `finding`s. Admitting is a Mind write, not a Body edit, so Soul stays read-only on repos. |
|
||||
| §5 and §6 (`:337-399`) | Triage outcomes are `resolution`s and `follow_up`s. The status header becomes a set of selection recipes. Self reconciles the ledger from `cut_report.structural_delta`. |
|
||||
| Self's discipline (`:401-451`) | Never restate typed state in prose. When the organ is unreachable, stop; never keep a second copy (the target's invariant). **Keep "Record the substrate you needed and did not have".** |
|
||||
| `references/briefs.md` | Each faculty's "read first" becomes selection recipes. Hands gets `view <cut_spec id>`. A new **Rehydrate** section: `whoami`, then the recipes. |
|
||||
| `references/cut-map.md` | Rewrite it as "Campaign state": the document set, the keys, the resolution matrix, and **selection recipes** in RS-3's vocabulary (below). The prose-map template shrinks to body facts, model page and rationale. |
|
||||
| `references/changelog.md` | A dated entry with this map's evidence. |
|
||||
|
||||
**Selection recipes** to write. These replace the deleted presets, spelled in RS-3's aliases:
|
||||
|
||||
- **rulings in force:** `schemas: [epiphany.pipeline.ruling.v2]`, `in_force = true`, `root = <campaign>`;
|
||||
- **open items:** questions, findings and follow-ups with `in_force = true`;
|
||||
- **specs with no report:** `cut_spec`s, with the hop `cut_spec` inbound and absent (Hands must confirm that the substrate can express absence; if it cannot, this recipe is two queries and says so);
|
||||
- **a subject's history:** `resolution`s with `subject` = X, `descending`.
|
||||
|
||||
**Verification (Soul, by reading plus one live exercise).**
|
||||
|
||||
- `rg -n "status header|WriterLeaseHeld|lease|open_items|rulings_in_force|Mind Steward" ~/.claude/skills/eureka` hits only changelog and history lines.
|
||||
- Every leaf kind is named by at least one brief.
|
||||
- No brief asks for prose relay of a typed artifact.
|
||||
- **Live:** a fresh agent given only the skill and the instance slug runs `whoami` and the rulings-in-force recipe against the live organ, and both return typed answers.
|
||||
|
||||
**Estimate:** prose shrinks. `cut-map.md` is replaced, not extended.
|
||||
|
||||
## Cut 12. Parked (Q-H5)
|
||||
|
||||
Recorded, not built:
|
||||
|
||||
- A hand-off is two admissions, one per mind, sender first. Each side's derivation already exists (P8).
|
||||
- Import waits for a consumer, most likely Epiphany's adoption campaign.
|
||||
- Old Cut 12's in-process `hand_off(&mut from, &mut to)` is infeasible under one daemon per mind (P7).
|
||||
|
||||
## 14-I. Idunn path (blocked; not this week)
|
||||
|
||||
Named so that the interim unit has a deletion line. Its dependencies:
|
||||
|
||||
1. **Route continuity** (`Idunn/docs/route-continuity-target.md`, invariants 1, 2 and 5, and its Q1 b):
|
||||
- Huginn declares no `odin.verse-rendezvous` dependency, so it is a route-proof target.
|
||||
- No nginx reload on observation, because every reload breaks RUDP sessions.
|
||||
- Stateful deadlines resolve (its Q3).
|
||||
2. **A shared Rust runtime participant** in `cultnet-rs`: presence signing, activation credential, lease adoption and the challenge answer. Otherwise Huginn writes a third copy (P9). The owner is CultLib.
|
||||
3. **Idunn has no path from a selected dependency's endpoint to a service argument.** Qdrant and Ollama will be `external-operator-binding` dependencies whose endpoints are duplicated in `argument_bindings`, as Ghostlight duplicates Odin's. Route this to Idunn.
|
||||
4. **The slot for `minds/<slug>/mind.redb` is `external-file`**, because redb is not Idunn's single-file CultCache format. It is one slot per instance, so the recipe names the instance.
|
||||
5. Whether the huginn-daemon hub (`serve.rs:138`, which drops other connection ids) receives Idunn's RUDP challenge. Not probed.
|
||||
|
||||
The later Verse-presence cut (Q23 A) comes after 14-I.
|
||||
|
||||
## Subtraction ledger (estimates; Self reconciles at each landing)
|
||||
|
||||
| Cut | Removed | Added | Dependencies, targets, formats |
|
||||
|---|---|---|---|
|
||||
| 11a | `NoIndex` from the binary | ~650 | +`ureq`, +`serde_json` (daemon); +1 collection per mind |
|
||||
| 11b | the unwired refusal and its test | ~350 | none |
|
||||
| 13a | 223 (moved) | ~300 | +`base64` in `huginn-mind` (moved) |
|
||||
| 13b | the old design's 3 preset tools | ~400 | +1 binary; +`rmcp`, +`tokio` |
|
||||
| 12 | the whole cut (parked) | 0 | none |
|
||||
| 14 | 2 dead freeze entries | ~150 ops | +1 unit, +1 ufw rule, +1 port; backups resume |
|
||||
| 15 | per-cut prose sections, status headers | prose shrinks | `cut-map.md` replaced |
|
||||
|
||||
## What in the old specs (cut map lines 5490-5866) is now wrong
|
||||
|
||||
1. **Cut 11's `pending_index` document.** A mind admits exactly fifteen types (`mind.rs:84-88`), and this would be derived state in the truth store. It is replaced by reconciliation by set difference.
|
||||
2. **Cut 11's "indexing at admission, in the same call".** It blocks the single-threaded serve loop (P7). A cold embed takes 5-6 s (P3).
|
||||
3. **Cut 11's flags "following `epiphany.service`'s spelling (R16)" and its `10.77.0.1:11435` precedent.** Both endpoints are dead (P2).
|
||||
4. **The compatibility record ported from voidbot.** Its `embedderId` includes the host URL (P4). Compatibility keys on model and digest instead.
|
||||
5. **One shared collection with an `instance` payload.** It becomes one collection per mind.
|
||||
6. **Cut 12's two-mind in-process hand-off.** It is infeasible (P7), and the hand-off already works per mind (P8).
|
||||
7. **Cut 13's seven tools.** They become four, after the operator deleted `open_items` and `history` as presets.
|
||||
8. **Cut 13's "tool schemas equal the published schemas".** The published request schema will `$ref` CultLib's `$id` (RS-3 F9). MCP tool schemas must be self-contained.
|
||||
9. **Cut 13 is missing Deferred handling** (BP-3 §7).
|
||||
10. **Cut 14's `kind = cultcache-directory`.** No such kind exists (P9).
|
||||
11. **Cut 14's "`shared-infrastructure` dependency on Qdrant, Ghostlight's shape (`:168-187`)".** Ghostlight declares only Odin, and non-Idunn infrastructure is `external-operator-binding` (P9).
|
||||
12. **Cut 14's `idunn-huginn` unit name** in the backup freeze loop. Units are named per incarnation, `idunn-<target>-<sha>` (P9).
|
||||
13. **Cut 14's "Named owner: `gamecult-authority-backup.service`, daily at 03:20 UTC".** It is not installed, and it would refuse to run (P10).
|
||||
14. **Cut 14's route through nginx `rudp://10.77.0.1:17872` with presence health.** Huginn has no runtime participant, and route continuity is rewriting that path (P9).
|
||||
15. **Cut 15's anchors, and its "Mind Steward" name.** Its `open_items` plus `query` status header and its rehydration sequence both use deleted tools.
|
||||
16. **Q14 C** (move the mutation harness into the skill repo). It is moot: the harnesses were retired.
|
||||
Reference in a new issue