Village of Clusters

The Village of Clusters

Right now koditoki's village is three villagers hand-typed into a file. This is the read of what that world actually does when it runs, and the build-ordered brief for growing it into thirty-plus specialists without breaking the thing that makes it interesting: a digger who cannot feed himself.

bun run scenario village · 120-tick baseline, real event log · 19 Sep 2026

01What the chain actually does

Three agents, one dependency chain, no shortcuts. The forager eats what it picks and needs nobody. The smith casts one copper nugget into a tool and hands it to the digger, who alone can reach the roots under the bed. Cut any one relationship and someone starves — that fragility is the whole point of the demo, and it is the thing a cluster generator has to reproduce thirty times over, not paper over.

3
specialists today
16
gave percepts / 120t
64/64
peak frame bits (smith)
t62
digger hunger-caps

The digger's hunger, over the full run

1.00.50 t0t60t120 hunger cap

● digger   ● forager (self-sufficient, sawtooth from eating alone)   ● smith. The digger's last successful dig is t28; hunger rises unbroken after and pins at 1.0 by t62 — it does not die (no mortality system is wired in), it just sits there, "exhausted," for the rest of the run.

Why: the tool has a use-count, not a supply chain

The world seeds exactly one copper nugget with hits: 4. The smith casts it once, hands it off at t07, and never holds copper again — its want clause (sharp ?x) <- (kind ?x copper)(holding self ?x) can't re-fire. The digger spends the four hits at t08/t12/t16/t28, and the tool's (sharp nugget) mark never returns.

tickeventhits left
t07smith gives nugget to digger4
t08dig3
t12dig2
t16dig1
t28dig — tool spent, sharp retracted0
t62digger hunger reaches 1.0, never eats again—

The scripted 36-tick demo ends at t36 — before this ever shows. "The digger still eats" is a claim about a window, not a steady state, and any longer or larger run inherits it once per cluster, on the same clock, independently.

02What one give costs

Trade is gated by trades-with (rigid, checked once at grounding) but priced by give's grounded literals, and the two behave nothing alike. Probed directly against compileFrame/give.ts:

co-located, dealt-with partnersvocab bitsopsframe
acquainted, not co-located (any count)00free
17 / 641fits
317 / 643fits
632 / 646fits
947 / 649tight
1262 / 6412tight
13+——no-frame

Same real village frame (give + cast + eatFromHand + walkTo + sleep). Each co-located partner adds ~5 bits: their location, a promise-retraction bit spent whether or not a promise exists, their holding, the give, the receipt. An acquaintance who isn't in the room costs nothing — that's the design lever.

03The cluster: what's per-agent, what isn't

A cluster is a grove (forager+bush), a bed (digger+roots), a forge (smith+mold+nugget) and a square, all local to that cluster's three specialists. Of everything in the three agent specs, almost all of it — clauses, reflexes, schemas, framing, disposition, body — parameterizes only over self and is copy-per-instance for free. Four things don't:

whattodayindexed
scenery(self)'s two module constsAGENTS, WORKPLACES (whole village)this cluster's 3 names only
smith's seeded promisepromised("smith","digger","pick")promised(smith(i), digger(i), "pick")
smith's earshot belief["can-hear","forager","smith"][..., forager(i), smith(i)]
smith's asksFor hearershearers: ["forager"]hearers: [forager(i)]
world thingsnugget, mold, bush, bednugget-i, mold-i, bush-i, bed-i
The trap: growing AGENTS/WORKPLACES in place and leaving scenery() alone hands every mind every other cluster's agents and workplaces too — facts/mind grows with population, the exact OwnMap defect (koditoki-9bb1) already fixed once in households.ts. Worse: no AgentSpec in village.ts sets ownMap today, so build.ts's belief() silently hands every mind the world's whole topology regardless of what scenery() seeds — invisible now only because the world is 4 places.

04The map

sq grove forge bed forge–grove edge (load-bearing) empty road, ≥3 hops sq grove forge bed cluster i cluster i+1

Each cluster is a 4-place diamond around its own square, diameter 2 — the shape the current 3-agent village already validates (and already fills to 64/64 bits). Clusters never share a place; they're joined only by empty road nodes, long enough that no agent's speech (hearing range 2) reaches a neighboring forge or grove.

rolekHopsreachwhy
forager, digger, smith22exactly the diamond's diameter through the square — unchanged by cluster count, held constant regardless of share

