- Rust 98.4%
- C++ 1.2%
- Python 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
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>
|
||
| .voidbot | ||
| crates | ||
| schemas/cultnet | ||
| tools | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
Huginn
Huginn is the GameCult memory organ. It owns each agent instance's mind as typed state: what an instance remembers, which of that memory is admitted to steer its next action, and the provenance of every admitted claim.
Upstream: https://github.com/GameCult/Huginn.git
Authority
- An instance owns its mind; Huginn owns the state. Huginn is the single writer of an instance's memory documents. No other service, script, or agent writes them.
- Memory documents are CultCache state.
huginn-mindpersists them through CultLib's Rust CultCache into a redb store at<state_root>/minds/<instance>/mind.redb.huginn-daemonserves one such mind over CultNet RUDP: admission and the read side ridecultnet.operation_request.v0andcultnet.operation_response.v0, and the two wire schemas answer a schema catalog request. - Retrieval is to depend on Qdrant directly, refusing loudly when Qdrant is unreachable rather than falling back to a second store or a second writer. No crate opens that connection yet.
- Mind state is not version-controlled. It lives in Huginn's store, not in any repository.
Generic .cc inspection is not Huginn's job. CultCache Studio inspects and
edits .cc state. Huginn reads and writes minds.
Layout
A Cargo workspace of three crates:
crates/huginn-mind: storage, identity, admission, and queries and derived status over memory documents.crates/huginn-daemon: the CultNet surface. The socket, the sessions, the process and the operation envelope, and no rule.crates/eureka-state: a stub. It will carry typed state for the Eureka pipeline; today it holds none.
huginn-mind is live: it opens one instance's store (an owned redb CultCache
at <state_root>/minds/<instance>/mind.redb, locked for the mind's lifetime),
and refuses a store whose instance document names another instance, whose
epoch record is foreign, or whose types are not a mind's. It admits batches
of pipeline documents through one commit path: the leaf's bounds, formats and
keys, then the organ's cross-document rules (references, in-force status,
the resolution matrix, derived resolutions and stewardships), then one
compare-and-swap that lands the batch whole with a receipt naming the exact
bytes it read and wrote. An exact replay answers with the stored receipt.
It reads them back the same way: a document with the facts of its admission
joined from that receipt and its status derived at read time, typed queries
over one mind, a campaign's open work, and a subject's or a repo's history.
Status is never stored, and the views derive it through the same rules
admission does. Document shape and keys come from epiphany-pipeline; the
store is CultLib's Rust CultCache.
huginn-daemon opens one mind, binds one UDP socket in that order, and
answers every frame on the session it arrived on: one operation per Mind
method plus whoami, whose payload is the mind's own types as named
MessagePack. A refusal is an answer with a rejected status, never a
transport error; an envelope that does not decode is answered with a typed
failure and reaches no mind. The two schemas it publishes live in
schemas/cultnet/ and are pinned to their derivation by a test. There is no
index yet.
eureka-state is a stub; the campaign's cut map in
Epiphany/notes/eureka-pipeline-state-cut.md owns what each crate must do
next.
cargo check --workspace
cargo run -p huginn-daemon -- --state-root <abs> --instance <slug> --bind 127.0.0.1:17872
Persona
Huginn's legacy repo Persona lives under .voidbot/. Its state/huginn.cc
holds legacy void.* documents and its voice/identity.json names the
Persona. The two disagree about jurisdiction; that migration belongs to the
portable-Persona work, not to this workspace, and nothing here reads or writes
.voidbot/.