Skip to content

The live M5 stack: every change running on the residents, merged with canary - #5917

Open
joelteply wants to merge 83 commits into
canaryfrom
fable/a-forgotten-read-still-counts
Open

joelteply wants to merge 83 commits into
canaryfrom
fable/a-forgotten-read-still-counts

Conversation

@joelteply

Copy link
Copy Markdown
Contributor

The branch deployed on the M5 (live as 99919af before this merge), with canary merged in (not rebased). It carries every change measured live on 2026-11-10:

  • thoughts fold;
  • every turn with hands is an act;
  • thinking is never budgeted;
  • censored needs reach grants;
  • evicted reads count;
  • re-read weights;
  • forgotten reads count and thin ages read her own rate;
  • queued minds wait their turn, with reclaim and yield ending the turn instead of deadlocking;
  • embeddings lease per pool;
  • the stored-row decode;
  • a mind's children run below the engine.

Supersedes #5910, which closes as included.

Measured live: see the receipts on #5906, #5907 and #5910, and the 15-minute reads in this session.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

joelteply and others added 28 commits November 9, 2026 21:09
…d: no floor grows with everything she ever thought

Benchy's, Aris's and Aiko's acts were refused before they reached the model (the M5, 709fe47,
2026-11-09): on an act every view source's floor is required, and her thoughts had no fold, so
each thought her memory shed kept its own pointer line in the floor, forever.
`delib.payload.over_window` named the binding source `view/thoughts` for all three:
Benchy 78,755 tokens (2,536 thought pointers at 31 each, from her volatile.json) against a
27,333 window; Aris 39,240 against 26,202; Aiko 45,318 against 21,352. Atlas fit only because
his window is 102,232, and 1,704 collapsed thought pointers sat in it. Actions already fold
(4-5k folded per resident), so the floor was all thoughts.

- Thoughts::fold: the oldest run is ONE line, its first and last #n, how many, and how to
  reopen the newest of them (each was saved whole when shed).
- Source::fold has no default: a source that does not decide floored every item at its
  pointer, silently. Each source now states its decision: tasks fold to one line; conversation,
  facts, notices, open views and held work say why they need none (no pointer, or superseded by
  key); test fixtures say they do not exercise folding.
- Regression test: 1,000 shed thoughts floor at no more than 100 do, one line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…h returns its typed error, the checkpoint names its disk boundary

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…short reply's cap and discarded

is_work_turn asked only for a deliverable or her initiative, so a message arriving while she
worked a card was a PASS with a short reply's ~400-token allowance, though her hands are the
same on every turn. The write she was composing reached the cap before its call committed and
the whole act was discarded ("tools/<cut at the output limit>"): 394 of Atlas's acts, 277 of
Aiko's (the M5, 2026-11-09, volatile.json), while they went 392-542 acts without a write.
Lane admission already ranked a held card as work (admission.rs); the cap now agrees.

The new test fails without this (the turn stays a pass) and 984 pass with it. The touched
file's six legacy ratchet sites (MIN_SERVE_CTX as a window, two latency constants, an image
base64) are cleaned before merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…er decides what a mind may do

Joel, 2026-11-09: "Minds aren't for cards, they're for thinking and doing, period." The previous
commit made a held card turn a message turn into an act; that still let a system-picked class cap
her. is_work_turn is now her capacity alone: hands. A mind with no hands can only speak, and takes
a pass. Lane admission's held-card branch (holds_work_card) is gone with it.

The test: a message turn with no card and no deliverable is an act for a mind with hands, and a
pass for one without.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…eads its collapse costs

An act's outcome was hashed only while its body was in RAM; working memory moves bodies to their
handles within a few acts, and an evicted read hashed as Absent, which is no occurrence
(rereads.rs: only an observing act with a hashed outcome is one). A re-read of it never matched,
so her view measured zero re-reads in every age bucket while Atlas re-read an identical file 103
times in 300 acts and 70-90% of every resident's file reads repeated one 3-5 acts old (the M5,
2026-11-09, volatile.json). Collapse looked free to the depth choice, her view collapsed what
she had just read, and she read it again.