share as scripted (how many clusters' squares coincide) is the honest reading of "the Dunbar knob" — but the frame has no headroom for it. The unmodified single cluster already peaks at 64/64 bits with nobody sharing anything.

05Acquaintance

trades-with stays scoped to each triple's own three members — forager, digger, smith of the same cluster, never a neighbor. Per §02, that's free at any population as long as the partner isn't standing in the same place. Three probed configurations, real compileFrame, 10 clusters, own-group scenery:

sharedealsWith scopesmith frameresult
1own triple (3)29 / 64flat, 3→100 clusters
2own triple, square shared38 / 6488% full by share=4
2whole share-group—no-frame
3, 6whole share-group (as scripted)—no-frame — both requested values fail

Decision: default share = 1. It is not the density dial it's described as — it's an on/off switch, and it's off. share > 1 is a separate, deliberately-run experiment that needs its own frame-slimming pass (fewer ask words, tighter rigid set) before it means anything; ship it documented as expected-to-fail, not degraded-gracefully.

06What to measure, and the pass condition

metrichowpass
specialist countclusters × 3≥ 30 (clusters ≥ 10)
facts/mind vs. populationcompare clusters=3/10/30 with ownMap:trueflat, not the 174→1254 households curve
frame bits, per clusterdiff every cluster's per-tick trace against the unmodified 3-agent baselineidentical shape, no stray bit from a leaked cross-cluster fact
digger fedeach cluster's digger eats at least once inside the run window (< ~t60/cluster, per §01)all clusters, not indefinitely — state the window explicitly
cross-cluster leakagerelations matrix: any give/ask/promise between agents of different clusterszero, by construction
ms/agentwall clock at clusters=3/10/30flat, matching the households anchor (~300–330ms/agent), not the fort's hub curve

Already run at 10 clusters (30 agents), share=1, 120 ticks: 0 no-frame verdicts, 4 completed gives/cluster, all 10 diggers eat at least once, zero cross-cluster edges in the relations matrix, ~303–333ms/agent flat from clusters=3 to 30. This is the configuration to ship.

07Build order

Smallest verifiable step first, so a regression is caught at the step that caused it — never at "run 30 clusters and see."

  1. One cluster, indexed by i=0, byte-identical to today. Write squareOf/groveOf/bedOf/forgeOf and the per-cluster scenery(); run clusters:1 and diff its full event log against unmodified village.ts.Gate: zero diff, including frame-bit counts per tick.
  2. Add ownMap: true to all three specs. Confirm KB size is unchanged at clusters:1 (topology was already only 4 places) — this step should be invisible here and only matter from step 3 on.Gate: facts/mind identical to step 1.
  3. Two isolated clusters, share:1, roads long enough to clear hearing range 2. Run 40+ ticks; check cluster 1's forager KB never gains a fact naming cluster 2's agents, and vice versa.Gate: zero cross-cluster facts in either KB; both diggers eat once each.
  4. Ten clusters, share:1. Run 120 ticks. Plot facts/mind and frame-bits vs. cluster index.Gate: both flat across clusters; 30 specialists; every digger eats at least once before ~t60.
  5. Thirty clusters, share:1. Repeat step 4's measurements at 3x scale; time the run.Gate: ms/agent flat vs. step 4, matching the households curve, not the fort's.
  6. (Separate track) share:2 as a named experiment. Don't fold into the main branch. Confirm it fails the way §05 predicts — no-frame near-immediately with the vocabulary as-is — before spending any effort slimming it.Gate: documented failure, matching the probe, not a surprise.

08What's likely to go wrong, ranked

  1. 1
    The literal reading of "generated by index."

    Growing AGENTS/WORKPLACES in place while leaving scenery()'s body untouched compiles and runs fine at clusters:1–3 and silently reproduces the OwnMap facts/mind curve from clusters:10 up. It looks correct for exactly long enough to ship.

  2. 2
    Porting households.ts's pure star and dropping the forge–grove edge.

    households.ts has no spoke-to-spoke edge; village.ts's does, and the file's own comment says without it the smith forgets the grove is stocked before it can ask. A literal port drops this fourth edge per cluster, silently.

  3. 3
    Treating share as a dial instead of a switch.

    The brief names it the density knob; the measured frame has zero headroom above share=1. Anyone who runs share=3 expecting a smooth curve gets no-frame and will spend time debugging a working design.

  4. 4
    Reading "the digger still eats" as indefinite.

    It's single-shot per cluster, independent of clustering — one nugget, four hits, no resupply. Every cluster hunger-caps its digger on its own clock past ~t60. A report or demo run longer than that will show it, per cluster, at scale.

  5. 5
    Inter-cluster road length left implicit.

    The hearing daemon delivers by ground-truth graph distance, not by who the speaker believes is listening (koditoki-9fc5). Road length has to be sized ≥ hearing range + 1 explicitly and verified with a two-cluster probe, or a neighboring forager quietly picks up a promise it can never collect on.

Where this brief overrides the survey it was built from: the survey frames share as tunable and asks for measurements at 3 and 6; three independent probes (trade-cost, frame-bit, and the direct share∈{1,2,3,6} sweep) all agree it overflows at share=2 under the literal design and by share=3 under every scoping variant tried. This brief ships share defaulted to 1 and documents anything higher as a deliberately-broken experiment, because that is what three independent measurements say is actually true of this frame today — not because the knob is unwanted.