Commit graph

660 commits

Author SHA1 Message Date
Meta Vens
3efc0dc82d Take the sequenced keys and make in force mean what it says
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>
2026-09-16 19:28:17 +02:00
Meta Vens
30daff8822 Pin the nineteen rules Cut 8 held but nothing tested
Soul's pass found the suite claimed more than it proved: H1-H20 killed
twenty mutations, and nineteen further permissivenesses could be restored
with every test still green. A rule no test reaches is a rule the next
pass may delete by accident.

Two of them were about the receipt's atomicity and its reach, and needed
new machinery. A store in `receipt.rs`'s tests records the replacement set
of every swap it is asked for, which lets one test say what no assertion
over the resulting image could: a commit is one compare-and-swap, and the
documents and their receipt go in it together. Splitting the receipt into
a second swap now fails. Separately, a batch citing three image documents
is pinned against all three, so truncating the strong reads to the first
of them fails.

The rest are one fixture apiece, each closing a row the per-kind rules
already held: stewardship looked up in image as well as batch; the cut
report's repo as well as its branch; a promise measured by the claim that
names that label; the invariant vocabulary taken from the in-force target
and not a superseded one; a question that is answered or withdrawn but
never superseded, and a target never answered; the derived stewardship
dated by the hand-off; derived writes passing A3-A7, so a derived identity
the batch already holds is A4's refusal and not a store error; A4 and A2
themselves; a question's two distinct options and its recommendation among
them; unique labels on a question's options and a target's invariants; a
finding's locations; and an `Answered` naming the ruling that actually
answers its subject.

`tools/eureka-cut8-mutations.psd1` grows to H1-H39. H21 and H22 pin the
opener's epoch key and count, H23 `EmptyRepos`; the rest are Soul's own
survivors, adopted as entries so the thing that found them is the thing
that guards them.

Harness: M0 green at 31 tests, H1-H39 all killed by their own test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:06:51 +02:00
Meta Vens
acd32f35cc Restore the withdrawn resolution: the operator ruled Q17 the other way
The previous commit carried Self's default reading of Q17, that a subject
resolves at most once and a resolution is not itself a subject. The
operator ruled the other way: a withdrawn resolution is kept, attached to
the subject it withdrew, and the subject may then be resolved again.

So the matrix row comes back exactly as it was and
`the_resolution_matrix_is_admissions` returns to admitting the withdrawal
of a resolution. Only the comment above `matrix` is new, and it says whose
ruling this is and what it still needs: a second resolution of one subject
needs a key the first does not already hold, which is a sequence in the
leaf's resolution key and is mapped elsewhere.

`EmptyRepos` and the opener's epoch record stand.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:58:42 +02:00
Meta Vens
1cfa81d571 Close the epoch record, the minted leaf refusal and the nested resolution
Soul's Cut 8 pass found three rules the code only half held and one
document that promised more than the crate does.

The opener's step 3 said "exactly one epoch record at the current epoch"
and checked neither the count nor the key: a record keyed anything at all
was read as the epoch record, and a second one was never looked at, so a
current record beside a foreign one passed or refused on nothing but the
order the store returned them in. Step 3 now counts before it reads,
refuses a record under any key but the epoch, and names the defect in
`found`: the stored epoch, the offending key, or the count.

The campaign row minted `PipelineRefusal::FieldBound` for an empty
`repos`. The organ does not get to invent the leaf's refusals; an empty
`repos` is admission's own rule and is now `MindRefusal::EmptyRepos`.

The matrix admitted `Withdrawn` on a resolution, which made a subject
resolvable twice over: withdraw the resolution and the subject is open
again. A subject resolves at most once, and a resolution is not itself a
subject, so `resolution` joins `campaign`, `cut_report`, `verdict`,
`instance` and `hand_off` as not resolvable. The key grammar still spells
a resolution of a resolution; admission refuses it here. Self's default
ruling on Q17, standing until the operator rules.

`README.md` and `AGENTS.md` claimed CultNet publication and a Qdrant
connection in the present tense and called the store `.cc`. The daemon is
a stub, nothing connects to Qdrant, and the live crate writes
`mind.redb`. They say so now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:53:08 +02:00
Meta Vens
ca30d3eae5 Pin Cut 8's rules with twenty mutations
Cut 8, Huginn half, commit 4 of 4: `tools/eureka-cut8-mutations.psd1`,
entries H1-H20, each the exact source change that restores one
permissiveness this cut closed and the one test that must fail while it
is applied. The suite runs through Epiphany's `tools/eureka-mutations.ps1`
with `-Repo F:\Projects\Huginn`; Huginn holds only the entries.

H5 is a four-edit entry: `Mind::open` is switched to the transient redb
store, which needs a `MindStore` impl the live crate deliberately lacks,
so the entry supplies it in `store.rs`. That makes `store.rs` a fourth
harness target beyond the three the spec names; the file's header says
so. H7 moves the replay check above the per-kind rules rather than above
A7, since the digest it looks up needs A7's strong reads to exist.