- ActionRecord::stored_content: the whole's hash, taken as the body leaves RAM (shed_body),
  checkpointed with the record (ContentHash serializes).
- action_renderings hashes a resident body, else the hash it kept: the same occurrence evicted.
- Test: an evicted read renders the outcome hash it had resident, through the checkpoint's disk
  form too; it fails when the hash is not kept.
- The touched working-memory files' unwraps each carry their reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
… re-shrinks while the engine's count rises instead of giving up after one try

With every turn she has hands for an act, the window-feedback tests exposed an act path that gave
up: its cap shrank to the engine's measured room ONCE, so a second, larger count fell through to
a view refit that refused, and the act she composed was parked. The cap now re-shrinks each time
the engine's count rises (bounded by the window, so it ends) while her reply's grant still fits.
The tests state an act's cap as the room past its prompt, at least her reply's grant, and each
retry as measured progress (a smaller view, or the same view with a smaller cap); a mind with no
hands takes the pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…act's cap, never by her past thinks

act_reasoning_budget took her reply's grant, sized from her measured past think and answer, so
her reasoning could never grow past what she had already thought: on the M5 (51db8ff,
2026-11-09) the median act's think budget was 536 tokens (302-2,445) while its cap was the whole
room past her prompt. The budget is now that cap past her measured answer, so the answer keeps
its room and she thinks as far as her window lets her. A pass is unchanged (its cap is its grant).

The test pins it: with the act's cap, the think budget is the cap less her answer; it fails with
the grant-bounded budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…on her turns

Joel, 2026-11-09: "If you cut off thoughts it totally ruins their mind"; "not forced to act by you
or the system". The previous commit bounded her think by the act's room instead of her past
thinks; it still closed her thought at a number. Her turns now send no reasoning budget
(Think::ModelDefault): she thinks until she decides she is done, inside the room her window has.
act_reasoning_budget has no caller and is deleted with its assertions; the test asserts no think
budget is sent and the output-room receipt carries none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…tokens does, granted as its value

