The client decodes every published refusal and the grant and encodes the request
and each message back to the same bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delete the classifier, initiative, burn, base-rate dial, shares, Envelope dollars,
persona turn max_cost, --max-budget-usd and the Self-run settle. Add tempo::tally
and tempo::pick, self-declared urgency from the reply as a Pulse, the usage line in
Persona input, and the wire-v4 connector (lease v2, usage_readout).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opens claude only under an open Self run; one pure decide() owns flush, grace, headroom and escalation; process-group signals; ends on child exit or EOF. Soul s3 holds (eureka-body:verdict:cut-mind-self-run.s3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/public_surface.rs reads the library source the way a consumer sees it
and compares every pub item with its signature, pub fields, derives, impl
headers and every path-looking string literal to tests/public_surface.txt.
A new pub static or pub fn under any name, a derive onto Grant, or a root
read from another path fails it. It replaces the three compile_fail
doctests that named with_root, TEST_ROOT and control_root. Dev-dependencies
syn, quote and proc-macro2, already in the graph through serde's derive.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the eureka-state Cargo.toml comment block to main's, regenerates
Cargo.lock from main's with cargo update -w, adds the Persona and PersonaPut
arms to the CLI's unexpected-answer match and the persona field to the CLI
test MindStatus literals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new bin of eureka-state, crates/eureka-state/src/bin/huginn/: the agents'
shell door to the mind. Text out, typed exit codes, strict decode of
agent-written JSON (unknown key paths refused, never values), the named
campaign-state recipes, and a read-back after every committed admission.
lib.rs, main.rs and tests/mcp.rs are untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
put_persona replaces the mind's gamecult.persona_state.v0 whole through the
commit primitive: schema-valid against the published file, personaId is the
instance, canonical provenance, CAS on updatedAt, monotone updatedAt. The
document is an opaque seventeenth stored type, one per mind. Wire operations
persona_get and persona_put, MindStatus.persona, HuginnClient methods and the
eureka-state persona tools. Published wire schemas follow in the next commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Soul s2: under a permissive umask the store was group/world-writable between
CultCache's rename and the chmod, and an fd opened there survived it. The
mind-control binary now sets umask 077 first (the library never touches the
umask). The writer refuses to write through an existing directory, lock or store
that is not root's or is group/world-writable, with the typed UnsafeStore error
naming path, mode and owner. Tests: a non-root open-for-write probe against the
real binary under umask 000; refusals for 0777/0775 directories, 0666/0664
locks and stores, and a non-root-owned directory; accepted root installs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Package and lib become eureka-pipeline / eureka_pipeline; the crate joins the
workspace; huginn-mind's git dependency on GameCult/Epiphany is replaced by
the path, and Cargo.lock is regenerated (the Epiphany source entry is gone).
Every epiphany_pipeline mention in huginn-mind, huginn-daemon and eureka-state
is renamed. The schema-publication test reads ../../schemas/cultnet. The
leaf's license-file key (../LICENSE, which Huginn has no file for) is dropped.
Type ids, the epoch and every schema_id string are untouched. Schemas README
and the pipeline section of schemas/cultnet/README.md are rewritten for the
new home; README and AGENTS no longer say the leaf is pinned by git. New test:
the catalogue lists exactly the schema files beside it.
Not yet built or tested; verification follows on Yggdrasil.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the leaf from 73edee5cdaddece8bda9c6c3271e8ddd064337f9 to the
leaf-key-bound merge commit on Epiphany main,
ef9568655e75bb6cb362a038e2739a5ac5e777ee. Cargo.lock follows with that one
line; no other lock line moves.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
eureka-state on Windows could not reach the daemon: the cultnet-rs RUDP
client bound 127.0.0.1, and a loopback-bound socket cannot send to
10.77.0.1 (os error 10051). CultLib 8fc74c7 binds the unspecified address
for a remote endpoint. The daemon and the client move together, because
the ack cuts between the revs changed the RUDP handshake.
epiphany-pipeline moves to Epiphany 73edee5, which pins the same CultLib,
so one cultcache-rs is in the graph and the pipeline's CultCache types
stay Huginn's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The eureka-state binary serves whoami, admit, view and query over stdio (rmcp
2.2.0), each one HuginnClient call. EUREKA_INSTANCE and HUGINN_ENDPOINT are
process configuration: the server fills in the instance and provenance.tool, so
input can name neither. Bad configuration never stops startup; whoami reports
it and every other tool refuses with the typed body. A mind's refusal is a
normal result; Unavailable, Rejected and Misconfigured are results with
isError. The query input schema bundles CultLib's selection schema into $defs
(vendored; a test pins it to the cultnet-rs revision).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
One call carries one HuginnMindRequest to a daemon over CultNet RUDP on the
operation connection and returns the mind's own HuginnMindResponse. Each call
is its own session and nothing survives it. A Deferred answer is fetched on
that session through cultnet-rs's fetch_content, capped at huginn-mind's
MAX_DEFERRED_BODY_BYTES, and decoded, so a caller never sees the deferral.
Refusals are answers. ClientError::Unavailable is the only error and names the
endpoint. The client opens no store and admits nothing.
Tests run a real daemon in-process on a loopback port over a temporary mind,
plus scripted servers for the failures a daemon cannot be made to produce.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
envelope.rs (encode/decode request and response, OperationFailure,
FAILURE_SCHEMA) leaves huginn-daemon for huginn-mind, next to the wire types
it wraps. The daemon imports it and no longer re-exports it. base64 moves
with it; the daemon keeps base64 because serve.rs measures and decodes
payloads. schemars leaves the daemon, whose only user was the envelope's
OperationFailure derive. The two envelope tests run unchanged from their new
home on huginn-mind fixtures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
huginn-mind::index owns what of a document is searchable: index_text is a
pure function per kind, Mind::index_entries builds the entries with the
writing receipt's ordinal, INDEX_TEXT_VERSION says when the rule changed.
huginn-daemon::index owns the projection. A Projector reconciles the mind
against the collection at startup (the only place a point is judged
missing: set difference on point ids, rebuild when the model name, digest,
dimensions or text version differ, refuse a collection that is not this
instance's) and one worker thread embeds and upserts in batches of 32,
retrying with a 30 s to 10 min backoff. IndexSink::committed builds entries
and leaves them in an inbox; admission never waits on the embedder or on
Qdrant. Whoami reports the worker's health as IndexStatus. The Ollama and
Qdrant adapters are the only files holding HTTP or JSON (ureq 3, no TLS).
The daemon now requires --qdrant-url, --ollama-url and --embedding-model;
NoIndex leaves the binary.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The four hand-written filters, the order and the cap are deleted:
PipelineQuery, PipelineQueryPage, QUERY_LIMIT_MAX, Reader::matches, ordered,
repo_matches. Mind::query(selection, semantic) validates through
cultnet_rs::validate, closes the values door (rows.rs refuse_values, S6),
takes the snapshot from the cursor's own asOf (an asOf beyond the head is
CursorInvalid), builds Reader::at(as_of) so every derivation runs over the
documents admitted at or before it, and evaluates through cultnet_rs::select.
The page carries matched, asOf, the next cursor, headers or whole views by
projection, and typed edges when the selection hops.
rows.rs is the organ's vocabulary: SelectionRow (a Row), Vocabulary (a
RowSet: thirteen schemas, eleven aliases, fifteen roles) and the value door.
huginn-mind links cultnet-rs at the rev the daemon pins. Selection refusals
map totally onto SelectionInvalid, CursorInvalid or Unavailable. The wire's
Query carries CultLib's selection by $ref, and both published schemas are
regenerated. The relations RS-2 unpinned are re-pinned as selections.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
One pin move: huginn-mind pins cultcache-rs at 6251cbc and the leaf at
4891f2e5; huginn-daemon pins cultnet-rs at 6251cbc. cargo tree shows one
cultcache-rs and one cultnet-rs per crate.
Adopting RS-L, which the new leaf carries:
- Campaign, cut-spec, question and ruling fixtures carry a Title; the
question and ruling fixtures gain the new required title field.
- The epoch literals in daemon and envelope tests move v1 -> v2. A store
written at an earlier epoch is refused at open (ForeignEpoch); no key
migration.
- Derived stewardship, hand-off and resolution keys are now the lowercased
OrgRepo::identity(); test key literals follow.
- The two published wire schemas are regenerated (Title, OrgRepo bound and
description); the byte-for-byte pin test passes.
CultLib 6251cbc gives CultNetMessage::Error code and details; the four
construction sites and one pattern in huginn-daemon take None and `..`. No
behaviour change: None is skipped on the wire. mind 70 and daemon 18 pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Epiphany codex/eureka-pipeline-state b3ae787b extends the leaf's door
test with the 64-byte whole-name and per-label bounds and NUL, adds
three matching mutation entries, and corrects the entries file header
that still named the deleted repo-local harness path. One cultcache-rs
throughout: epiphany-pipeline, cultnet-rs and huginn-mind all still
resolve to CultLib a0813c6e.
Verified: cargo test --workspace (huginn-mind 65/65, huginn-daemon
20/20, eureka-state 0/0), cargo tree -i cultcache-rs shows one node.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
N1 (medium-low). Moves the epiphany-pipeline pin to the leaf's new commit
(9d3a3efd), which opens Slug::validate_slug. Huginn's own cultcache-rs pin
is untouched; cargo tree -d still carries exactly one cultcache-rs.
Mind::require_instance now validates the declared name's grammar before
comparing identity, so admission's A1 and every read refuse a bad name
identically, through one door. Mind::open and open_with validate the
declared instance before path_for ever runs, so a Slug carrying `..` and a
path separator (Soul's outer/inner probe) is refused before any store is
created, not after it has already escaped the state root.
require_grammatical_instance and its daemon-side branch in daemon.rs are
deleted: the throwaway PipelineInstance wrapper it built is no longer the
only door onto the leaf's grammar. The refusal now names the field a
caller actually held the declared name under (declared, or instance) and
the whole declared name, not the old wrapper's instance.instance field or
a single failing label.
Adds a leaf-escape regression test pinning Soul's probe and asserting no
store is created, and a fullwidth-instance test showing Admit and Query
refuse identically. Updates the cut10 grammar-fold mutant (S3wide) to the
new function shape and the affected daemon test to the new field name.
N5 and N6, prose only. The cut10 entries gain a note by S1 and S1d: neither
mutant "dies on all three" faults its shared fixture carries, contrary to
an earlier claim; S1d exempts the orphan fault by construction and is
killed by the undecodable-document block instead, the next one the test
body reaches. The cut9 header's self-contradiction at lines 18-25 is
resolved (nothing in the suite exercises the door, not "nothing reaches
it"), its self-reference now points at store.rs:18-19, and its line-1
range is corrected to V1-V24L.
All three suites rerun clean after the pin move: cut8 67/67, cut9 38/38,
cut10 65/65, every M0 green, restores hash-verified. cargo test --workspace:
huginn-mind 61/61 (was 60), huginn-daemon 18/18 (was 17).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`huginn-daemon` becomes a library and a binary. `Daemon` holds one mind and
dispatches a `HuginnMindRequest` to the `Mind` method that answers it, with no
socket and no clock: `now` is the caller's. Every read that names an instance
is put to `Mind::require_instance` before the method runs, so ruling 14 is
checked once, by its owner, whichever side of the wire the request came from.
`IndexSink` is where Cut 11 attaches; its error is logged and the outcome is
what the mind decided.
`envelope` carries the vocabulary in CultNet's `cultnet.operation_request.v0`
and `cultnet.operation_response.v0` as base64 of named MessagePack, which is
what the C# reference and Eve's plugin ABI speak. The envelope's operation
string is checked against the decoded payload rather than trusted. A message
that does not decode is answered with a typed failure naming its code, and
reaches no mind.
`serve` binds one non-blocking socket on CultNet's operation connection id and
answers each frame on the session it arrived on. A refusal is an answer with a
`rejected` status, never a transport error. The two wire schemas go out on a
schema catalog request. A hostile datagram and a departed peer are logged and
served past. `startup` is the one place the mind opens and the socket binds,
in that order, so a mind that will not open leaves the port free.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`huginn-mind::wire` names one request per `Mind` method plus `whoami`, and one
response per method plus the read side's refusal. The payloads are the
admission and read types unchanged, so a client speaks the organ's own
vocabulary rather than a projection of it. `operation()` and `instance()` say
what the envelope carries; `status()` derives the envelope's status from the
response, and a refusal is data in that response rather than a transport
failure.
`Mind::status` answers `whoami` from the image every time it is asked: the
instance, the schema epoch, and how many pipeline documents and receipts the
mind holds. Nothing stores a status and no configuration supplies one.
`schemas/cultnet/` publishes the JSON schema of each wire type, derived from
the type and pinned to it byte for byte by a test, so the schema catalog
cannot advertise a shape the crate does not speak.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pin moves to Epiphany `5cda0886`, where `PipelineRef::validate_ref` opens
the leaf's grammar to a reader. `view` and `history`'s subject scope ask it
before they look, so a ref whose kind and id disagree is refused instead of
reporting the nothing a lookup finds. `query`'s `campaign`, `cut` and `repo`
filters take a `Slug`, a `Label` and an `OrgRepo`, not refs, and are unchanged.
The refusal is `MindRefusal::Document`, the leaf's `InvalidFormat` wrapped
where the organ already wraps the leaf's four. A new variant would be a second
name for one rule, and the grammar it names lives in the leaf.
`a_ref_whose_kind_and_id_disagree_is_refused_by_both_doors` pins both doors on
a disagreeing kind and on a local no writer composes, and pins that absence is
still absence: a well-formed id of a document this mind does not hold answers
`None` and an empty history as it always did. The history test's malformed
scope asserts the refusal where it asserted an empty answer.
`V24` and `V24L` in `tools/eureka-cut9-mutations.psd1` are the validation
dropped and the validation applied at `view` alone. `V10L` leaves the suite
with its reason in the header: a subject compared by `id` alone can no longer
differ from one compared whole, since both sides' kind is now a function of
the id, so the weakening is unreachable and a mutation that cannot fail is not
a proof.
`cargo test -p huginn-mind --lib` 57 passed, 0 warnings; `cargo check
--workspace` clean; Cut 8 and Cut 9 suites green-and-killed through the
harness. The lock moves the one pin.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The pin moves to Epiphany `d5a36c2a`, where a resolution's key ends in its
per-subject sequence and a stewardship's in its per-(instance, repo) one. Two
fields arrive with it, so `stewardship` and `resolution` in the fixtures set
`sequence: 1` and `stewardship_n`/`resolution_n` say otherwise when a test
needs a second record. Every literal key that gained `.n1` moved with it,
`id_of` included.
The rules the sequence was for:
- `in_force` is recursive. A document is in force when no resolution that is
itself in force names it, so a withdrawn resolution stops closing its
subject. The old one-level version counted a withdrawal as if it still
stood, which is half of why a reopened subject could not be resolved again.
- The other half was A10. `refuse_collisions` no longer has a `Resolution`
arm: a resolution key is not one-per-subject any more, so a collision on it
means what it means for every other kind, and whether a subject is closed
is A8's question.
- A8 asks it, for both sequenced kinds, in the same two steps. The sequence
is the writer's and must be the previous plus one over image and batch,
counting every record of that scope but this document --
`ResolutionOutOfSequence`, `StewardshipOutOfSequence`. Then no record of
that scope may still be in force -- `AlreadyResolved`, `AlreadyStewarded`.
With both, at most one stewardship of a repo stands on a mind, which is
what `stewardship_of` has been assuming.
- Q19 A is enforced rather than described: a resolution whose subject is a
resolution of a resolution is `IncompatibleResolution`. A withdrawal cannot
be withdrawn; a closure is reinstated by resolving the subject again.
- `derive` sets a sequence on each write it makes, from the same image the
rule reads.
Two existing tests move with the rules. `subject_resolves_at_most_once` is
now `a_subject_with_a_resolution_in_force_refuses_another` and separates the
two refusals. The hand-off test's last case used to be an A4 collision
between a batch's own stewardship and the derived one; the derived write now
takes the sequence after the batch's, so there is nothing left to collide and
the batch is refused as out of sequence instead.
31 tests, 0 warnings; `cargo check --workspace` clean.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Cut 8, Huginn half, commit 1 of 4: the store port, the opener and the
identity rules. `huginn-mind` gains its dependencies (`cultcache-rs` at
Epiphany's pin `a0813c6`, `epiphany-pipeline` at `a65c6420`, the Epiphany
half of this cut), `store.rs` with the one narrow `MindStore` trait that
injects compare-and-swap, `mind.rs` with `Mind::open` and `open_with`, and
`refusal.rs` with the organ's refusal vocabulary.
The opener refuses in order and attaches nothing until every gate passes:
a foreign type, a foreign epoch, a missing identity, an identity naming
another instance. The path under the state root is derived from the slug;
the `instance` document is the authority, so a store copied into another
instance's directory is refused. The owned store's exclusive lock is the
single-writer mechanism: a second `Mind::open` of the same path is
`MindAlreadyOwned`.
The receipt type and the two provenance shapes (`Faculty`,
`PipelineProvenance`) land here rather than in the later commits because
the opener registers the receipt's type id, and their only consumer is the
receipt; the commit primitive itself is the next commit. `MindRefusal`
carries a serde remote definition of the leaf's `PipelineRefusal`, which
derives neither `Serialize` nor `JsonSchema` at the pinned rev; it is
compiled against the leaf's type so a leaf change breaks the build rather
than drifting.
`Cargo.lock` is regenerated and gains 90 packages. The map predicted a
CRLF-to-LF rewrite of the whole file; the file was already LF in the index
and stays LF, so the diff is additions only.
README and AGENTS now say `huginn-mind` is live and the other two crates
are stubs.
Builds: `cargo check -p huginn-mind --lib --tests` and `cargo test -p
huginn-mind --lib` (4 tests) pass. This commit carries dead-code warnings
on the store and cache accessors and the refusing test store; commits 2 and
3 consume them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Three stub crates following Odin's layout: huginn-mind and eureka-state as
libraries, huginn-daemon as the binary. Edition, license and publish are
workspace-owned. .gitignore gains target/ and drops the four entries that
named build outputs no longer produced here.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>