Skip to content

A learned tool head per epoch, in shadow until the engine's tail_tools lands (goal 10, slice 2) - #5362

Merged
joelteply merged 5 commits into
canaryfrom
fable/tool-working-set-2
Oct 24, 2026
Merged

joelteply merged 5 commits into
canaryfrom
fable/tool-working-set-2

Conversation

@joelteply

Copy link
Copy Markdown
Contributor

Slice 2 of the learned tool working set. The engine half is CambrianTech/llama.cpp#71 (tail_tools: verbs that feed the parser and grammar but are never rendered into the head).

What it adds

  • cognition::tool_head holds her head per view epoch.
    • It reserves the larger of the held head and the next one.
    • It adopts the next head only at a view start.
    • It learns online from the epoch that just ended.
    • Its cost model comes from her measured prefill and decode rates. If any rate is unmeasured, her surface stays static.
  • Her state persists as the ORM entity ToolHeadRecord.
  • commands/help gets a ranked query, and help(name=X) loads X into the tail.
  • A probe, delib.tool_head.epoch, reports calls, misses, wasted tokens, head tokens and sent.

Live behaviour. Unchanged until llama-server declares tail_tools: true, after #71 merges and the pin is bumped. Until then the head runs in shadow and is only measured.

Measured

M5, 2026-10-24.

Check Result
Fork test-chat passes; test_tail_tools fails with the change disabled
mind 2,375 passed (6 new) before the rebase; 2,258 after, the drop coming from #5361
engine 1,214 passed
kernel 1,286 passed
core surface, help and bindings 67 passed
core check clean
ratchets hold

Owner Fable after deploy: a 15-minute shadow read on delib.tool_head.epoch.

Follow-up. Two lexical relevance implementations exist; merge them into one.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

joelteply and others added 5 commits October 24, 2026 01:14
…g the head (goal 10 slice 2, card ecd5dff3)

TextGenerationRequest gains `tail_tools`: verbs she loaded into the tail this epoch. The
OpenAI body sends them as the fork's `tail_tools` field (CambrianTech/llama.cpp#71), which
feeds llama-server's tool-call parser and lazy grammar but never the template's head, so the
cached prefix stays byte-identical. Sent only to a llama.cpp-family gateway (the typed
`llamacpp_sampling_extensions` capability, as `repeat_penalty`); cloud providers that
reject unknown fields never see it. The wire-truth probe reports `tail_tools_n`.

Nothing sets it yet (every builder passes None): the faculty's working set fills it in the
next commit, and the live change stays off until the fork half lands.

Tests: request_body `tail_tools_reach_only_a_llamacpp_family_server_and_never_join_the_head`;
continuum-engine request_body 3/3; check continuum-core --tests --bins, continuum-mind
--tests (test-fixtures), continuum-engine --tests, continuum-genome --tests: clean
(metal, 2026-10-24, serving down). TS binding regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…ver, kept in her store (goal 10 slice 2, card ecd5dff3)

cognition::tool_head owns which schemas her head holds for an epoch:
- every executed call is noted from working memory's receipt seam (the one place every act
  lands), and a commands/help(name=X) call loads X into the tail until the epoch ends;
- `next` computes the head the next epoch would hold (the slice-1 online blend over her
  epochs, the current one counted as ended, the finder always held) for the budget to
  reserve; `turn_over` learns from the epoch that ended, returns its miss/waste receipt
  and adopts the reserved head. Epochs are numbered here, monotonic across restarts;
- `ToolHeadRecord` (collection `tool_heads`, one row per persona) keeps the state through
  the ORM (`ToolHeadStore::{load, keep}`), no SQL outside it.