resident_need dropped every censored sample (her act's priced window lay past her grant), so a mind
whose own price chose more than her grant stayed pinned at that grant: every move down needs a way
back up (Benchy's censored needs of 27-44k against a ~26k grant, the M5, 2026-11-09). The rule
dated from 2026-11-04, when a censored sample was an uncollapsed overflow and the grant handed a
bound a whole sequence; since #5833 a censored sample is her PRICED window. resident_need is now
need_tokens' value, granted as a number (never a bound, never a whole sequence), within her wish
with the sent headroom; contention stays the allocator's proportional sharing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…is its priority scaled by her measured re-reads of what it would collapse

Her view's sources weighed flat constants (conversation, thoughts and actions all 5), so her acts,
whose reads she kept asking for again, got the same share of her window as a conversation she did
not re-read (the M5, 2026-11-09: 70-90% of her file reads re-read one 3-5 acts old). Pricing the
window's size for forgetting cannot fix that: by her measured act time a larger window slows every
act, and each re-read costs an act at that pace, so the lever is the split inside the window.

- View::claims prices each collapsible item at HER measured re-read rate (her live view's clock;
  the free claims() has none).
- Claim::weight = priority x (1 + her re-reads per act of what it would collapse); the window's
  one allocation weighs her view sources by it (epoch.rs). Unmeasured ages add nothing, so an
  unmeasured source keeps its priority. The start's fixed/growing split still reads the priority.

Test: four items asked for again half the time weigh a priority-5 source at 15; unmeasured, 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
On the M5 (26960c0) a mind queued behind others for room had her turn refused and
re-measured every publisher beat: 186 settle.inference_failed an hour, each one a full view
render. Queued, reclaiming, and an engine relaunching (Ungranted::awaits_room) now wait for a
later evaluation with her demand standing; any other reason still refuses within one cadence,
named.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
The embed lane leased a fixed 1,792 MiB (a 2,048-token context) for every call; with
1.5-1.7 GiB free on the M5, 758 of 834 embeddings in 3 h were refused (26960c0), so her
recall ran mostly dark. EmbeddingBatch tokenizes first and leases its padded window times the
measured per-token cost (engine-c, Qwen3-Embedding-0.6B, linear within 0.1% at 256-2,048
tokens): a short text leases ~182 MiB. The standing floor is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…padding has one owner

- The lease's two attempts are explicit: a second refusal carries the availability it measured,
  never a defaulted 0.
- The media path matches its one item instead of unwrapping it; genes and stop sequences flatten
  an absent list instead of defaulting it; display defaults and the trained-ceiling default say
  why; the lease TTL says it is a SIGKILL backstop only.
- llama pads every context to 256 tokens (GGML_PAD in llama-context.cpp): ContextParams::
  allocated_n_ctx in the llama crate is that fact's one owner, where EmbeddingBatch prices it.
- The queued-mind test's closure moves the id it captures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…in age reads her own rate

Her re-request measurement drained its index to what her sources still offered at each start, so
asking again for a read her view had dropped, the costliest re-request, was never counted: on the
M5 (2026-11-10) half her file reads repeated one from the same hour while the measurement counted
3 to 14 in 1,800 to 3,000 acts per resident. And with starts every 3 or 4 acts halving every
tally, each age bucket held a few exposures and no event, so one assumed event priced it at 0.15
to 0.43 per act, a hundred times her own rate: the depth and the source weights read a prior, not
her.

- The index of her occurrences is kept with her view, including what a start dropped, out to one
  bucket past the oldest age her view holds; past that it drains.
- Hazard::Measured carries her rate over every age (one more event than she made), and a bucket
  reads (events + prior) / (exposure + 1): a thin bucket reads what she does, a full one its own;
  a mind with no history still prices a re-read high.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…r 1,000 lines)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…bsent: her learning runs again

Every reader of a DataRecord decoded it itself (serde_json::from_value(row.data), 15 production
sites). A field an entity gained later with #[serde(default)] is NULL in every older row, which
serde reads as a present null, so it failed: on the M5 (2026-11-10) the training trigger's
recovery died every 30 s on 380 submissions with `learned` NULL ("invalid type: null, expected
u32", 279 training.serving_wake.failed in 4 h), and no resident's in-place learning ran.

DataRecord::decode (orm/record_decode.rs) is the one owner: strict first, borrowing the row, so a
null a type can hold (Option, serde_json::Value) reads as written; only when that fails and the
row holds NULLs are they read as columns the row predates, absent, so the field's default answers
and a required field still refuses. All 15 sites migrate to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…ard zero)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…on its own row

- record_decode: an older row and a written null are separate cases; a row holding both refuses
  (documented: telling the two NULLs apart needs the field types).
- orm/store.rs: find_all and find_eq are the verbs themselves, each caller counted where it reads
  (bounded: exemption); a failure without a message says so.
- lived_examples: a call with no message yields no example, never an empty prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…or is what it embeds

Joel, 2026-11-10: "It depends on where it's resident... We just can't assume any specific
machine." The embed lane leased VRAM on every node, even for a context in host memory (a backend
offloading no layer, or a build with no GPU), and its standing floor was a fixed 1,792 MiB VRAM
bound reserved by the resolver, which knows neither the context's residence nor her texts.

- LlamaCppBackend::context_pool: device memory when llama offloads to a GPU
  (llama::supports_gpu_offload) and the backend offloads any layer, else host memory.
- inference/embed_lane_memory.rs owns the lane's memory: each lease in that pool, and the floor
  re-stated at each lease at the largest context the lane has embedded (the resolver's probe
  and calibration passes set it first). EMBED_LANE_VRAM_BYTES and the resolver's reservation are
  deleted; the lease code leaves llamacpp_adapter.rs (938 -> 848 lines).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…reclaim it blocks

