Commit graph Huginn/schemas/cultnet
Author SHA1 Message Date
Meta Vens
cb86f58a7d wear-facts: a Mind's first line shows the wear her own mind holds, specs with no report, open Soul findings and quiet campaigns on what she stewards, each leading to a record she can open (squash of hands/eureka-body-wear-facts ab61a53..6014554; Soul s1-s2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 07:06:16 +01:00
Meta Vens
545d6319bf repo-activity: a Mind hears of changes in her stewarded repos, on her first line and by one organ notice on change; a repo the anonymous read cannot reach is named, never a failed read (squash of hands/eureka-body-repo-activity 5a294ed..eee0b14; Soul s1-s4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 04:54:26 +01:00
Meta Vens
61b0b55bb1 memory: golden and schemas re-derived for origin
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 17:48:18 +01:00
Meta Vens
152e315e5d memory: a persistent kind for what a Mind keeps (eureka-pipeline, huginn-mind)
epiphany.pipeline.memory.v2, instance-rooted `<instance>:memory:<label>`:
PipelineMemory with seven sorts (Asked, Decided, Committed, Learned, Did,
Idea, Rapport), her own text, who it is about, where it was learned, whether
directly, what it rests on, a salience that only ranks, and the memories a
consolidating one replaces.

Admission refuses a foreign instance and resolves a memory only Deferred to a
follow-up (an ask taken on), Recorded, Withdrawn or Superseded by a memory.
Memories have their own lineage so supersession stays within one mind. The
read side selects by sort, about (multi-valued) and direct; the index carries
the text; `consolidates` is a citation role.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 15:15:40 +01:00
Meta Vens
bc59cb9025 Merge remote-tracking branch 'origin/main' into eureka-body/heartbeat-joint-lever 2026-10-31 09:53:14 +01:00
Meta Vens
bc9e75debe schemas: the pulse, request and response schemas re-derived for tempo_vote_withdrawn
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 09:07:01 +01:00
Meta Vens
9d081e2501 schemas: drop a runner line that landed inside the regenerated request schema
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 06:39:40 +01:00
Meta Vens
09fb87168f schemas: publish SearchSources and Sources (derived from the wire types)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 06:36:57 +01:00
Meta Vens
16f54117ab schemas: the request wire schema is the derivation, without interleaved test output
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 06:07:17 +01:00
Meta Vens
751923d326 schemas: regenerate the mind request and response wire schemas for the Pulse kind
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 06:01:43 +01:00
Meta Vens
90d4637c96 tests and schemas: Pulse in the kind enums, reply keys, vote replacement, one clock read per lease
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 05:44:44 +01:00
Meta Vens
056ca778d8 mind-body: tear out dollar metering; a shared tempo heartbeat; usage-only connector (WIP, tests follow)
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>
2026-10-31 05:24:54 +01:00
Meta Vens
1868f6a023 Request schema: the SemanticQuery description keeps its newlines escaped
The previous commit wrote raw newlines into the JSON string, which the
published file must not hold.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 20:03:08 +01:00
Meta Vens
19a4d83571 Request schema: regenerate SemanticQuery's description (top_k is the most rows answered, all members)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 19:58:26 +01:00
Meta Vens
d7b1b1d116 A semantic query returns up to top_k members of its selection, with scores
The mind decides membership (Mind::members) before the index is asked; the
index scores exactly the members (Qdrant has_id + exact) and the page carries
one cosine score per row. Deletes OVERSAMPLE_MAX, the x4 oversample, the
unfiltered VectorIndex::search contract and check_semantic.

Not yet compiled when committed: the first Yggdrasil run follows.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 19:51:29 +01:00
Meta Vens
f8eaf50a03 campaign-repos: a campaign's repo set changes by target revision; one line_of decides supersession lineage (squash of eureka-substrate/campaign-repos c0a1f2c..4442e1a)
target.repos additive at epoch v2; Docs::repos_in_force owns membership; line_of owns lineage (targets per campaign, cut_specs per campaign+cut, stewardships per instance+repo, rulings and follow_ups global); rival_in_force owns one-assignment-in-force. Soul s5 holds (eureka-substrate:verdict:cut-campaign-repos.s5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 23:38:20 +02:00
Meta Vens
5da8b06a6d Merge remote-tracking branch 'origin/main' into eureka-body/mind-launch 2026-10-10 19:16:19 +02:00
Meta Vens
b759addf3e Regenerate the response wire schema for PersonaStatus; lock serde_path_to_error
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 08:38:42 +02:00
Meta Vens
35c5b7ac24 Merge origin/main (f003801) into eureka-body/mind-launch
Mechanical adaptation: testkit's PipelineCutSpec builder gains the new reads field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:50:46 +02:00
Meta Vens
6fde91fe0a Persona: compile fixes and the regenerated wire schemas
The jsonschema error kind lives in jsonschema::error; the eureka-state tool
closures own their input; huginn.mind_request/response.v1 carry the persona
operations (derived by the existing published_wire_schemas test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:47:00 +02:00
Meta Vens
8f41db55e0 huginn-mind: published response schema carries RunId
Regenerated from schema_for!(HuginnMindResponse) as the wire test derives it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:18:38 +02:00
Meta Vens
7ef8669037 huginn-mind: published response schema indents AlreadyLive like its siblings
ff259a1 and 11af610 carry the schema block one indent level too deep, so
published_wire_schemas_match_derivation fails on them; this commit fixes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:01:30 +02:00
Meta Vens
ff259a16bb huginn-mind: admission refuses a second live run of hers of one turn (AlreadyLive)
Ruling one-live-self-run, decided where batches are serialized: run_rule
(was run_claims_rule) now also refuses an opening or reinstated run whose
instance and turn already have an in-force operated_by Mind run. The
fixture run() is an operator's run; mind_run() is hers. Unverified: this
commit has not been built when written; verification follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:48:01 +02:00
Meta Vens
ae12b0cb6a Golden lines and published schemas derived for CodeLocation.symbol and CutSpec.reads
Two golden lines appended (dumped by golden_dump on Yggdrasil at 44c010c; numstat 3 added, 0 deleted
against the base) and the six schemas regenerated from the types. Builds; with 44c010c the
workspace tests are expected to pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:12:25 +02:00
Meta Vens
44c010c9da Leaf: CodeLocation.symbol and CutSpec.reads, additive at epoch v2 through the value_types! = absent marker
Adds Absent, Symbol, ReadAnchor and read_window_is_bounded, the marker in
value_types!, the workspace fixtures and the reads admission test. The
golden file and the six published schemas are not regenerated yet, so the
golden, schema and wire-schema tests fail at this commit (it builds).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:29:53 +02:00
Meta Vens
bb9e64dca1 Mind: run admission with AlreadyClaimed, the run resolution row, the claims edge
A run's instance must be this mind. Every claim must name a document in force
(MissingReference, CitesResolvedDocument); a claim held by another run in force
is refused AlreadyClaimed { item, run }, so the claim is the one consumption
fact. A byte-identical replay of an admitted run owes its claims no fresh
freedom and answers AlreadyAdmitted. A run resolves Recorded or Withdrawn only.
Runs cite their claims under role `claims`, so cites and cited selections reach
them; the header facts carry label, operator, turn, start and claims.

The published schemas that embed PipelineKind (finding, follow_up, question,
resolution, ruling) and the two mind wire schemas are re-derived: the Run
variant, PipelineFacts::Run, AlreadyClaimed and the claims role. The leaf commit
before this one has those five stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:07:19 +02:00
Meta Vens
6c705f5f2a Leaf: a run kind, PipelineRun with RunOperator and RunTurn, instance-rooted
A run is the one record of what a Self or a Persona turn took on: key
<instance>:run:<label>, claims of CutSpec, Finding or FollowUp only (refused at
validate), a canonical-decimal budget, turn PersonaTurn or SelfRun per ruling
every-wake-a-run. The run sample is appended at the end of samples(); the golden
gains one line after sample.hand_off and no existing line changes. The
schema and its catalogue entry are derived from the type. No existing kind or
field is touched; the epoch stays v2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:57:59 +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
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
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
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
5eea49f756 Cut 11b: the semantic read, joined back through the mind
Qdrant supplies candidate ids and scores and nothing else. Mind::rank drops
every hit the mind does not hold, then lets CultNet's selection decide
membership over the whole mind with its keys narrowed to the candidates, so
in_force and cited mean what they mean in query. Order is score then ordinal;
the page is cut to the selection's limit and top_k; next is never set. A
cursor with semantic, blank text and an out-of-range top_k are refused by name
before anything is embedded. Mind::query loses its semantic parameter and the
"index is not wired" refusal; the published request schema's description of
SemanticQuery follows.

The serve loop never waits on the embedder: a semantic query becomes a Search
held beside its session, the worker embeds (behind the model's query
instruction) and searches between batches, and the loop answers when the
worker has or when ServeOptions::search_timeout passes. A worker that is
Failing or Refused refuses at once, typed Unavailable naming its own error.
VectorIndex gains search; Qdrant's adapter sends no filter.

Model identity is re-read before every flush and before every search: a model
re-pulled under the same dimensions but a new digest forgets the
reconciliation instead of mixing vectors, and the next step rebuilds. An idle
worker forgets the reconciliation every Backoff::recheck (60 s), so a lost
collection is found without a write. Both go through Projector::reconcile,
still the one judge.

Also fixes a race in the worker loop found by the mutation runs: entries were
taken from the inbox under one lock and published as wanted under another, so
whoami could read a stale Current with nothing queued while the worker held
them. Taking, absorbing and publishing are now one critical section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 13:37:01 +02:00
Meta Vens
f4416e659b Merge eureka/memory-organ (BP-3) into hands/cut11a
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 10:04:04 +02:00
Meta Vens
78f8c6ee2f Cut 11a: the index write side
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>
2026-09-29 08:05:26 +02:00
Meta Vens
7636737d60 BP-3 1: an oversize answer is deferred to the body plane
deliver replaces within_window as the one owner of how an answer leaves:
whole when the envelope fits the window, deferred when the payload is within
MAX_DEFERRED_BODY_BYTES (64 MiB), refused by name past it. A deferred answer
is HuginnMindResponse::Deferred(DeferredAnswer { manifest }), the reference's
CultMeshCdnArtifactManifest; the chunks are asked for on the session the client
holds and answered by cultnet-rs's answer_content_chunk_request. The body is
held by DeferredBodies (256 MiB budget, sliding 60 s TTL, least-recently-
touched eviction, shared chunks stored once), in memory only. The mind knows
nothing of deferral.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 07:29:17 +02:00
Meta Vens
eb57bdba40 RS-3 2: the read side is CultNet's selection over the organ's rows
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>
2026-09-29 05:56:37 +02:00
Meta Vens
e770fb9f21 BP-2: move Huginn to CultLib 6251cbc and the RS-L leaf 4891f2e5
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>
2026-09-29 03:56:03 +02:00
Meta Vens
056db28792 RS fix batch: read-side density, the smallest seal, R5's two shapes, stale prose
F1: AdmissionIndex::build now calls receipt::head before copying any
ordinal, so query and view refuse a receipt chain that is not exactly
{1..=N} (duplicate, gap, zero, or above-N) instead of trusting a stored
ordinal unchecked. New test query::tests::query_and_view_refuse_a_receipt_chain_that_is_not_dense
pins it behaviourally: it plants each broken chain directly (bypassing
admission) and asserts query and view both refuse. The schema's ordinal
gets minimum: 1 (was 0), matched by #[schemars(range(min = 1))] on
AdmissionFacts::ordinal. head's doc now says AdmissionIndex::build asks
it too.

F2: Mind::open_with is renamed to an internal open_checked, and a
test-only Mind::open_with is gated behind cfg(test) or the test-support
feature, exactly like store_path_for. Production reaches open_checked
only through Mind::open, which takes the store's per-path lock first.
mind::tests::a_mind_has_one_owner_at_a_time (pre-existing) still pins
that a second Mind::open on the same path is refused. The full store
seal against a second in-process owner over an arbitrary MindStore stays
a recorded follow-up. lib.rs's crate doc now names Mind::open as the
owner of "may this store be this instance's mind".

F3: R5 is not binary. Two loosenings are pinned as R5b and R5c in
mind.rs, with behavioural fixtures: a foreign type with no epoch record
at all still refuses MissingIdentity, not ForeignStore (R5b); an epoch
record at the current key with a foreign value, beside a foreign type,
still refuses ForeignEpoch, not ForeignStore (R5c).

F5: gate step numbering in mind.rs's doc comment and its test comments
now read 2 (epoch), 3 (type), matching the code's actual order. docs.rs
no longer credits resolutions_of/assignments_of with reading history,
which RS-2 deleted. The two-door test in query.rs is renamed from
a_ref_whose_kind_and_id_disagree_is_refused_by_both_doors to
..._by_view, since view is the only door left.

Per the operator's ruling retiring the anchor-based mutation harness,
this batch carries no .psd1 changes; the cut9/cut10/readside mutation
files have uncommitted edits from the earlier pass (stale references to
the old test names, and R3L's target) left for a later pass to fold in
or drop.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 18:34:47 +02:00
Meta Vens
0d6f9a60d7 Cut RS-1: the receipt ordinal, receipt v2, and the epoch-first opener
Per eureka-read-side-cut.md. The receipt goes to v2 (type id and schema
version both huginn.mind_commit_receipt.v2) and gains ordinal: u64 at key
7. candidate takes the ordinal; digest stays unchanged, so an exact
replay keeps the ordinal its first admission assigned. validate refuses
ordinal == 0. New receipt::head(mind) decodes the receipts and requires
their ordinals to be exactly {1..=N}, refusing Unavailable otherwise;
admission is the only caller, passing head(self)? + 1, and no other code
computes an ordinal. AdmissionFacts gains ordinal, filled by
AdmissionIndex::build; the order query.rs sorts by is unchanged (RS-3
deletes it, not this cut).

mind.rs's opener now calls refuse_foreign_epoch before
refuse_foreign_types, so a store written at a foreign epoch is refused
as ForeignEpoch rather than ForeignStore: a real epoch bump always moves
every type id, so the type gate would otherwise fire first and the
epoch gate would never be reached for the one case it exists for. The
doc comment is updated to match. Regenerated both published wire
schemas.

New tools/eureka-readside-mutations.psd1 (R1-R5, R1 and R3 with L
variants, R1 also with an off-by-one-head R1L2; R2 and R5 documented as
having no loosening, on the same grounds cut10's D9 and D12 give). Five
new tests carry them: ordinals_are_admission_order_not_clock_or_id_order
and an_exact_replay_keeps_its_ordinal in admission.rs,
a_chain_that_is_not_dense_refuses_admission_and_reads_alike in
receipt.rs, and a_v1_receipt_store_is_refused_at_open plus
a_store_written_at_the_previous_epoch_is_refused_by_the_epoch_gate in
mind.rs. cut8's H6 is re-anchored on the new gate order (its own rule,
every gate before attach, is unchanged); cut8 (67), cut9 (27) and cut10
(65) all rerun clean, because candidate's signature and the opener's
order both changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 11:56:04 +02:00
Meta Vens
92ac16c5b0 Cut RS-2: delete open_items, history, and the admission window
Subtraction only, per eureka-read-side-cut.md. Deletes PipelineOpenItems,
HistoryScope, open_items, history, and the admitted_after/admitted_before
window from the read side, their wire variants and arms, the daemon arms
and tests that exercised only these, the OpenItems/History lines from the
instance-gate tests (each keeps its query and view cases), and the README
clause. serve.rs's N4 gate fixtures use whoami in place of open_items as
the long operation name; the case is kept.

Deleted the now-dead Reader::views() helper and the 'a lifetime it forced
on Reader, and stripped the History-only assertions from two tests the
map did not name (a_ref_whose_kind_and_id_disagree_is_refused_by_both_doors
and views_read_admitted_at_from_the_receipt_not_stored_at) that also called
the deleted API. Regenerated both published wire schemas.

Removes cut9 V9-V15, V22 and V22L (12 entries incl. V13L/V14L), and cut10
D19, D19L, D23, D23L, S1 and S1d (6 entries). D13 and D13L are re-anchored
on the surviving view/query arms; D13L uses a near-miss spelling rather
than a pure casing change, since PowerShell's default -eq made a casing
mutant read as "changes nothing" to the harness's own guard. V24 collapses
from a two-door Edits array to a single edit now that view is the only
door left. cut9 (27 entries) and cut10 (65 entries) both rerun clean with
M0 green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 09:51:32 +02:00
Meta Vens
9bbe746719 Refuse an answer that will not fit, by name
A reply larger than one send could carry was handed to the hub anyway. The hub
refused it, the loop printed the error and served past, and the client waited
for an answer nothing would ever send. A real-sized read was lost in silence,
which is the one thing this surface promises not to do: a refusal is typed, and
that has to hold at every size.

`within_window` now encodes the reply and measures it against the window this
module configures, before a send it can already see will fail. Over the limit,
the answer becomes `MindRefusal::ResponseTooLarge { bytes, limit }` on the
response schema, reaching the client exactly as every other refusal does, with
the encoded size and the limit so a caller can narrow its own request. Nothing
is truncated, paginated or retried.

The arithmetic, measured rather than estimated. One `PipelineCutSpec` with every
list at the leaf's bound -- 256 file changes, 64 deletes, keeps, adds, builds,
tests, negative checks and operator checks, a full authority map and structural
delta -- is about 980,000 bytes of field content. As a `View` response, base64
in the operation envelope and MessagePack around that, it encodes to 1,315,551
bytes. The window is `MAX_PENDING_RELIABLE_PACKETS` (1024) times
`MAX_FRAGMENT_BYTES` (1200), so 1,228,800. A single document at the leaf's own
bounds therefore does not fit, by 86,751 bytes.

The window's values are unchanged by ruling: what a list read should return is
an open question, and raising the window would bake multi-megabyte answers in
as ordinary traffic before that question is answered. The module documentation
states 1,228,800 as the current bound rather than as a design target.

The test admits two cut specs into a real mind: one at 256 file changes and one
narrowed to 180, whose view encodes to 1,189,495 bytes. Over one loopback
session the query, the wide view and the open items are each refused by name,
and the narrow view is then delivered whole. Both halves are load-bearing: the
delivered answer sits 39,305 bytes under the limit, so `D22` and `D22L`, which
shrink the configured window, break it.

`D20` removes the gate, `D20L` measures against a larger number than the hub
carries, `D20L2` and `D20L3` collapse the two fields the refusal reports, `D21`
sends it as a failure envelope instead, and `D22`, `D22L` and `D22L2` shrink
each half of the window the gate trusts. All eight die. The whole suite, 51
entries and M0, is green.

Two reads pipelined on one session are still not both answered, and the module
documentation says so: the window is per session and counts what is
unacknowledged, so a second large reply sent before the first is acknowledged
fails to send even though each passes the gate alone. The gate measures one
answer against the window, not against what the session still has room for.
That is not fixed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 00:08:10 +02:00
Meta Vens
c4b2a0fa2e Give the mind a wire vocabulary and publish its two schemas
`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>
2026-09-16 22:52:53 +02:00