Run 2026-09-16 against `0bd7133`: M0 green with 26 tests, every entry
killed by its named test, every target restored byte for byte.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:17:00 +02:00
Meta Vens
0bd71337fd Admit a batch through one ordered path with every rule in it
Cut 8, Huginn half, commit 3 of 4: `admission.rs`. `Mind::admit`
prepares typed documents through the leaf and calls `admit_prepared`,
the single commit path for this cut, Cut 10's sink, Cut 12's hand-off and
import, and Cut 13's tools. The steps run in order and the first failing
step is the outcome: the batch names this mind (A1), one to sixty-four
envelopes (A2), the leaf's bounds, formats and key recomputation (A3),
unique identities (A4), every instance-carrying document names this mind
(A5), an empty mind's first batch carries exactly one `instance` document
and a batch carrying one derives the epoch record (A6), every reference
resolves in image or batch by `(kind, id)` (A7), the derived writes pass
A3-A7 themselves, the per-kind rules and the resolution matrix (A8), an
exact replay answers with the stored receipt only after validation (A9),
no write collides with the image (A10), the commit (A11).

Derived writes: a ruling that answers derives the `Answered` resolution
of its question; a hand-off derives the stewardship withdrawal on its
source side and the stewardship assignment on its receiving side. "In
force" is computed at rule time from the resolutions in image and batch
and never stored; the two derivations are the only resolutions a rule
ignores when judging its own document.

Two places the leaf is thinner than the spec assumed. `FindingWithoutRange`
is unreachable: a finding envelope without `range` fails the leaf's decode
as `Document(InvalidFormat { field: "payload" })` before any organ rule,
and the test asserts that. A campaign with no repos has no refusal of its
own in the spec's list; it is refused as `Document(FieldBound)` on
`campaign.repos` with a minimum of one. `PipelineAdmissionBatch` cannot
derive `JsonSchema` because the leaf's `PipelineDocument` does not; the
outcome and refusal types do.

The epoch record is derived whenever the batch carries the `instance`
document, which only the first batch may, so a replay of the first batch
digests to the same receipt and answers `AlreadyAdmitted` instead of
colliding.

Tests: 26 (the spec's 25 plus the replay-planting test, the receipt's
deliberate second construction site). `cargo check -p huginn-mind --lib
--tests` and `cargo test -p huginn-mind --lib` pass with zero warnings;
`cargo check --workspace` passes. README says what the crate does now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:13:44 +02:00
Meta Vens
a4c5b79d52 Land a batch whole with a receipt naming its exact bytes
Cut 8, Huginn half, commit 2 of 4: the commit primitive in `receipt.rs`.
`candidate` is the one construction site of a receipt: its id is the
digest of `(instance, strong_reads, writes)`, each list in identity order,
computed before anything is stored so admission can look up an exact
replay. `replay` finds a stored receipt with that id and checks its reads
and writes equal the candidate's. `commit` is one compare-and-swap over
the store, the strong reads as its expectation and the writes plus the
receipt as its replacements; the image is re-pulled either way, and a lost
swap is diffed against the fresh image and typed `Conflict`.

Provenance and `committed_at` are on the receipt and outside the digest,
so the same bytes replayed by another faculty in another session answer
with the first receipt. The store requires every expected identity to be
replaced, so a strong read is re-inserted unchanged beside the writes, as
Epiphany's receipt path does.

Builds: `cargo check -p huginn-mind --lib --tests`, `cargo test -p
huginn-mind --lib` (4 tests). Dead-code warnings remain on the primitive
and the refusing test store until admission consumes them in the next
commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:05:30 +02:00
Meta Vens
946758f666 Open a mind fail-closed over an owned redb store
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>
2026-09-16 17:04:23 +02:00
Meta Vens
4094e689c6 Say in AGENTS that the crates are stubs
README carried the caveat that every crate is an empty stub filled by a
named Eureka cut; AGENTS described ownership, admission, CultNet
publication and a Qdrant dependency in the present tense. Add the same
caveat to the Body section and name Studio as the .cc inspection owner
in one sentence instead of a list of things Huginn does not do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:25:29 +02:00
Meta Vens
e20c786c96 Describe Huginn as the memory organ
README and AGENTS now state what Huginn is: the single writer of each
instance's mind as typed CultCache state, published over CultNet, depending
on Qdrant directly and refusing loudly without it. Generic .cc inspection is
CultCache Studio's. The docs name the Rust workspace, the F: drive body, and
the cut map that owns what each crate does next.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:12:18 +02:00
Meta Vens
f63c0f2d0b Add the empty Rust workspace
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>
2026-09-16 15:11:05 +02:00
Meta Vens
1320fc4661 Retire the TypeScript body
Huginn is the memory organ. Nothing consumed the @gamecult/huginn package,
the huginn CLI, or buildHuginnEveDsl; generic .cc inspection belongs to
CultCache Studio, and CultLib owns the inspector. Delete the package, its
tracked build output, and its toolchain config outright. No code moves.