The reclaim of a mind's grant takes her turn lock (MindTurn::reclaim), so a turn that waits for
the reclaim inside itself waits on itself: on the M5 (d0abbdd, 2026-11-10) Aris parked to the
1,500 s tick deadline five times and acted 3 times in 132 minutes. Reclaiming refuses again (her
turn ends, the reclaim proceeds, she is regranted); queued and an engine relaunching still wait.
Each wait now probes its reason (delib.window.waiting), which the loop had dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…es idle cores and yields

On the M5 (2026-11-10) a resident's `cargo test` of the whole workspace ran at the engine's own
priority beside serving: load 36, 4.5 GB swapped, decode at 3.4 tokens/s against 56 at rest, and
the residents' acts fell from 39 to 10 per 15 minutes. Her build is never refused or capped for
it; it is scheduled like an OS background job.

- continuum-host process_class: Unix (macOS, Linux) lowers the child to the OS's lowest ordinary
  priority (PRIO_MAX), which still runs on every idle core; Windows gives it
  IDLE_PRIORITY_CLASS; elsewhere the parent's class.
- The kernel's command_for, the one way a caller's child is spawned, applies it to every child,
  confined or not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…th a floor per pool

Codex on #5910: one context's bytes are in two pools (KV and device compute on the device; the
CPU's compute share and the output buffer on the host), a configured placement is not a measured
one, and one process-wide floor was reused across pools.

- Placement (configured, never measured: card 9bddda93) is Device, Host or Split. A split is not
  measured, so each pool is charged its upper bound.
- ContextBytes { device, host } comes from rates written as receipts (MiB llama reported over the
  tokens measured), now including the host-resident context: CPU KV 224 and compute 1,200.97 MiB
  at 2,048 tokens (engine-c, -ngl 0, logs in evidence/replicate-2026-11-10).
- embed_lane_memory leases each non-empty part in its pool, with a floor per pool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…GPU hint caps a process, and an unreported memory mode is unknown

On the M5 (2026-11-10, resources/board) the VRAM row read 0 available while the RAM row read
3,165 MiB of the same unified memory: the VRAM ceiling was min(pool, Metal's working-set hint),
netted against the whole system's usage, CPU-only processes included. A 4,096,000-byte learning
step waited on it (training.admission.waiting) and every VRAM lease beyond a floor did.

1. Unified memory is one pool: both views carry the pool's ceiling and one measured usage.
2. The working-set hint bounds ONE GPU process (CapacitySource::per_process_cap,
   ResourceDaemon::per_process_cap): the serving plan sizes the engine under it as well as
   under what the pool has left; it never shrinks the pool.
3. Dedicated VRAM is unchanged (GpuCapacitySource, no per-process cap).
4. The memory mode is the backend's report: GpuMonitor/GpuDeviceProbe::memory_mode return
   Option, None is unknown. Metal reports hasUnifiedMemory, NVIDIA its dedicated framebuffer.
   An unreported mode leaves VRAM ungoverned and named (resources.memory_mode_unknown), never
   a guessed Discrete over what may be one pool (an AMD APU, an ARM iGPU).

Tests: both rows of the M5's unified pool offer 3,165 MiB; the hint caps a GPU process, not the
pool; the serving plan reads the pool and stays under the cap (simulated 32 GiB host, labelled).
Card 13c81ced: the first resource-kind adapter in the shared-resources shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…ry, the GPU hint caps a process, and an unreported memory mode is unknown"

This reverts commit 75f1ba7.
…ead-still-counts

# Conflicts:
#	core/continuum-context/src/view/tests.rs
#	core/continuum-engine/src/capacity/mind_window.rs
#	core/continuum-engine/src/capacity/window_grant.rs
#	core/continuum-engine/src/inference/backends/llamacpp.rs
#	core/continuum-engine/src/inference/backends/llamacpp/embedding_batch.rs
#	core/continuum-engine/src/inference/llamacpp_adapter.rs
#	core/continuum-engine/src/inference/mod.rs
…handoff it blocks (the M5, 15:01-16:05Z: holders parked to the 1,500 s deadline)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…he one that served most

On the M5 (2026-11-10, 75f1ba7 and after) two lanes served four residents, and Aiko and Benchy
waited queued for ten minutes (49 and 55 delib.window.waiting) behind holders that were idle or
slow. Only fresh service since a grant made a holder a yield candidate, and the armed yield was
the first holder by id, so a slow or idle holder pinned the queue.

- A holder idle across two evaluations (her seat from before the first, nothing in custody at
  either) is a handoff candidate whether or not she served since her grant; one that never
  served on this engine holds no KV here, so her seat is withdrawn with no physical handoff.
- The holder armed to yield (yield_after) is the one that served most since her baseline.

Tests: an unused seat goes to the waiter after two idle evaluations, never one; the holder that
served most is armed, not the lowest id. Draft for Codex's review (his grant custody code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…(reproductions of Aiko's 2026-11-10 balloon)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply and others added 30 commits November 10, 2026 13:25
…he planner's smallest lane"

