Commit graph Huginn/crates
Author SHA1 Message Date
Meta Vens
5cccab6899 mind-body: the binary restricts its umask; the writer refuses unsafe installs
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>
2026-10-09 16:39:19 +02:00
Meta Vens
2377fa6d40 mind-body: the mode test writes once, so a skipped lock mode is not repaired by the second write
cargo-mutants on the diff missed deleting the negation before the lock check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:28:39 +02:00
Meta Vens
d125b95c8d mind-body: the wrong-dial fixture registers the brake it reads beside
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:25:26 +02:00
Meta Vens
550037e0b3 mind-body: pin the creation modes, the non-root refusal and the dial decode branch
Modes under umask 000/022/077 via a re-executed test child; a non-root user
(setpriv uid 65534, skipped with a message when not root) cannot write the store
but can read it; an undecodable or NaN dial reads Held(Undecodable) whatever the
brake says, which kills the fail-open default-dial mutant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:24:58 +02:00
Meta Vens
0a3b9c3ba5 mind-body: the writer creates the control store 0755 and 0644 whatever the umask
Soul s1: created under umask 000 the directory was 777 and the files 666, so a
non-root user could write the store. The writer now sets the directory (when it
creates it), the lock file and the store file explicitly, after the setter's own
refusals so a refused write still creates nothing. CultCache stages with the
process umask and offers no mode, so the store file is set after the rename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:24:58 +02:00
Meta Vens
9a9165d4f1 mind-body: pin the --instance guard and the binary's refusal exit
Two cargo-mutants survivors from the first run (main -> (), and the
--instance match guard -> true) are killed by a malformed-command table row
and a process-level test of the refused invocation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:05:51 +02:00
Meta Vens
76367e888a mind-body: need() as a fn so the borrowed value outlives the closure
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:01:14 +02:00
Meta Vens
4b3e850fef mind-body: the operator's brake and burn-rate dial, the reader and the mind-control CLI
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:58:29 +02:00
Meta Vens
4a41ec947c Golden: one new line for a Mind ruling; every existing line unchanged
Derived with the ignored golden_dump, appended at the end of the corpus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:36:30 +02:00
Meta Vens
775148f28b RulingAuthority gains Mind: a ruling the Mind's Self makes is its own word
Additive unit-enum variant; schemas regenerated by derivation. The query
alias domain now derives from the enum (unit_enums! exposes NAMES), so the
second authority list in huginn-mind rows.rs is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:35:22 +02:00
Meta Vens
e516c58b17 Golden: restore the sample.campaign payload line dropped by the header edit
Payload lines are now byte-identical to 09e1b97; only header comments differ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:28:28 +02:00
Meta Vens
4d3db41423 LICENSE: byte-identical official AGPL-3.0 text; golden header states exact provenance
LICENSE is the gnu.org agpl-3.0.txt verbatim (LF). The golden header now
names 44be4cd as the dump commit, the four kinds (cut_spec, cut_report,
finding, follow_up) whose source-path string was renamed relative to
Epiphany ef956865, and that 87b6455 differs only in test code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:28:14 +02:00
Meta Vens
09e1b9792b Relicense the workspace AGPL-3.0-only, own the CultLib rev once, index every schema
Add the AGPL-3.0 text as LICENSE and set the workspace license to
AGPL-3.0-only (rulings license-split, leaf-relicensed-agpl); the leaf's
provenance (Epiphany ef956865, PolyForm Noncommercial, relicensed by its sole
copyright holder 2026-10-09) is recorded in its Cargo.toml and README.