Eve's huginn-cc-surface fixture is Eve's own lowering conformance material
and stays in Eve.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:10:15 +02:00
Meta Vens
91b7fcf8bd Merge Huginn Eve projection branch 2026-06-21 15:52:11 +01:00
Meta Vens
afee1a262f Document Huginn repo swarm wiring 2026-06-21 15:51:46 +01:00
Meta Vens
0d58398b4b Reduce Huginn to Eve DSL projection 2026-06-05 12:42:30 +01:00
Meta Vens
49970186dd Make Huginn consume CultLib CultCache 2026-06-05 12:23:59 +01:00
Meta Vens
a079386a3e Make Huginn emit Eve DSL only 2026-06-01 22:15:39 +01:00
Meta Vens
d905639ecf Point inspector at Norn viewer 2026-05-25 18:19:03 +01:00
Meta Vens
c13a564c60 Seed Huginn social interpretation biases 2026-05-24 20:39:32 +01:00
Meta Vens
30e6cad173 Retire Huginn echo state 2026-05-22 23:09:33 +01:00
Meta Vens
cfe9605a5d Collapse Huginn payload graph regions 2026-05-22 20:43:15 +01:00
Meta Vens
5adf52e221 Tie Huginn node articles to viewport layout 2026-05-22 20:25:09 +01:00
Meta Vens
8dc87fa2c4 Fix Huginn stardust buffer spaces 2026-05-22 20:09:34 +01:00
Meta Vens
6337f5daf8 Derive Huginn social placement from graph 2026-05-22 13:16:33 +01:00
Meta Vens
3fb855f5b7 Add Huginn repixelized avatar 2026-05-22 13:03:43 +01:00
Meta Vens
c396862b5a Seed Huginn repo Face state 2026-05-22 11:58:00 +01:00
Meta Vens
88a3b0ca78 Use EpiphanyGraph force solver in Huginn 2026-05-20 20:38:30 +01:00
Meta Vens
e3f0d8ffaf Drive Huginn stardust from packed art channels 2026-05-19 14:08:05 +01:00
Meta Vens
8b36467c09 Make Huginn payload cloud and stardust viewport native 2026-05-19 13:35:39 +01:00
Meta Vens
1feea63f63 Bump release version to 0.12.0 2026-05-19 10:47:20 +01:00
Meta Vens
5a74ffd144 Use article focus for Huginn node expansion 2026-05-19 10:32:11 +01:00
Meta Vens
8f802ea17b Make Huginn node preview payload-first 2026-05-19 10:03:32 +01:00
Meta Vens
1adb64fa07 Carry Huginn particle albedo from source cells 2026-05-18 22:58:29 +01:00
Meta Vens
01fa2135bb Sample Huginn particle albedo from artwork 2026-05-18 22:53:45 +01:00
Meta Vens
172548b704 Calm Huginn particles with quadtree stardust 2026-05-18 22:47:34 +01:00
Meta Vens
4e4485c7ba Fit Huginn terrain to graph bounds 2026-05-18 22:14:36 +01:00
Meta Vens
0d2c749fb3 Sync Huginn terrain zoom with graph 2026-05-18 16:59:54 +01:00
Meta Vens
73ec476118 Couple Huginn graph to terrain field 2026-05-18 16:33:47 +01:00
Meta Vens
211e445374 Make Huginn particle field adaptive 2026-05-18 16:07:48 +01:00
Meta Vens
e7a46f2d59 Render Huginn field with WebGPU particles 2026-05-18 15:46:45 +01:00
Meta Vens
fd7f59b50e Pack Huginn renderer signal textures 2026-05-18 15:25:20 +01:00
Meta Vens
7d5858b1b6 Add Huginn signal plate renderer 2026-05-18 15:07:59 +01:00
Meta Vens
6098b3ffd1 Make Huginn graph first 2026-05-18 12:45:41 +01:00
Meta Vens
75b8770f24 Dedupe React for Huginn inspector 2026-05-18 11:17:16 +01:00
Meta Vens
32ccf43a0f Rebuild Huginn graph inspector 2026-05-18 11:01:16 +01:00
Meta Vens
a8765d12d6 Bound Hugin payload graph rendering 2026-05-18 05:17:35 +01:00
Meta Vens
2d29ee507f Load Hugin assets relatively in Electron 2026-05-18 04:41:59 +01:00
Meta Vens
1c455ff7f5 Sync Hugin graph and raw selections 2026-05-18 04:19:53 +01:00
Meta Vens
e5c4c9dd50 Embed EpiphanyGraph in Hugin 2026-05-18 03:56:43 +01:00
Meta Vens
8631f0ab62 Brand inspector as Hugin 2026-05-18 03:41:06 +01:00