This reverts commit 670c319.
…ery turn she has hands for is an act, 60cf344)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…iles' unwraps say why; engine_install named by path, not a cfg'd re-export

A floor of what the epoch held refused her turn when the window shrank (window_feedback tests): a
window that cannot hold it turns the epoch over. As min_tokens it is still topped up before any
other claim grows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…rts that named the deleted grounding path"

This reverts commit b26493f.
…nd assembled-context path is deleted"

This reverts commit a2c46d1.
…gine_slots is left as it was

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…lds nothing; the directed-turn loop reads the contract in force

A view that had just started held its whole receipt as its flex basis before any prompt carried
it, and the act's newest room update collapsed to a pointer (capacity_and_wire: "room updates must
survive"). `View::sent` records how many of the epoch's events the engine received; `held` sums
only those, so a start (or a restored view before its next send) claims nothing it never sent.
The directed-turn loop asserted a contract block's absence, which a standing block of her view
keeps as history; it reads the in-force line, as the directed check above does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
… a pass and an act no longer share a faculty

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…no KV, and its refused save deferred every relaunch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…xt costs no prefill

Live on the M5 (2026-11-10, c5c4542, 21:11-21:22Z): views granted 7.5k-13.5k tokens of 41k-49k
windows with no room left, an epoch every second act. The price held the window where value per
second peaks on her measured act time, and that time was measured while turnovers re-prefilled her
whole view: a self-confirming squeeze. What her live view already sent is resident (computed once,
no prefill), so it joins what she must hold (`least`), beside her held head levels and her newest
result: the priced window never drops below it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…ne view: she reopens instead of re-reading

The M5 (2026-11-10, friction read over 6 h): 20 identical re-reads cost 2,876 s; Atlas read one
666-line file four times. Her ledger said "read 1-752 (all of it)" with no content and no handle:
only a paged read named one. An inline read that showed the whole file now names its own saved
handle (a partial inline read keeps the handle already named, since its own covers only its
range), and `./x` and `x` are one view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…lue order

kv-pages was DEFERRED: nothing could take a byte back under disk pressure.
On the M5 (2026-11-10) it held 30 GB, ~3.1 GB of it restorable.

KvPageStorePool (continuum-engine slots/page_store_pool.rs) frees, cheapest
regret first: pages of model dirs no live slot pool serves; then pages the
serving pool does not confirm; then the coldest confirmed pages of minds no
slot holds, each forgotten through note_page_lost before its file goes. Never
a resident's page, never saved-ledger.json, never a served page while page IO
is in flight, the node stops or an endpoint transitions; unserved dirs wait
while an engine launches. Budget: the derived-cache policy
(cargo_target_budget_bytes of the volume); pressure includes the volume's.
Registered after the cargo pools; kv-pages moves from DEFERRED to OWNED.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…ad the engine holds