One [workspace.dependencies] entry per CultLib crate now owns the revision;
the crates inherit it. index.json lists Huginn's two wire schemas too, and the
catalogue test drops its huginn. prefix exclusion: every schema file is listed.
README and AGENTS stop naming Epiphany notes as the owner of the cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:15:10 +02:00
Meta Vens
c0e383003b Restore LF line endings in the leaf (the earlier edit rewrote the file as CRLF)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:10:38 +02:00
Meta Vens
23a132d194 Read-back test also asserts stored bytes re-encode identically; document the opt-in command
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:57:42 +02:00
Meta Vens
6d62c26920 Commit the golden encoded envelopes the wire parity test asserts
Derived at 87b6455; provenance in the file header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:56:52 +02:00
Meta Vens
44be4cd564 WIP: golden corpus and dump for the wire parity test (golden still empty)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:56:04 +02:00
Meta Vens
87b6455612 Test that a PipelineRefusal read as text names its variant and parts
Kills the one cargo-mutants survivor on eureka-pipeline (Display::fmt replaced
by Ok(Default::default())).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:20:50 +02:00
Meta Vens
c46b0ec183 Rename the pipeline leaf to eureka-pipeline and take it as a path dependency
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>
2026-10-09 14:09:16 +02:00
Meta Vens
73908920ca Copy the pipeline leaf and its schema catalogue from Epiphany ef956865
Byte-identical blobs of epiphany-pipeline/{Cargo.toml,src/lib.rs},
schemas/README.md and schemas/cultnet/* at Epiphany ef9568655e75, so Soul can
diff the copy against the source. Left out as the spec orders:
epiphany.work_organ_state.v0.schema.json and its index.json entry (no reader
outside Epiphany). Epiphany's schemas/cultnet/README.md is not copied over
Huginn's own README there; the next commit folds its pipeline section in.
The crate is not yet a workspace member and keeps its Epiphany names, so this
commit does not build as a package; nothing else changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:07:58 +02:00
Meta Vens
818499a5db huginn-mind: a cut spec naming its own cut is refused UnknownDependency (ruling self-dep); depends_on near-miss labels pinned
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 13:41:23 +02:00
Meta Vens
0bd9eb6b53 huginn-mind: the summary bound fixture's width floor follows depends_on being cut labels
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 13:13:17 +02:00
Meta Vens
eedb7529ea huginn-mind: dependency cuts in the summary bound test keep their spec keys within the label bound
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 13:03:20 +02:00
Meta Vens
56baa46b11 huginn-mind: published wire schemas follow the renames; the summary bound test uses real cuts
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:57:55 +02:00
Meta Vens
cfa8d570ff huginn-mind: a cut spec's depends_on must name cuts of its campaign
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:54:20 +02:00
Meta Vens
08d48957f8 huginn-mind: campaign admission no longer checks repo stewardship
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:50:57 +02:00
Meta Vens
db8430c6fe huginn-mind: report heads match by commit, findings defer to cut specs, MindSteward is Life
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:50:38 +02:00
Meta Vens
0f491b00ef huginn-mind: iterate the envelope slice in the read-back census
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:12:36 +02:00
Meta Vens
f29a7ce7f0 huginn-mind: tighten the refusal and read-back tests (Soul s1 findings)
snapshot_admits_and_reads_back now counts pipeline envelopes apart from the
loop, asserts validate() and pipeline_key == stored key for every one, admits
a resolution for every stuck finding and a withdrawal for every unwithdrawable
resolution, and fails with the id named on any refusal.

The refusal tests assert the exact leaf refusal (field and value from
pipeline_key) at Mind::admit and at the helper, and eureka-state gains a
tool-level test that the typed refusal reaches the result intact.

Tests only; no production code changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 12:10:19 +02:00
Meta Vens
790b6cd4f6 huginn-mind: refuse an unkeyable document as a document, and prove the real mind reads back
Admission wrapped every prepare error in Unavailable, so a key the leaf
could not compose reached the client as an unavailable organ. One private
prepare(document, cache) now keys through the leaf first, mapped to
MindRefusal::Document, then encodes; Mind::admit and the derived-write loop
both use it. Unavailable stays for store, cache-encode and decode faults.

Tests: an_unkeyable_document_is_refused_as_a_document (store and receipts
unchanged), prepare_classifies_a_key_refusal_as_a_document_and_encodes_the_rest
(the derived path cannot reach an unkeyable resolution after the per-kind
bound, so the helper is pinned directly), and the #[ignore]
snapshot_admits_and_reads_back, which opens a scratch copy of the live mind
named by HUGINN_MIND_SNAPSHOT, views every stored document back unchanged,
resolves the six stuck findings and withdraws one long-keyed resolution.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 11:40:29 +02:00
Meta Vens
2b0cd0898b huginn-mind: pin epiphany-pipeline to the per-kind key bound (leaf-key-bound)
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>
2026-10-01 11:40:28 +02:00
Meta Vens
9dd6d8007a Pin CultLib 8fc74c7 in every crate, and the pipeline leaf that follows it
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>
2026-09-30 14:36:44 +02:00
Meta Vens
cd3dfc0268 eureka-state: an encode failure is not retryable, and a non-UTF-8 endpoint is pinned
A request that fails to encode is a defect in this client, not a transport
failure, so it is ClientError::Unencodable, typed Unencodable at the tool,
instead of the retryable Unavailable. A test reports a non-UTF-8
HUGINN_ENDPOINT as such.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 04:55:05 +02:00
Meta Vens
3d9965983f huginn-mind: provenance follows the leaf's Title rule and covers the tool
refuse_provenance validated only agent and session, and only for blank and a
handful of line breaks. It now covers agent, session and tool with the leaf's
Title character rule (an alphanumeric character, no control or line-break
character, no bidi control) inside Short's byte bound. The leaf keeps that rule
private, so a differential test holds the copy to a campaign Title. Restores
the stage doc comment that drifted above SHORT_MAX. Multibyte bound test kills
a chars-count mutant.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 04:55:04 +02:00
Meta Vens
c2b2952749 eureka-state tests: reach the limit through the source runtime id, seed the stewardship
The source runtime id adds its field name when present, so exact sizes are
searched over both paddings. The attribution test admits a campaign, which
needs its repo stewarded first.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 03:04:04 +02:00
Meta Vens
db40a6647b eureka-state tests: size the oversize requests the way base64 allows
Request sizes step by four (the payload rides as base64), so a request of
exactly the limit is not reachable through the request. The client tests use
the largest request that fits and the smallest that does not; the raw-socket
test tops a message up with a source runtime id to reach the limit and one
byte past it, which pins the limit against the transport's own refusal.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:54:54 +02:00
Meta Vens
890a00b0de eureka-state: typed TooLarge, typed InvalidInput, and the pins Soul found missing
- The client refuses a request whose encoded size exceeds what one RUDP send
  carries (65,507 less the 36-byte header and the 6-byte channel id), before
  connecting, as ClientError::TooLarge { bytes, limit }. It was reported as
  a retryable Unavailable ("Message too long"). The MCP tools surface it as
  isError with {"error":"TooLarge","bytes","limit"}.
- Arguments that fail to deserialize are still a tool error, now with the
  same typed body as every other one: {"error":"InvalidInput","detail"}.
- A non-UTF-8 EUREKA_INSTANCE or HUGINN_ENDPOINT is reported as not valid
  UTF-8 and not as unset.
- Tests: the schema walk asserts no $id or $schema below any tool's root; the
  15 s timeout is measured against a silent daemon; the semantic part of a
  query is observed on the wire; Internal's and TooLarge's bodies and the
  InvalidInput rewrite are pinned; attribution is refused through the MCP
  admit; oversize is TooLarge at and one past the limit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:51:50 +02:00
Meta Vens
1d3f917d9e huginn-mind: admission validates who admitted the batch
A batch's agent and session were committed as given: "", " ", a 5,000
character string and a string with a line break all became the receipt's
admitting session. The mind now refuses, at A1 before any document is read,
an agent or session that is longer than Short's 200 bytes (FieldBound), blank
after trim, or spread over more than one line (InvalidFormat), so every
writer gets the check. The refusals are the leaf's own, since the fields are
the leaf's Short type; the published wire schema does not change. Short
exposes no validator, so the bound is a constant pinned to the maxLength its
schema publishes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:51:15 +02:00
Meta Vens
120e0a6e5a eureka-state tests: initialize must advertise the tools capability
cargo-mutants on the Cut 13b diff: replacing get_info with Default::default()
survived, and a client that does not see a tools capability does not list tools.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 01:41:14 +02:00
Meta Vens
63ac5038c9 eureka-state tests: seed the stewardship, pin the schema path, malformed input is a tool error
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 01:01:22 +02:00
Meta Vens
295b602b2e eureka-state tests: common names PipelineRef
8041d82 did not build on its own: the moved Inert index names PipelineRef and
the import stayed behind in client.rs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 00:42:07 +02:00
Meta Vens
03eb670db3 eureka-state: drop the imports the fixture move left unused, and expect the router field
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 00:39:03 +02:00
Meta Vens
e0f07c3f53 eureka-state: the MCP server, four tools 1:1 with the wire
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>
2026-09-30 00:36:33 +02:00
Meta Vens
8041d82fe1 eureka-state tests: the in-process daemon fixtures move to tests/common
The daemon half of the client's fixtures (an in-process daemon over a temporary
mind on a loopback port, and the scripted server) becomes tests/common/mod.rs,
so the MCP binary's tests (Cut 13b) drive the same daemon instead of a second
copy of it. No behaviour change; client.rs shrinks by the moved lines.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 00:36:18 +02:00
Meta Vens
3dc50eec8e eureka-state: whole-call deadline, correlated answers, Rejected, disconnect
The timeout now bounds the whole call: one deadline at call start, every wait
gets what remains, so a chunk-by-chunk deferred body cannot outlive it. The
response's message id and operation are checked against the request. An
envelope-level OperationFailure is ClientError::Rejected, distinct from the
transport's Unavailable, so a retry on Unavailable cannot loop on a permanent
protocol mismatch. Every call disconnects its session, success or failure.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 00:16:33 +02:00
Meta Vens
26f4137d55 Pin the client's configured instance against a mutation that replaces it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:46:33 +02:00
Meta Vens
272dc09800 Add the eureka-state client core: HuginnClient::call
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>
2026-09-29 23:41:25 +02:00
Meta Vens
4f5d0b1a7e Move the deferred-body bound to the wire vocabulary both sides read
MAX_DEFERRED_BODY_BYTES was the daemon's constant, but a client caps its
content fetch at the same number, and the client crate cannot depend on the
daemon. It moves to huginn-mind::wire next to the response schema it bounds;
the daemon imports it, the pin test stays in bodies.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:32:54 +02:00
Meta Vens
debe7ce9e7 Move the operation envelope to huginn-mind: one spelling for client and daemon
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>
2026-09-29 23:30:21 +02:00
Meta Vens
9497001d34 Pin settle-at-reconcile and the first-reconcile wait in take_search
Two mutants survived Soul pass 4: dropping `self.settle()` from reconcile,
and dropping `!projector.stale()` from take_search. One test each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:59:57 +02:00