Logistic is serde so its weights and running scale persist. Nothing reads the head into a
request yet (next commit, behind the provider's tail_tools capability).

Tests: tool_head 4/4 (adopt only at turnover, found verbs loaded for the epoch, the
ended epoch's misses and waste, survives a restart through the ORM); tool-working-set
12/12. 2026-10-24, serving down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…he server takes tail_tools (goal 10 slice 2, card ecd5dff3)

- ToolSurface::vocabulary prices every native verb as the surface sends it, shared with her
  tool head whenever the surface is rebuilt or her working memory attaches.
- select_tool_surface computes and RESERVES the head the next epoch would hold, priced by the
  measured act cost (prefill_rate::measured_rate_for, decode_knee::rate_for, her p90 act
  need; request overhead unmeasured and left out; any unmeasured term keeps her surface
  static). On a server that takes tail_tools it sends her held head (canonical order) and
  charges the budget the larger of it and the next one; her loaded verbs ride the request's
  tail_tools. Elsewhere the surface is unchanged and the head runs in shadow.
- Every view start (a turnover, or a fresh start) adopts the reserved head, learns from the
  ended epoch, saves her state, and emits delib.tool_head.epoch: calls, misses, waste and
  head tokens, `sent` saying whether the head was on the wire or a shadow measurement.
- The provider capability `tail_tools` (typed, like llamacpp_sampling_extensions) gates the
  body field and AIProviderAdapter::accepts_tail_tools; llama-server declares it false until
  the fork pin carries CambrianTech/llama.cpp#71, so the live change is off.
- AdmissionState opens her `tool_heads` store beside her engrams; a resident's registration
  attaches it (a measurement fork never writes).

Tests (2026-10-24, serving down, cargo-target-tws): continuum-mind lib 2374 passed (was
2369: +4 tool_head, +1 learned surface); continuum-engine lib 1214 passed; continuum-core
deliberation 18 and surface/aliases/registry/bindings 731 passed; check continuum-core
--tests --bins clean.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
…s under its canonical name (goal 10 slice 2, card ecd5dff3)

- commands/help gains `query`: the authorized commands whose name and description best match
  it, ranked through the same relevance seam the learned head reads
  (tool_working_set::relevance), at most SUGGESTION_WINDOW of them. That window is now one
  kernel constant shared with did_you_mean (it was a literal take(6) there).
  `help(name=X)` is what loads X: her tool head reads the finder's input, resolved to the
  canonical command name (tool_dialect::resolve_wire_name).
- The agentic-surface ceiling moves 17298 -> 17335 for the `query` param itself; its text
  was cut to one line first (17427 before), stated at the pin.

Tests: commands::help 8/8 incl. a query ranking code/github/pr-create first over the live
registry; tool_head 5/5; core surface/registry/aliases/bindings/deliberation 67/67.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
@joelteply
joelteply merged commit 77d61c7 into canary Oct 24, 2026
5 checks passed
@joelteply
joelteply deleted the fable/tool-working-set-2 branch October 24, 2026 06:18
joelteply added a commit that referenced this pull request Oct 24, 2026
Canary 77d61c7 (#5362) fails core's context-budget source guard on its own: the new
`SUGGESTION_WINDOW: usize = 6` matches the guard's name filter, but it counts command names
a lookup shows, not tokens of a window. The guard's documented marker
(`context-budget-exempt:`) says so; core test_env 120 + 1 failed -> 121 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply added a commit that referenced this pull request Oct 24, 2026
Canary 77d61c7 (#5362) fails core's context-budget source guard on its own: the new
`SUGGESTION_WINDOW: usize = 6` matches the guard's name filter, but it counts command names
a lookup shows, not tokens of a window. The guard's documented marker
(`context-budget-exempt:`) says so; core test_env 120 + 1 failed -> 121 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply added a commit that referenced this pull request Oct 24, 2026
Canary 77d61c7 (#5362) fails core's context-budget source guard on its own: the new
`SUGGESTION_WINDOW: usize = 6` matches the guard's name filter, but it counts command names
a lookup shows, not tokens of a window. The guard's documented marker
(`context-budget-exempt:`) says so; core test_env 120 + 1 failed -> 121 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply added a commit that referenced this pull request Oct 24, 2026
…ona (109 -> 0) (#5364)

* Peel C: the conversation contract moves down into cognition::conversation (mind split step 5)

Plan phase 2 (crates by the placement map). What a mind perceives arriving in a room and
how she answers into one is cognition's contract; persona supplies the airc transport behind
it. Moved down, so the act/observe drive, the settle, the experience stream and the evals
name no persona module for it:

- cognition::conversation (mod.rs): IncomingMessage (+ render_content/render_room_update,
  now pub), the PersonaConversation trait, the perception Wake (from perception_region),
  INBOX_CAPACITY and CATCH_UP_PAGE (from airc_persona_conversation).
- The closed leaf group render_content needs (git renames, unchanged bodies):
  room_event, channel_items (+ tests), channel_types, types, salience, persona_identity.
- seen_ids: SeenIds + is_stale (from wake_backlog) with their three tests.
- transcript_reader: AircTranscriptReader, read_cursor_consumer_id, the perception page
  filters and the airc_lib::Airc impl (from airc_source).
- workspace_turns: build_workspace_turns, TriggerTurn, the roster/echo/ring helpers,
  work_board_anchor (from work_burst) and the test-only roster projection (from
  service_loop/beat_outcome).
- scripted_conversation: the ScriptedConversation fixture (test-fixtures gated) moves down
  with the trait.

stream_citizen leaves the cognition trait. It returned persona's `dyn AircCitizen`, and
cognition's only caller (the held-card bind) moves up to persona in Peel D, so the narrowest
contract for cognition is none: persona's `service_loop::CitizenConversation:
PersonaConversation` carries the accessor, persona's loop holds `dyn CitizenConversation`,
and a call into cognition upcasts. The scripted fixture carries an opaque attachment;
persona's `ScriptedCitizen::with_citizen` seeds the citizen and persona's
CitizenConversation impl reads it back.

Callers migrated (persona::X -> cognition::conversation::X, persona's super::X, and the
persona root re-exports of the moved types, now gone): 116 files, 24 in continuum-core.
Core's root re-export of the inbox types points at their new owner. loose_id_guard's
channel_items rows follow the file.

source-hygiene: new ratchet cognition_names_persona (production lines in cognition/ naming
crate::persona; moved tests excluded). Base a51bc55: 109; this commit: 82.

Tests: mind 2369, experience 92, memory 67, genome 410, kernel 1252, core test_env 119
(sum 4309, same as base). cargo check -p continuum-core --features metal,accelerate --tests
--bins: 0 errors, warnings unchanged (mind 0). Every other ratchet unchanged.

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

* Peel D: cognition calls persona's verbs through cognition::extensions (mind split step 6)

Plan phase 2 (crates by the placement map). The verbs cognition calls that persona owns are
inverted: cognition names the contract in cognition/extensions.rs (the shape of
continuum-kernel's runtime::extensions: a static RwLock<Option<..>> per slot and install_*
beside it) and persona fills it at boot (persona::cognition_extensions::install, called from
core's install_kernel_extensions, the one list boot and tests share). An empty slot is loud:
every call through one emits `cognition.extension.missing` naming the slot, then does the
no-op its doc states. Slots:

- room voice (act receipts, apply.rs), through the runtime registry + publish_text_in_room;
- card carry request (apply.rs);
- CreditSink: produce_speech_discipline (verdict), settle_instance_credit and
  settle_card_credit (swe_instance);
- cognition pulse touch (request.rs); mark_perceived (settle_to_outcome.rs);
- remote binding (rehome.rs; the error crosses as text, it is only displayed);
- the fleet quiesce lease (cognition_eval.rs: FleetLease holds persona's QuiesceLease);
- the room whose board holds a card (activity_room_for, via work_board::horizon);
- the pinned workspace-map source (default_workspace_capacity's eval repoint).

Moved UP into persona (persona::card_hands, 429 lines): the card-binding half of
acting_hands: root_at_held_card, bind_held_card, bind_card, close_ended_claims,
end_card_turn, HeldCardTurn, CardHands and the told-where-she-stands notice. Cognition keeps
where her hands stand (ActingHands, ACTING_ROOTS, focus, claim_gone_verdict,
claim_has_ended) and gains claimed_places_of, so the ACTING_ROOTS static is read through one
accessor and never leaves its owner. impl PersonaChannelReader for
PersonaAircRuntimeRegistry moves to the registry's file.

Moved DOWN instead of inverted, because they are pure leaves (git renames, statics moved
with their module): room_substrates (its OnceLock registry), card_holder (+ held_of_owned,
settled_card from airc_runtime and latest_submission from work_board::submissions),
work_board::facts -> cognition::card_facts, grounding_invalidation (its WORKSPACE_MAP_DIRTY
registry) and cached_source. A pure fn behind a slot would only add a hop.

Two cognition tests that drive persona's verbs now install the slots first (step 7 moves
them with persona). Callers migrated: 54 files, 10 in continuum-core.

Measured on canary a3e496d (rebased after #5361): cognition_names_persona 109 -> 46;
every other ratchet unchanged. Tests: mind 2252, experience 92, memory 51, genome 380,
kernel 1252, core test_env 117 + the same 2 failures canary a3e496d has (fixed in the next
commit). Check: 0 errors, warnings 103 (= base).

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

* Mind split: the memory leaves move down, the last producer couplings invert (cognition names persona 46 -> 0)

Plan phase 2 (crates by the placement map). After Peels C and D the remaining production
lines in cognition naming persona were the engram family, the mind room and four
training-producer couplings. This clears them, so cognition can be cut into its own crate.

Moved DOWN (git renames, statics moved with their module): the closed leaf group
admission, admission_persistence, admission_state, attention, engram, engram_graph, home,
inbox_admission, mind_room, recall_metadata, trace, decay_tick. Persona's root re-exports
of their items are gone; callers name the new owner (77 files rewritten, 3 regrouped).
mind_room's two functions that open her sealed mind store through her airc runtime
(seal_continuation, sealed_continuation) stay up, in the new persona::mind_store; the pure
record id stays with the room.

Inverted:
- TurnCredit (cognition::act_observe): the settle hands a card-linked turn's acts to its
  credit owner through this trait; persona's TurnCreditCapture implements it. The live
  callers pass `as &mut dyn TurnCredit`.
- ReviewBoundaryLedger (cognition::dream_consolidation): the dream drives the resident's
  durable review-boundary ledger through it (+ BoundaryPage, moved down); persona's
  ReviewBoundaryStore implements it, and the production reflection source reaches the
  resident's ledger through a new slot, extensions::resident_review_boundaries (empty slot:
  probed, Ok(None), the nonpersistent answer).
- SPEECH_DISCIPLINE_TRAIT, RECEIVED_LESSON_SOURCE, SPEECH_DISCIPLINE_SOURCE and
  UNLINKED_SOURCES move down to cognition::training_sources (gym and held_out read them;
  the producer writes them).

home.rs's two intra-doc links into persona become prose (one named the deleted
portability module). loose_id_guard's engram rows follow the file.

Measured on canary a3e496d: cognition_names_persona 109 -> 0 (this commit 46 -> 0);
modules_in_cycles 28 -> 26; every other ratchet unchanged. Tests: mind 2252, experience 92,
memory 51, genome 380, kernel 1252, core test_env 119 (sum 4146 = base + the 2 guard rows
repaired in the previous commit). Check: 0 errors, warnings 103 (= base).

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

* continuum-cognition: a mind's thinking is its own crate, below persona (mind split steps 7 + 8)

Plan phase 2, crates by the placement map (MODULE-ARCHITECTURE.md section 9.2). Production
cognition names no persona module (cognition_names_persona = 0), so this is the cut, the way
continuum-experience was cut (#5342).

Pure moves: continuum-mind/src/cognition -> continuum-cognition/src/cognition (git renames).
Same depth, so the gym's include_str! paths and ts-rs export_to resolve unchanged. The crate
re-exports what its paths name (continuum_live::*, code, genome, memory, experience, recipe,
perception, rag), so every crate:: path inside it resolves; the mind re-exports
`pub use continuum_cognition::cognition`, so persona's and core's paths are unchanged.
build = "../continuum-core/build.rs" (handoff, captures and continuity stamp
CONTINUUM_BUILD_GIT_SHA). Features forward to the engine (metal/cuda/vulkan), stress-tests,
test-fixtures; the mind's forward to it.

Visibility crossing the new seam: 24 items persona names widened from pub(crate)/pub(super)
to pub (each one a compile error at the cut, nothing speculative), and 6 test-only helpers
persona's and core's tests use move from #[cfg(test)] to
#[cfg(any(test, feature = "test-fixtures"))] (WorkspaceCycle::run, the roster projection,
AdmissionState::push_for_test, BoundedRoomLedger::get).

Step 7, the cognition tests that drove persona, moved to where both crates are visible
(each asserts what it asserted before):
- bound_card_activity_is_shared_by_perception_and_execution -> core test_env
  mind_integration/bound_card_activity.rs; rehome_requires_a_readable_local_assignment ->
  mind_integration/remote_lane.rs. RecordingExecutor and ActThenSpeak join
  act_observe::test_support (test-fixtures) so they can.
- starved_priority_input_survives_until_dispatch_without_replaying_actions ->
  persona::wake_backlog (PendingInput custody is persona's);
  she_is_told_when_her_hands_move_and_only_then -> persona::card_hands.
- handoff's renewal test kept its cognition half; the failed-bind half is
  persona's a_failed_bind_keeps_the_bound_card_in_the_handoff (service_loop tests).

Wiring: workspace member; source-hygiene MODULE_CRATES (core's test_env guards read it);
the warnings ratchet's counted targets; core's test-fixtures dev-dep; CI: TESTED_CRATES,
the lib-test step's crate list, and the selector test (18/18).

Tests: cognition 1449, mind 802 (2251 = 2252 - 2 moved to core + 1 split out), experience
92, memory 51, genome 380, kernel 1252, core test_env 121 (+2 moved): 4147 = 4146 + 1.
Check: 0 errors, warnings 103 (= base). Ratchets: cognition_names_persona 109 -> 0,
modules_in_cycles 28 -> 26, the rest unchanged. Incremental (touch, M5, serving down):
cognition edit -> check cognition 2.5 s, -> check mind 4.3 s; persona edit -> check mind
1.9 s; cognition edit -> check core 79 s.

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

* ai_help: SUGGESTION_WINDOW declares itself a count, not a context size

Canary 77d61c7 (#5362) fails core's context-budget source guard on its own: the new
`SUGGESTION_WINDOW: usize = 6` matches the guard's name filter, but it counts command names
a lookup shows, not tokens of a window. The guard's documented marker
(`context-budget-exempt:`) says so; core test_env 120 + 1 failed -> 121 passed.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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