Live on the M5 (1ee16e3, 21:52Z): Benchy's tools changed at a view turnover and her act cached 0
of 25,228 tokens; the head renders ahead of every message, so adopting a new head recomputed her
whole prompt where a turnover alone re-prefills only her view. The next head is adopted only by
the act that started her view (the first after a start or restart, where nothing of hers is
cached); verbs she picks up meanwhile ride her tail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…t, so her next acts append instead of turning over

Live on the M5 (1ee16e3, 21:41-22:05Z): Aris was granted ~45-59k with her view at its edge
(33,939 held vs 33,306 granted), an epoch every second act, while the grants publisher used
109k-168k of a 524k pool and 34 `persona.need.censored` said minds wanted more. Her need recorded
only what one act held; the reserve her view's start leaves never reached the publisher. The need
now grows by what her view already sent (`ActNeed::grown`), the same reserve the start leaves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…ression, measured)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…rebuild repays

Live on the M5 (2edea7a, 23:44-23:56Z): with the cache holding, acts prefill 1-3k fresh tokens
but decode at 9-10 tok/s over 65-88k held context, against ~15 at 20-30k, so a long-lived view
slows every act. Two corrections:
- Her fresh tokens per act are fitted on warm acts only (most of the prompt cached): cold and
  turnover acts re-prefill everything and taught the price that context costs a recompute every
  act. Decode and prefill rates per token still learn from every act.
- Keep or collapse is a priced choice: keeping costs its measured seconds every act; collapsing to
  a smaller window costs that window's seconds plus re-prefilling it once, spread over the acts
  until her view grows back (size difference over her fresh tokens per act). The higher value per
  second wins (`priced::collapse`).
Tests: cold acts never enter the fresh fit; she collapses when warm acts repay the rebuild and
keeps when each act re-prefills a lot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…llapse where it slows warm acts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…t and collapse where it slows warm acts"

This reverts commit 0b14502.
…ead of refusing her checkpoint

The revert of 0b14502 left checkpoints saved without the field; every seat failed with
"missing field `fresh`" (the M5, 2026-11-11 00:43Z).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…cing is measured before a deploy

`replay --allocate` priced every act as if nothing were resident. Live, her view is an
append-only epoch: each view claim's basis is what the epoch already sent (`Claim::held`), the
price holds it, and a grant below it is a turnover that re-prefills the view.

- examples/replay/epoch.rs: per resident, in capture order, each view source's basis is what the
  plan kept of it on her previous act (at least its floor); a grant below it counts a turnover;
  each act's fresh tokens are predicted (cold, turnover re-prefill, or the new tail).
- allocate.rs: an epoch plan beside the existing columns, priced by `--fit live` (default: her
  running fit fed each completed act as the core feeds it) or `--fit node`; prints turnovers
  (count, per act), mean held basis, predicted fresh per act beside the measured mean, and
  predicted s/act on the node curve at those fresh tokens. Reads acts through price.rs's
  `sample` (the duplicate parser is deleted); the message/loop parsing is one helper each.
- price.rs: `ActTime::seconds_prefilling(context, fresh)`; `seconds` delegates to it.

Not compiled here (serving node); CI compiles the example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…, not the act

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
The price trades value per second, so a collapse that buys her a better-leveled
context is not a regression by time alone; the gate reads both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…arlier acts on the model

Act time is the engine's and model's, not one mind's; --fit pooled replays what a node-wide fit
would price live, without the node fit's look at later acts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
A window growing inside an epoch changed the plan's head grants and the dyno replayed those acts
cold (Aiko: 12 acts, 7% of her seconds); live, her head is adopted only when an epoch starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…npriced

Aris on the M5 (2026-11-11): the fresh line was added empty in 0cb138f, his fit stayed
Unmeasured, his view filled the window (59k tokens), his acts grew too slow to finish under
contention and restarts, so the line never filled. Every held token is now charged as prefilled
until her own acts measure the line: the dearest reading, replaced by hers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant