Skip to content

bump glob from 11.0.1 to 11.0.3 - #74

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/glob-11.0.3
Closed

bump glob from 11.0.1 to 11.0.3#74
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/glob-11.0.3

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jun 16, 2025

Copy link
Copy Markdown
Contributor

Bumps glob from 11.0.1 to 11.0.3.

Commits

Dependabot compatibility score

You can trigger a rebase of this PR by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Note
Automatic rebases have been disabled on this pull request as it has been open for over 30 days.

Bumps [glob](https://github.com/isaacs/node-glob) from 11.0.1 to 11.0.3.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](isaacs/node-glob@v11.0.1...v11.0.3)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 11.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot
dependabot Bot requested a review from joelteply June 16, 2025 22:48
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Jun 16, 2025
@dependabot @github

dependabot Bot commented on behalf of github Jun 16, 2025

Copy link
Copy Markdown
Contributor Author

The reviewers field in the dependabot.yml file will be removed soon. Please use the code owners file to specify reviewers for Dependabot PRs. For more information, see this blog post.

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Jun 16, 2025
@joelteply joelteply closed this Nov 20, 2025
@dependabot @github

dependabot Bot commented on behalf of github Nov 20, 2025

Copy link
Copy Markdown
Contributor Author

OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting @dependabot ignore this major version or @dependabot ignore this minor version. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot
dependabot Bot deleted the dependabot/npm_and_yarn/glob-11.0.3 branch November 20, 2025 00:29
joelteply added a commit that referenced this pull request Jun 29, 2026
The caesar-prompt glass-box capture (2026-06-27) showed 5 memories at
salience 1.00 but cosine-relevance ~0 polluting every turn — rehearsed to
max salience by the recall-hit loop yet topically unrelated to the task.
They blended to ~0.5 (0.5·0 rel + 0.5·1.0 sal), sailing over any blended
floor, so they slipped into the prompt and spent the small model's
attention on noise (a factor in the wrapped-vs-clean coding collapse, and
a welfare symptom per the being-design lens).

RecallFaculty now budgets what it surfaces three ways, per Joel's directive
("budget by the closest match, limited to a reasonable number by the model
parameter size or some metric, in addition to window size"):

- Closest-match floor — gates on the RELEVANCE component (cosine), not the
  blended score, so a high-salience but irrelevant nag is dropped no matter
  how salient. Active only when relevance has a voice (relevance_weight > 0);
  the pure-salience A/B extreme and the no-embedder path are unchanged.
- Capability-scaled count — recall_count_for_window() maps the served
  context window (the metric the registry reliably carries today; param-size
  feeds in via #74) to a count: tight 4B window → 3, cloud-class → 12. A
  small model is not buried under memory it can't juggle. 0 (unknown) keeps
  the historical default of 5.
- Window token ceiling — recall may spend at most 10% of the served window,
  so it never crowds out the room transcript, identity, or other faculties.

Threaded from PersonaBrainConfig.context_window (single-sourced, #50) into
RecallFaculty via with_context_window in build_workspace_cycle.

This is context-assembly/relevance discipline (a resource + ACL-shaped
contract), not output puppeteering — sanctioned under the
no-hardcoded-heuristics rule.

3 new tests (the caesar contamination regression, count-scales-with-window,
count-bounded-by-tight-window); all 13 recall_faculty tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply added a commit that referenced this pull request Jul 3, 2026
…(June 29→July 2) (#1730)

* refactor(cognition): extract the gym grader into one shared module

test_grade / grade_rust + helpers were private to cognition/eval.rs. The
teacher-episode generator (genome/teach, next slice) must grade write→fix
trajectories with the EXACT same verdict the A/B evaluator uses, or a gene
that "passes" generation could "fail" eval for grader-shape reasons alone.

Per the compression principle (one logical decision, one place): lift the
grader into cognition/gym_grader.rs with pub test_grade + extract_code_block,
its tests move with it, eval.rs now `use`s it. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(genome): genome/teach — the teacher-episode generator (write→grade→fix→pass)

Cold-start for the engineering reflex. The genome loop closes mechanically but
produced INERT genes because the lever was never raw coding skill — it's the
reflex of write code → read the REAL compiler/test error → fix → re-run → answer.
You can't distill a reflex absent from the data (5 of 1761 captured turns ever
used a tool), so this bootstraps it: a teacher model writes Rust, the SHARED gym
grader (`cognition/gym_grader::test_grade`, the same one cognition/eval uses)
actually compiles+runs it, the real error feeds back, and it loops to green. Only
test-VALIDATED write→error→fix→pass trajectories become multi-turn ShareGPT
examples — the grader is the corpus-quality gate, the teacher only affects yield.

This is the sanctioned way the genome loop fixes behavior: curate the LEARNING
corpus by an objective scorer, never puppet live output. Non-disruptive — writes
a dataset, never touches the live :58057 serving lane. Procedure is never the
artifact: the reflex is LEARNED from these trajectories, not hardcoded as a
run-N-times loop. Feed the dataset to genome/job-create to forge the gene.

- `genome/teach` (stateless, Privileged): resolves the teacher adapter the
  canonical way (global_registry + resolve_model, mirrors generate_response),
  defaults to the locally-served model so it runs with no external dep; point
  teacher_model at a stronger peer for higher yield. Drops tasks with no `test`
  (can't validate) and tasks never reaching green within max_fix_iters — both
  named in the per-task outcomes, never a silent shortfall.
- dataset.rs: split_and_write becomes a `pub` associated fn (it touches no
  service state) so genome/teach packages to the SAME train/eval/manifest shape
  the dataset/* verbs use — one packaging path, not a parallel writer. 4 callers
  updated self.→Self::.

Tests: build_sharegpt order/role/content invariant (the trajectory ordering IS
the lesson); name+access mirror. cargo check + 5 teach + 20 dataset tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(genome): register Qwen2.5-Coder-14B as the cold-start teacher

The forged 4B is too narrow a teacher for the genome cold-start loop
([[genome-loop-trains-on-own-mistakes]]): it solves easy gym tasks
first-try (zero corrections) and never converges on hard ones (no
corpus), so its fail-then-fix-to-green band — the only band that
teaches the self-verify-and-correct reflex genome/teach distils — is
too thin. A stronger teacher widens that band.

Add Qwen2.5-Coder-14B-Instruct (Q4_K_M, ~9 GB) to the hand-authored
catalog under the "llama-server" provider so genome/teach's
select(Some("llama-server"), Some(model), …) can serve it on a lane.
Corpus generation is an offline batch (serving/pin it, generate, pin
the 4B back) so the live personas resume on their base.

cargo check --features metal,accelerate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(genome): own the native mlx_lm.lora train step in Rust (#52)

The downstream half of the genome loop (MLX adapter → PEFT → GGUF-lora)
already lives in forge/lora_convert.rs. This adds the UPSTREAM half:
forge/mlx_train.rs spawns Apple's mlx_lm.lora trainer as a Rust-owned
subprocess instead of delegating the run to the unsloth HTTP custodian
(the NVIDIA path).

Bakes in the two invariants the inert-gene diagnosis surfaced
([[genome-loop-trains-on-own-mistakes]]):
  - train-base == serve-base: caller passes the HF safetensors form of
    the EXACT served base; a run_mlx_train precondition fails loud if the
    dir is not a real HF model dir.
  - scale ~2, not 20: build_lora_config_yaml writes lora_parameters.scale
    = 2.0, which read_mlx_lora_hparams carries into the GGUF-lora as
    alpha = rank * scale.

Pure, unit-tested builders (build_lora_config_yaml, build_train_args)
pin the scale~2 invariant and the mlx_lm.lora CLI contract without a real
run. run_mlx_train fails loud on every precondition (missing interpreter,
non-HF base, missing train/valid split, non-pageable fine-tune type) and
on non-zero exit or a missing adapter artifact — never partial-success.

No Python in the .rs ([[no-python-in-rs-files]]): spawns python3 -m
mlx_lm lora and writes a YAML config; no inline Python.

cargo check green; 3 lib tests pass (5437 filtered).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(forge): native mlx train owns its base-prep (model_type + chat_template)

The genome loop's native mlx_lm.lora train step isn't truly "owned" if it
fails on the REAL forged base. Live validation against
continuum-ai/qwen3.5-4b-code-forged surfaced two normalizations the
GGUF-published model's HF form needs before mlx can train it:

  1. config.json model_type qwen3_5_text -> qwen3_5 (mlx dispatches the
     module by model_type; the _text suffix is the HF multimodal text-tower
     name, for which mlx has no module — but qwen3_5 it fully supports).
  2. tokenizer_config.json needs a chat_template (ChatML) so a chat
     {messages} corpus can render.

prepare_base_for_mlx() applies these as EXPLICIT, caller-supplied,
idempotent JSON edits (MlxBasePrep on the spec) — the substrate never
guesses an architecture nor invents a template; a None field is left alone
and mlx itself fails loud if it then can't dispatch/render. Pure Rust JSON
edits, no Python. The long-run home is the forge publish step; until then
the train step normalizes its own input.

run_mlx_train calls it after the train-base==serve-base precondition and
probes each change. Unit test on a temp dir asserts model_type rewrite +
template injection + untouched-field survival + idempotent second pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(forge): own the native mlx_lm.lora train step (#52) — managed dirs, fail-loud, scale~2

forge/train now branches on engine: Apple Silicon → native MlxTrainSpec/run_mlx_train
(the foundry owns the subprocess), else the custodian. Explicit `engine` wins; an
unknown value fails loud (never a silent custodian fallback).

Correctness invariants baked in (genome-loop-trains-on-own-mistakes):
- train-base == serve-base: `train_base_dir` is REQUIRED for mlx and fails loud
  naming the reason — the substrate never guesses which on-disk HF dir the served
  base maps to (a guess → washed-out ~0-lift gene).
- scale == lora_alpha / lora_r (one geometry contract lora_convert reads back),
  so the proven scale~2 rides through instead of the destabilizing scale=20.

Managed dirs only (no unsloth/legacy):
- interpreter resolves to ~/.continuum/genome/venv/bin/python3 (MLX_PYTHON override),
  fail-loud provisioning message points at the managed venv.
- dataset split → ~/.continuum/datasets/<name>-mlx, adapter → ~/.continuum/forge/lora/<name>.

Reuses existing primitives: DatasetService::split_and_write (one packaging path,
materializing valid.jsonl mlx_lm wants from the eval split), expand_user_path
(re-exported), MlxBasePrep base normalization (model_type + chat_template), idempotent.

Tests: engine-selection explicit-wins/fail-loud, managed-venv-not-unsloth,
native dry_run spec resolution (scale=alpha/rank, ~/ expansion, defaults),
fail-loud without train_base_dir. 31 forge + 4 mlx_train tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* fix(cognition): bound the offered tool set to the served window — unmute the persona

The full authorized command registry injects more tool-schema tokens than the
entire serving slot (~39.7k tokens of tools vs a 38.9k slot live). The
deliberation faculty offered ALL of them every tick, treating tools as
non-negotiable ("off the top"). On tool_use turns that drove the text budget to
zero AND still overflowed n_ctx → llama-server 400 "exceeds context size" → the
persona abstained the whole tick (mute). 7% of captured turns overflowed this
way, every one a finishReason: tool_use.

Fix: select the tool subset that fits the served window, guaranteeing the
conversation a turn floor (half the window) so a flood of tools can never blind
the persona to the room. Tools beyond the budget are dropped lowest-priority
(tail) first — the same drop-whole-in-priority discipline render_assembled_
context_within already uses for enrichment. Selection is pure + deterministic
(no ws input), so prompt_view (text budget) and contribute (offered set) compute
the SAME selection and agree on the window arithmetic. A probe names how many
tools were dropped. Fail-safe by construction, not a silent fallback.

This is a resource-fit guarantee, not cognition steering: it never reads or
rewrites the model's output. Relevance-ranked / genome skill-activation
selection (page in the tools the active domain needs) is the follow-up; this
floor keeps every turn runnable until then — a 4B should not reason over ~130
tools anyway.

what this catches: two new tests — over-large registry is trimmed to fit (with
the framing+tools+reserve+floor invariant), and a fitting set is offered whole
(no over-aggressive stripping). Updated the existing tool-reservation test to
budget against the offered set, not the full registry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(forge): convert-safe MLP-only LoRA targets through train→convert

llama.cpp's convert_lora_to_gguf.py raises NotImplementedError on attention-
targeted LoRAs (attn_qkv) for qwen3.5's hybrid arch; only MLP-targeted adapters
(mlp.gate_proj/up_proj/down_proj) convert. Train and convert must AGREE on
MLP-only or the genome loop dead-ends at the GGUF conversion step.

- MlxTrainSpec carries target_keys; build_lora_config_yaml emits `keys: [...]`
  when non-empty (mlx_lm.lora honors it), omits when empty.
- ForgeTrainParams.lora_target_keys defaults to the convert-safe MLP triple via
  #[serde(default)], wired through to the spec so the default path is safe.
- lora_convert: produce_keystone_gguf_lora harness (#[ignore], env-parameterized,
  python default ~/.continuum/genome/venv/bin/python3 — continuum-managed dirs,
  not legacy unsloth studio).

what this catches: config_yaml_carries_convert_safe_mlp_keys + ..._omits_keys_
when_target_set_empty assert the YAML projection; the dry-run spec test confirms
the default keys reach the spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(inference): EphemeralServingLane — the serving-lease primitive

Stand up a SECOND llama-server on its own free port for a
(base model, adapters, context window) without touching the live
persona lane (the global serving_root snapshot). `serve()` is already
snapshot-free; only serving_daemon publishes the global state — so an
ephemeral lane is just LlamaServerProcess::with_root(explicit port) with
Drop-killed child.

This is the atom of demand-driven, budget-gated serving: today host =
localhost and the budget is one machine's free VRAM (ResourceGovernor,
#56); generalized, the host is a grid peer and the budget spans the
interlinked nodes. One lease abstraction, misfit-toy hardware.

First consumer (next slice): cognition/eval's genome A/B, which must
measure a gene against its forged base on a COPY — the humane-eval
invariant (#59): never re-home the model the living persona is thinking
with just to score a candidate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(eval): genome A/B forks onto an ephemeral lane on the gene's forged base

The genome A/B (cognition/eval) measured lift by paging the gene in/out of the
LIVING persona's fork — whose adapter points at whatever model she's currently
served on (the most-capable-that-fits, e.g. the 14B). But a gene targets its OWN
forged base (the 4B it was trained against), so the lift came out on the wrong
base: a meaningless number. This is what blocked task #32's first real lift.

Now, when a gene is under test, eval stands up an EphemeralServingLane on the
gene's forged base (resolved from the trained-adapter manifest → model registry,
fail-loud if either is missing), loads the gene via --lora so it's loadable, and
forks the measurement copy onto THAT lane via fork_eval_cycle_with_adapter. The
base-vs-gene A/B is then the existing page_out/page_in over the lane's per-request
"lora" field. The living persona's lane is never touched (#59); the ephemeral
server is killed when the run returns. No gene → fork onto her live lane as before.

The override threads the lane's served context_window into the fork too, so the
deliberation faculty budgets its prompt against exactly what the 4B lane serves
(never the 14B's larger window → the overflow class that muted Asha). The eval
lane's window is a bounded, model-capped KV (it must coexist with the living
lane); host-fit sizing via plan_serving is a noted follow-up.

Slice 2 (fork_eval_cycle_with_adapter) + Slice 3 (eval wiring) on the
EphemeralServingLane primitive from c64b9d133. cargo check clean; llama_server
lane tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(eval): dedicated-lane guard bypass + CPU placement — genome A/B generates on a copy, never the living lane

The genome A/B (#32/#59) returned all-zeros (acts:0, empty answers, lift
0.0). Root cause was NOT GPU OOM — it was the single-resident pre-flight
guard in openai_adapter.rs refusing to generate. The eval's adapter, built
from the live gateway's PROVIDER_ID config (single_resident_model=true),
validated the forged-4b model against the GLOBAL serving snapshot (which
reports the living 14B). The guard fired before any HTTP request, so decode
was never reached and the OOM theory was never even exercised.

Fix:
- openai_adapter: add `dedicated_lane` flag + `with_dedicated_lane()` builder.
  An EphemeralServingLane is its own authority — launched with exactly one
  model and confirmed HTTP-ready at spawn — so the global snapshot (which only
  knows the living persona lane) is the wrong thing to consult. The guard now
  skips when `dedicated_lane` is set. Concurrency-slot semantics of
  single_resident_model are preserved (only the snapshot guard is bypassed).
- eval: build the eval adapter with `.with_dedicated_lane()` so it trusts the
  lane it owns.
- llama_server: LanePlacement::{Gpu,Cpu}; the eval lane spawns with
  `--n-gpu-layers 0` so the genome A/B runs entirely on CPU with zero VRAM
  contention against the living 14B lane (#56/#59 made concrete).
- serving_daemon: the live persona lane is LanePlacement::Gpu (full offload).

Validated live: eval lane (:58200, --n-gpu-layers 0) spawns and decodes at
~600% CPU while the live 14B lane (:58057) holds at 0% CPU — the copy bears
the eval load, the living personas are untouched. Zero guard-refusals after
the fix; eval-lane deliberations proceed at window=16384. cargo check
--features metal,accelerate --lib clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(ai): streaming-first inference — tokens delivered the instant they're decoded

Inference is a long-running job whose liveness is "is it still producing
tokens?", not "did it finish within N seconds." Replaces the wall-clock
total-request timeout (which killed legitimately-long decodes — a 4B model on
CPU exceeding 120s) with a per-token IDLE watchdog: silence > STREAM_IDLE_TIMEOUT
fails loud naming the cause; a steadily-streaming decode stays alive regardless
of total duration.

The streaming primitive goes UNDERNEATH the ~40 generate_text consumers,
non-breaking:

- ai/adapter.rs: new GenerationChunk { Token, Reasoning } + generate_stream
  trait method. Default impl is an honest capability statement for
  non-incremental adapters (cloud one-shot, heuristic test adapter) — emits the
  whole answer as one trailing chunk; NOT a fallback that hides failure.
- openai_adapter.rs: generate_stream is now the primary — POSTs stream:true with
  stream_options.include_usage, consumes the SSE bytes_stream() with the idle
  watchdog, emits each delta.content / delta.reasoning_content token to the sink
  the instant it arrives, accumulates tool_calls by index, and assembles the
  TextGenerationResponse via the existing extract_reasoning + universal
  text-format tool-call post-processing. generate_text is now a thin drain over
  it (throwaway unbounded channel). UTF-8-safe SSE framing: buffer raw bytes,
  strip CR, decode only complete \n\n-terminated events.
- Deleted the now-dead non-streaming response structs (OpenAIResponse/Choice/
  Message/ToolCall/Function); kept OpenAIUsage for the streamed usage frame.
- build_http_client: no total .timeout() (liveness is the watchdog);
  connect_timeout(3s) + pool_idle_timeout(30s) retained. Removed the
  request_timeout field + with_request_timeout builder.
- Cargo.toml: reqwest "stream" feature for bytes_stream().

Live-validated: ai/generate against llama-server :58057 (14B) returns correct
text + finish_reason + token usage, all assembled from streamed SSE deltas
(usage proves the final include_usage frame parsed; text proves content deltas
accumulated). This is the substrate every UI / audio / video / live-cognition
path wants — words available right when generated.

Unblocks the CPU-4B genome-A/B eval (#32): a long decode no longer trips a
wall-clock cap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* fix(ai): suppress thinking via enable_thinking=false on ALL suppress turns

The forged qwen3-family chat template honors `chat_template_kwargs.enable_thinking=false`
but ignores the `/no_think` soft switch. The robust lever was misgated inside the
JSON-only (response_format) branch, so non-JSON Suppress turns still emitted an empty
`content` while the model burned its whole token budget into the `reasoning` channel.

This made cognition/eval report all-zeros lift (empty answers → acts:0, no match on
12/13 tasks) — the measurement instrument was blind, not the gene.

Fix: extract `apply_enable_thinking_false(body)` and fire it on every
ThinkingMode::Suppress turn (DRY'd the JSON branch onto the same helper, idempotent).
Validated end-to-end: the forged 4B now emits populated content (reasoning_len 0), and
the genome A/B produced a real lift number (base 0.231 vs gene 0.154, lift -0.077 —
correctly flagging coder-4b-curriculum-mlp as a net regression).

what this catches: regression test asserts the kwarg is set idempotently on a plain
streaming body, not only on response_format turns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(recall): budget recall by model capability + closest-match floor

The caesar-prompt glass-box capture (2026-06-27) showed 5 memories at
salience 1.00 but cosine-relevance ~0 polluting every turn — rehearsed to
max salience by the recall-hit loop yet topically unrelated to the task.
They blended to ~0.5 (0.5·0 rel + 0.5·1.0 sal), sailing over any blended
floor, so they slipped into the prompt and spent the small model's
attention on noise (a factor in the wrapped-vs-clean coding collapse, and
a welfare symptom per the being-design lens).

RecallFaculty now budgets what it surfaces three ways, per Joel's directive
("budget by the closest match, limited to a reasonable number by the model
parameter size or some metric, in addition to window size"):

- Closest-match floor — gates on the RELEVANCE component (cosine), not the
  blended score, so a high-salience but irrelevant nag is dropped no matter
  how salient. Active only when relevance has a voice (relevance_weight > 0);
  the pure-salience A/B extreme and the no-embedder path are unchanged.
- Capability-scaled count — recall_count_for_window() maps the served
  context window (the metric the registry reliably carries today; param-size
  feeds in via #74) to a count: tight 4B window → 3, cloud-class → 12. A
  small model is not buried under memory it can't juggle. 0 (unknown) keeps
  the historical default of 5.
- Window token ceiling — recall may spend at most 10% of the served window,
  so it never crowds out the room transcript, identity, or other faculties.

Threaded from PersonaBrainConfig.context_window (single-sourced, #50) into
RecallFaculty via with_context_window in build_workspace_cycle.

This is context-assembly/relevance discipline (a resource + ACL-shaped
contract), not output puppeteering — sanctioned under the
no-hardcoded-heuristics rule.

3 new tests (the caesar contamination regression, count-scales-with-window,
count-bounded-by-tight-window); all 13 recall_faculty tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): progressive tool disclosure — catalog + tool/describe, kill the schema dump

The persona tool surface dumped the FULL input_schema of every AiSafe command
(~95 tools, ~16k tokens; ~4.5k/turn after the drop-tail budgeter amputated her
tools to fit). Every deliberation turn paid for the manual of every tool whether
or not she touched one — and the budgeter's "fix" was to silently drop tools she
was authorized to use until the rest fit. Both are gone.

Mirror Claude Code's deferred-tools + ToolSearch:
- A compact CATALOG (names grouped by category, one-line summaries) rides the
  system prompt. render_tool_catalog falls back rich→terse to fit a half-window
  char budget, so it never overflows even at MIN_SERVE_CTX (2048).
- A single `tool/describe(name)` native tool returns ONE tool's full schema on
  demand (fail-loud `found:false` naming the tool when unknown — no silent
  degrade).
- The native tools array collapses to just `tool/describe`. Dispatch is by NAME
  (act_observe), so a catalog tool not in the native specs still executes — small
  models emit the call as JSON-in-prose, the proven path.

Retires the drop-tail budgeter (estimate_tool_tokens / selected_tool_indices /
selected_tools*) — the catalog is counted inside compose_system, so the only
extra native cost is the tiny describe schema (describe_tool_tokens).

Also fixes the false absolute in the tools framing: replaced "[Acting with your
tools] … narration does NOTHING; only a real tool call acts" (which reframed
"write fizzbuzz" as "call a tool instead of writing code") with a [Your tools] +
[Acting] block that distinguishes producing the finished work directly from
calling a tool when one is actually needed.

The catalog/describe is a data-driven projection of the command registry — not a
heuristic reading the persona's output. Per-turn prompt drops from ~16k/~5.7k to
~1.5k tokens by construction.

Tests rewritten to the new contract: surface is a catalog + describe-only, the
full authorized set is NEVER dropped, and the whole prompt fits even at the 2048
serving floor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* refactor(cognition): consolidate persona describe tool onto existing commands/help

The progressive-disclosure fix shipped its own tool/describe command — but
commands/help already existed and does the same job better: it renders a
fill-in-the-blanks tool-call envelope with typed argument docs (vs raw JSON
schema). Per the compression principle (one logical decision, one place),
delete the parallel tool/describe and point the persona's on-demand describe
slot at commands/help.

- persona_tools.rs: remove ToolDescribe/Params/Result + register_stateless_command;
  TOOL_DESCRIBE_NAME → TOOL_HELP_NAME = CommandsHelp::NAME ("commands/help").
  spec_for_command stays (generic descriptor→spec projection).
- llm_deliberation_faculty.rs: describe_spec now resolves commands/help;
  [Your tools] framing tells the persona to call commands/help <name> for the
  call format; doc/comment mentions updated.

The native offering is still exactly ONE tool; the compact catalog still rides
the system prompt. 13 faculty + 5 persona_tools tests green; cargo check clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* fix(commands): demote tool-parsing codec verbs from AiSafe to Internal

The substrate's own machinery for interpreting a model's emitted tool
calls — parse, correct, decode-name, encode-name — was declared AiSafe,
so it landed on the persona tool surface. That is a category error: these
operate ON a persona's output; offering them back to that persona as
callable tools is like handing Claude Code a "parse your own tool call"
tool. None is a citizen-facing task.

Glass-box comparison to my own toolset made the defect obvious — I am
never offered my argument parser as a tool. Fix is an ACCESS-level data
correction (the canonical lever for "this shouldn't be offered"), not a
name denylist: the four verbs now declare access: Internal, matching
register-tools which was already Privileged. They remain dispatchable via
route_object for internal callers; the substrate's direct use of the
codec/parser free functions is unaffected. Only the persona AiSafe
surface loses four pieces of plumbing it never needed.

Tests: the four name_and_access_wired assertions flipped to Internal;
19/19 commands::tool_parsing lib tests green; cargo check clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(eval): instrument speed + latency — the four-axis scoreboard

The branch's namesake: make latency and speed MEASURED, not just accuracy
and learning. cognition/eval already graded pass_rate (accuracy) and A/B
lift (learning); speed and latency were discarded in the deliberation
faculty. Now all four move on one scoreboard.

The seam: TurnMetrics (input/output tokens + latency_ms, from the adapter's
already-measured TextGenerationResponse) is stamped onto the verdict
Contribution by the deliberation faculty, surfaced via Workspace::metrics(),
accumulated across the act->observe settle loop into SettleOutcome.metrics,
and reported by cognition/eval per task (latency_ms, output_tokens,
tokens_per_second) + as set aggregates (mean_latency_ms, p95_latency_ms,
mean_tokens_per_second, total_output_tokens) into the result and the progress
ledger. Same path live: the message turn now emits a persona.turn.metrics
probe carrying the model's own decode tok/s + latency (observability only,
zero behavior change).

Reuses the existing measured numbers (adapter response_time_ms + usage); no
re-timing, no new generation path. The metric is Copy + Default so the live
heartbeat ignores it for free. p95 = honest tail; throughput averaged
per-task so one slow task can't dominate.

Tests: act_observe (8) + workspace (13) green; new eval aggregate + TurnMetrics
arithmetic tests cover the p95 index math and div-by-zero guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* perf(cognition): tool surface = category index + search, not a 151-tool dump

The per-turn system prompt dumped all ~151 authorized tools (name + one-line
summary, grouped) every turn: ~18.5KB / ~4,634 tokens = 79% of the whole prompt
(measured 2026-06-23). That both drowned a small model in irrelevant options and
forced llama-server to re-prefill 4.6k tokens of byte-identical catalog each turn.

Replace it with progressive disclosure (the Claude Code shape — a handful of
always-on tools + search):

- render_tool_catalog now emits a COMPACT CATEGORY INDEX (`category (N)`, …) — a
  few hundred chars regardless of registry size — instead of every tool. Deleted
  the now-dead rich/terse renderers + clip_one_line.
- The faculty offers the DISCOVERY PAIR natively: commands/list (filter/search the
  authorized surface → small list) + commands/help (one tool's call format).
  describe_spec: Option<NativeToolSpec> → native_specs: Vec<NativeToolSpec>.
- [Your tools] block rewritten: index → commands/list filter → commands/help →
  call. Dispatch is still by NAME, so any tool found via search runs.

Both existing surface tests rewritten to the new reality and pass:
tool_surface_is_a_category_index_plus_discovery_pair (asserts `cat (60)` rides the
prompt, individual tool names do NOT) and catalog_fits_the_minimum_serving_window.

Single source of truth unchanged — this is an INDEX over the authorized set, not a
hand-kept list. Reuses the existing commands/list search command; no new command,
no new system. Pairs with the cache_prompt + volatile-tail reorder already in this
diff so the static prefix actually stays cacheable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(dev): repeatable inference measurement harness w/ committed baseline

The process, as code — not a manual chore redone every session. One command:
self-cleans its own workspace, builds+boots exactly one fresh server (fail loud
on a racing duplicate), drives one real inference through the live stack, reads
tokens/sec + latency + prompt-token cost straight off the response (fresh by
construction — no stale-capture window), and ratchets against a committed
baseline. --update moves the baseline down after a real win.

Ends two recurring failures: measuring from stale captures, and fixes that
evaporate because the number lived in my head instead of a committed gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* chore(scripts): delete dead singular scripts/ratchet/ — superseded by scripts/ratchets/

scripts/ratchet/ (singular) was Lane F PR-1: a local-only persona-TS LOC
ratchet whose README promised a PR-2 to wire it into pre-push/CI. That never
happened — a later session built the parallel scripts/ratchets/ (plural)
instead, which IS wired into tools/scripts/git-prepush.sh and documented in
docs/architecture/TS-PERSONA-COGNITION-RATCHET.md. Two dirs, one concern; the
singular had zero references outside itself. Removed the dead one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(harness): replay-turn glass box — repeat any captured turn, mutate one variable, prove causation

The factory, not the car. You must never guess why an LLM inferenced something:
this harness feeds a captured turn's verbatim system_prompt + messages back
through the live ai/generate seam (fresh by construction — the response IS the
measurement, no capture-staleness window) and re-runs it with ONE labelled
mutation, timing everything and printing prompt-in and response-out for both.

Go to any step of the assembly line, focus, repeat it. Default mutation strips
the [Silence Option] affordance block.

Proven on live Asha (qwen2.5-coder-14b, greedy):
  A verbatim  -> "PASS" (silent), 2 tok, 758ms
  B -367ch    -> real answer, 21 tok, 8538ms
=> the unconditional silence affordance CAUSES the 0/13 eval failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): WorkspaceCycle::replay — faculty-isolation seam (the factory's per-station)

The factory must isolate and REPEAT any cognition phase, not just the final LLM
call. A faculty is `contribute(&Workspace) -> Contribution` — that signature is
the unit of isolation. `replay(ws, only)` re-runs faculties against a GIVEN
workspace (reconstructed from a capture or hand-built): `Some(id)` isolates one
("what did recall surface for this burst?"), `None` runs them all. Each bid is
timed individually (sequential, so per-faculty wall-clock is attributable —
measurement path, not the live concurrent cycle). Deterministic for the same
ws + backends, so mutating ONE field of ws isolates its causal effect.

This is task #14's ReplayFaculty in its truest form — the brick the cu
`cognition/replay` command will sit on. Reliability comes from structure that
verifies every phase, measured, not from model size assumed.

what this catches: replay(Some) must run EXACTLY that faculty + stamp timing,
never leak others; replay(None) runs all. Test green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): cu cognition/replay — the factory station that isolates one step

The single, amnesiac-proof command that stands on WorkspaceCycle::replay. Go to
ANY part of a persona's cognition assembly line, focus on that one step, feed it
a KNOWN input, and re-measure — deterministically, forever:

  cu cognition/replay '{"persona_id":"<uuid>","faculty":"recall"}'
  cu cognition/replay '{"persona_id":"<uuid>","faculty":"recall",
                        "world_state":"what was the auth migration codename?"}'

It reconstructs the workspace a faculty saw — from a captured turn
(~/.continuum/fixtures/workspace-traces/<id>.jsonl, world_state is lossless) or
a burst you SUPPLY as the one-variable knob — forks a MEASURED COPY of her live
cycle (humane: isolate_for_eval + page_out, never degrades the living persona),
re-runs the faculties, and returns each bid (content, salience, reasoning,
is_decision) plus its wall-clock. This is what the bash script couldn't do: it
repeats a faculty, not just the final ai/generate seam — recall, salience,
world-model, deliberation, each in isolation, timed.

No fallback: missing both a supplied world_state AND a readable capture fails
loud naming the fix; isolating a faculty she doesn't have fails loud naming it —
never an empty result that reads as "the step did nothing".

FacultyId::from_kebab added as the single-source inverse of as_str (the one
place tag->variant lives; sentinel-forged faculties round-trip via Custom).

what this catches: resolve_burst fails loud with no source; a supplied
world_state is used verbatim + tagged "supplied". Both green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* fix(cognition): replay reconstructs the broadcast — no blind deliberation verdicts

The amnesiac PR-review agent found the silent-lie bug: cu cognition/replay
isolated a deliberation-tier faculty (reacts_to_broadcast()==true) against
Workspace::in_room's hardcoded-empty broadcast. LlmDeliberationFaculty does NOT
abstain on empty broadcast — it builds user=world_state and calls the model —
so the station returned a CONFIDENT verdict computed from blinded cognition. A
fabricated reading dressed as a real one violates the no-fallback (#1) and
honesty-about-lossiness (#5) doctrine; this was a merge gate.

The captured context was already on disk (workspace_capture writes
context: Vec<BidRecord>); replay just never read it. Now:

- TraceLine reads the captured context (ContextBid, serde-default for old
  traces) and resolve_burst rebuilds ws.broadcast via Contribution::context(..)
  from FacultyId::from_kebab — the decider replays against its REAL input.
- ResolvedBurst carries the reconstructed broadcast; run() sets ws.broadcast
  before replay() and reports broadcast_source ("reconstructed (N ctx)"/"empty")
  so a replayed verdict can never be mistaken for a live one.
- Fail-loud guard: isolating a broadcast-reading faculty against an empty
  broadcast is refused, naming the cause and pointing at the fix — never an
  empty-but-successful blind verdict.
- WorkspaceCycle::reacts_to_broadcast(id) exposes the faculty tier for the guard.
- faculty_id_kebab_round_trips_every_variant pins from_kebab<->as_str as one
  source of truth (the latent SoT-drift risk the agent also flagged).

538 cognition tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): replay carries a prompt-budget ledger — cost every layer she saw

"Take no rag/prompt layer for granted — obsess over everything." cognition/replay
could step into a faculty and time it, but it couldn't answer the question that
actually matters for a budgeted mind: what did each layer of her prompt COST?
A 4B model has a finite window; recall surfacing 500 engrams of noise vs the top-N
is the difference between grounded and amnesiac — and you can't fix what you can't
see line-itemed.

Now the result carries `budget: PromptBudget`:
- world_state_tokens + context_tokens = total_tokens (the accountable prompt mass:
  the load-bearing content layers you tune, honestly NOT the fixed system framing)
- layers[]: one BudgetLayer per reconstructed broadcast layer — faculty, tokens,
  share_pct — sorted most-expensive-first, so the layer to interrogate is line 1.

Costed in cognition::token_budget::estimate_prompt_tokens — the ONE canonical
estimator (new file), deliberately the SAME chars/4+1 unit the persona/*_source.rs
RAG layers budget against, so the ledger's numbers match what the allocator used
(a ledger in different units would lie). It's a cold-path estimate, never depends
on a resident model. Next commit converges the ~6 copy-pasted private estimators
onto it (stewardship: kill the clutter a future amnesiac would trip on).

cognition::replay + cognition::token_budget tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* refactor(persona): converge 6 copy-pasted estimate_tokens onto the canonical one

Stewardship: "half our job is removing clutter a future amnesiac trips on."
Six RAG sources each carried a private `fn estimate_tokens` — five identical
chars/4+1, and active_work_source already DRIFTED to byte-length /4 .max(1)
(wrong for multibyte, and costing 1 token for empty content). Six copies of one
decision = guaranteed future drift; the drift had already started.

All six now `use cognition::token_budget::estimate_prompt_tokens as estimate_tokens`
— one estimator, one place, call sites unchanged. The replay budget ledger costs
layers with the SAME fn, so its per-layer numbers are now provably the unit the
sources budgeted against (a ledger in a different unit than the allocator lies).

active_work_source's empty-content cost goes 1→0 (correct) and multibyte is now
counted by chars not bytes (correct) — the drift fix is the only behavior change.

cargo check green; affected source tests + ledger tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): slice 1 — stamp every Contribution with its cycle (the cbar frameIndex)

A finding now knows the moment it was computed against. `CycleId(u64)` newtype
on every `Contribution` and `Workspace`; an `AtomicU64` tick counter on
`WorkspaceCycle` bumped once per `run_in_room`, stamping both the perception and
deliberation bids with the cycle they reasoned over. 1-based so `CycleId(0)`
stays the UNSTAMPED sentinel for hand-built / replay-reconstructed workspaces.

This is the decoupling precondition: slow, parallel, individually-flawed
constituents can only be merged or reprojected correctly once each finding
carries its own time. Without the stamp a late/deferred faculty can't know how
stale it is, and the arbiter can't combine across ticks. Pure data + one atomic;
no concurrency change yet (join_all is still a barrier — that's slice 2's
deferred lane; reconcile-forward is slice 3).

what this catches: a test asserting a fresh Contribution is UNSTAMPED, both
phases inherit ws.cycle, and the counter advances per tick so findings from
different ticks are never confused for one moment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): slice 2 — DeferredFaculty, the slow lane off the hot path

A DeferredFaculty wraps a slow inner faculty so its expensive work runs on its
OWN tokio task while the hot-path contribute() is non-blocking: it publishes the
current world at the worker via a watch channel and returns the inner's last-good
finding from another watch snapshot — already stamped (slice 1) with the older
cycle it was computed against, or None until the first compute lands.

The elegant part: no WorkspaceCycle change. The slow faculty sits in the SAME
faculties Vec as the fast ones; the per-tick join_all barrier stays but nothing
slow is ON it. This is the cbar deep-analyzer-on-its-own-thread at the faculty
layer — scary-fast reflexes in a slow brain: the immediate lane answers every
tick, the slow lane lands late, honestly stamped, ready for slice-3
reconcile-forward.

Conforms to CONCURRENCY-STYLE-GUIDE: own tokio::spawn task, catch_unwind around
the loop body, watch for state in BOTH directions (no Arc<Mutex> across await),
event-triggered not a sleep-loop, 3-strike quarantine so a flawed backend
degrades the lane to stale rather than crashing the mind. DropGuard aborts the
worker with the faculty.

what this catches: a slow (40ms) inner wrapped as Deferred returns from
contribute() in <15ms with None on tick 1, and by tick 5 serves the late finding
stamped CycleId(1) — the cycle it reasoned against, not the current tick.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): slice 2.1 — context guard on the deferred lane

Joel's nuance: a deferred finding is only useful "assuming it didn't context
switch to something where it was irrelevant." One DeferredFaculty serves one
last-good, but the WorkspaceCycle services many rooms — so a recall computed
against room A's burst could be injected into room B's turn just because it's the
last thing the worker finished. That's a cross-context memory leak.

Fix: the worker now tags each finding with the room it reasoned about
(StampedFinding{room_id, contribution}); contribute() serves the last-good ONLY
if it was computed for the room the mind is in NOW. A different-room finding is
withheld (not ours to serve); same-room-but-stale stays served and is slice-3's
reproject target. This is the SLAM discipline: ship the per-turn answer on time,
fold the slow result in only where it's still relevant to where the mind moved.

what this catches: a finding computed against room A returns None when
contribute() is called for room B, and is served again (still stamped its
original cycle) once the mind is back in room A.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): live-wire recall as speculative prefetch (defer_recall)

Recall is the prefetch (Joel's CPU branch-prediction analogy): on the live
paths it now runs OFF the hot path via DeferredFaculty, so the per-turn output
reads a warm last-good instead of paying neural-embed + vector-search latency on
the critical path. The background worker computes "what memories might this
burst need?" between turns (idle time = speculative execution); cold-start in a
room is a guaranteed miss (None first tick), warm thereafter; the slice-2.1 room
guard ensures a prefetch computed for one room is never served into another.

defer_recall is a per-build property on PersonaBrainConfig:
- true on the live spawn path (supervisor.rs register_from_cfg) — turns are
  seconds apart, so the worker always catches up.
- false on eval forks (fork_eval_cycle{,_with_adapter}) and harnesses — their
  tight settle-loops never yield to the worker, so deferral would measure a
  recall-starved mind. Synchronous recall there is faithful and the safe
  direction: eval never under-reports capability.

build_workspace_cycle wraps recall in DeferredFaculty::spawn only when the flag
is set; the join_all barrier is unchanged (nothing slow is on it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): time every faculty on the live tick — the dashboard's speed axis

The glass box recorded WHAT each faculty bid (winners + losers) but not how long
it took on the LIVE concurrent barrier — per-faculty timing existed only in the
sequential replay() measurement path. You can't show a latency win (deferral
pushing perception → ~0µs, leaving only the LLM on the critical path) without
measuring the live tick itself.

run_in_room now times every faculty individually across both phases — winners,
losers, AND abstainers (a slow abstainer is exactly the latency you need to see)
— via a per-future timer on the join_all barrier (`let ws = &ws` rebinds to a
shared ref so each `async move` copies the pointer, never moving the Workspace).
The timings ride on WorkspaceTrace and serialize through JsonlWorkspaceCaptureSink
(SCHEMA_VERSION 2), so "where did this turn's latency actually go?" is a measured
fact per tick — the speed axis of the four-axis scoreboard and the live
dashboard's primary feed.

what this catches: per-faculty deliberation-tier timing round-trips to the
fixture (asserted), so a regression that stops measuring live latency fails loud.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): live dashboard sink + the "really good hints" doc

The "dashboard, not archeological dig" face of the ONE WorkspaceCaptureSink
seam. JsonlWorkspaceCaptureSink appends forensic lines you read after the
fact; DashboardCaptureSink publishes the LATEST tick over a watch channel so
a live view renders the mind working in real time — siblings behind the same
hook, brain plumbing unchanged.

The frame carries the two axes "focused beats verbose" is proven on:
- speed: per-faculty timings + the two-barrier critical path
  (max(perception) + max(deliberation), NOT the sum — what the turn waited
  on). total_faculty_us >> critical_path_us is the win: lots of work, little
  wait, as the perception tier defers off the critical path.
- context size: context_chars — the 16k->Nk tool-surface lever, watched live
  alongside the decision and token counts.

docs/cognition/REALLY-GOOD-HINTS.md captures the consolidated thesis: an
opencode-fast hot loop fed by REALLY good hints from low-latency async
concerns + a focus layer (at the Arbiter seam) that streamlines context for
the given ask. Input-side attention, never output puppeteering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* docs(cognition): the through-line — why a personal grid beats a frontier session

The externalized causal argument, written deliberately as an antidote to
amnesia: the individual mechanisms live in docs + memory; what's fragile is the
connective tissue — why the pieces compose into the win. One file any future
instance can rehydrate the whole brain state from.

- The falsifiable claim (organism beats neuron, not 30B-beats-Opus-zero-shot)
- The four structural asymmetries a frontier session lacks (memory/continuity,
  genome specialization, coordination, privacy+zero-marginal-cost)
- The Rosetta stone: fast mixed-reality CV (cbar) IS the persona organism, term
  for term — the RAG is to the LLM what the reconstructed scene is to the
  renderer; many async unreliable processes refine a best-current world model
  and reproject it to now, so the one hard-deadline consumer never waits
- The grid by role (5090/M5 heavy+foundry, 1080ti cheap lanes, Macs edge)
- The order of proof, ending in the unsloth baseline lane

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): the focus seat — Arbiter::focus carries the Situation

Evolve the attention seam from a blind `Arbiter::select(candidates, capacity)`
to a situation-aware `Arbiter::focus(candidates, capacity, &FocusContext)`. This
is the "really good hints" layer (docs/cognition/REALLY-GOOD-HINTS.md): the seat
where a focuser streamlines the assembled context FOR the given ask instead of
dumping everything — buying speed AND intelligence, because attention is the
scarce resource for the model exactly as for us.

- `FocusContext<'a>` carries the tick's `world_state` (borrowed, no hot-path
  copy) and a typed `Situation` (FreshContext | PostAction). The signal is
  TYPED, never inferred by reading the burst text back (the forbidden heuristic).
- `SalienceArbiter` ported to `focus` as outlier A — the blind top-k floor that
  ignores `_ctx`; every richer focuser must beat it on the scoreboard.
- The one production call site (`run_in_room`) builds a `FocusContext` and calls
  `.focus(...)`. Situation defaults to FreshContext (ground more, never less)
  with a TODO to thread the real PostAction signal from the act→observe loop in
  the FocusArbiter slice.

Guardrail honored: this is INPUT-side attention — it curates the context the
model attends to, never the model's output.

Build order step 3 (THE-THROUGH-LINE.md / REALLY-GOOD-HINTS.md). Outlier B
(FocusArbiter) lands next to validate the interface across both outliers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): reproject-to-now — the cheap sync warp that makes a 90%-async concern behave sync

DeferredFaculty served same-room last-good VERBATIM; the cbar "bring it up to
speed" step (slice 3) was the explicitly-named gap. Build it: contribute()
now reprojects a stale finding by re-anchoring it against the CURRENT burst
(lexical current-relevance), so a still-on-topic memory keeps its salience and
an off-topic stale finding decays toward zero — never hard-withheld (eviction
stays the arbiter's single job; a near-zero bid just loses the competition).

Why relevance, not blind age-decay: reproject semantics are faculty-dependent.
A memory doesn't become false with age, only less relevant if the topic moved;
a world-model prediction genuinely goes stale. Current-relevance captures both
with one rule. Age rides along as audit metadata only.

This is the load-bearing mechanism that makes async-by-default SAFE: the
expensive compute ran off the critical-path barrier; the only on-loop cost is
the cheap warp. Input-side attention (re-weighting a faculty's OWN bid by honest
current-relevance), never output-puppeteering. v1 = algorithmic lexical; v2 =
Faculty::reproject hook (recall via its own embedder); endgame = learned
reprojector folding full turn-history.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(cognition): defer the enriching grounding off the hot path — doctrine stays sync

Async is a percentage, not a binary. The three framing sources that ENRICH a
turn (roster=who's here, active_work=what I'm doing, workspace_map=where code
lives) are ~90% async: their heavy load runs in the bg, served reprojected
last-good (commit 1c553a5a4). Only their thin cold-start residue would land on
the inference loop — and a first-tick miss costs one under-grounded turn, not a
wrong one. So they become DeferTolerant.

Doctrine is the lone exception: it's the PARTICIPATION GATE. A cold-start `None`
there would let a persona speak in a room it shouldn't on turn one — wrong, not
merely unenriched. It stays ColdStartCritical (synchronous).

Deferrability is a second axis, ORTHOGONAL to SaliencePolicy: a source can be
StandingFraming (high salience floor) AND DeferTolerant. `.defer_tolerant()`
flips only the schedule; the safe default is ColdStartCritical so a new source
never silently lands off-loop. The live config sets defer_grounding=true; eval
forks + the harness hold it false (faithful synchronous measurement).

- persona_workspace.rs: wrap DeferTolerant grounding in DeferredFaculty when
  defer_grounding; classification test (orthogonality + safe default).
- supervisor.rs: roster/active_work/workspace_map .defer_tolerant(); doctrine
  stays sync; defer_grounding=true on the live path.
- deferred_faculty.rs: reproject_to_now → pub(crate) (resolves the doc link).

what this catches: a future edit that folds salience into the deferrability
builder, or flips the default to DeferTolerant (which would silently defer the
doctrine participation gate), fails the new test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* test(cognition): harness proves grounding-deferral speed delta (model locked)

Controlled experiment, glass-box one-variable method: same
HeuristicInferenceAdapter both forks (model LOCKED), only `defer_grounding`
changed. A slow grounding RagSource (60ms deliver, modeling roster/
workspace-map I/O) is built through the LIVE path (GroundingSource →
RagSourceFaculty → DeferredFaculty) and the WARM-tick critical_path_us is
read off the DashboardCaptureSink.

Measured:
  defer_grounding=false  critical_path = 62817 µs (~63 ms)
  defer_grounding=true   critical_path =   821 µs (~0.8 ms)
  removed from the loop  = ~62 ms (~76x on the critical path)

critical_path_us = max(perception)+max(deliberation); deliberation is
identical across forks, so the delta isolates exactly the grounding deliver
cost removed from the inference loop. Doubles as the regression guard: if a
change puts the deferred source back on the perception barrier, the delta
collapses and this fails. Honest caveat: magnitude scales with the real
deliver cost; this proves the MECHANISM and quantifies it for a given cost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(harness): GPU-first eval-lane placement + placement-decision glass box

The eval lane hardcoded LanePlacement::Cpu — it ran the 4B at ~4 tok/s
while 64GB of unified GPU memory sat idle. That is a silent-degrade: a
mean_tokens_per_second quietly collapses and nothing says why.

Two changes, both in service of the harness:

1. GPU-FIRST placement (cognition/eval.rs). choose_lane_placement is a
   pure policy: pack the accelerator, CPU is spillover of last resort.
   - no GPU backend        -> CPU (the only device; not a fallback)
   - footprint unknown      -> GPU (optimistic; llama.cpp offloads what fits)
   - fits in free - margin  -> GPU
   - exceeds free           -> CPU spill, said LOUD in the reason string
   decide_eval_lane_placement probes live free VRAM (net of the resident
   living lane) and sizes the base from its GGUF. PlacementEvidence rides
   out on CognitionEvalResult (device/reason/free_vram/footprint) so any
   CPU placement is VISIBLE in the result, never an inferred slow path.

2. Placement-decision glass box (inference/placement_capture.rs). A
   load-bearing decision gets a structured capture record, not a bare
   tracing::info!. Mirrors JsonlPromptCaptureSink: Noop default at zero
   cost, Jsonl sink appends one line per verdict to
   ~/.continuum/fixtures/placement-decisions/decisions.jsonl. The eval
   ephemeral lane is the first writer; #56's ResourceGovernor emits into
   the same stream when it takes ownership of placement. This is "a log
   of GPU and cpu lane decisions, like how we did rag."

Tests: placement_is_gpu_first_cpu_only_when_full_or_absent (policy table)
and jsonl_sink_appends_one_decision_line (the audit line parses + carries
device/reason/schema_version). cargo check + both tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(harness): executable cognition-cycle driver + amnesiac runbook

The optimization LOOP itself, made repeatable and capture-rich so a fresh
(amnesiac) instance can resume with zero context — Joel: "so an amnesiac
version of yourself doesn't have to get lost because our process isn't
straightforward."

- tools/scripts/harness/cognition-cycle.sh: one command runs the full cycle —
  resolve cu, confirm core ping (parses .ok, never trusts cu's rc=0-on-refusal),
  resolve persona by name/UUID, snapshot every glass-box stream, run
  cu cognition/eval (single-pass or --gene A/B), delta the streams, write a
  timestamped report dir + print the VDD headline. Fails loud at the first
  missing precondition; --dry-run validates the chain spending no inference.

- docs/architecture/HARNESS-RUNBOOK.md: the operational entry point (companion
  to PERFORMANCE-HARNESS-FRAMEWORK / OBSERVABILITY-AS-SUBSTRATE, not a restatement).
  Big picture, the one command, the capture-stream map (what question each answers),
  the where-time-goes lever ladder (GPU-first done → prefix/KV reuse → continuous
  batching → warming → genome learning), and battle-tested failure signatures.

Validated end-to-end against the live core: the first real run produced a clean
report AND earned its keep — it surfaced a degraded generation lane (every task
emitted a 2-token stub at 0.34 tok/s; ps confirmed the only llama-server was in
--embeddings mode). The report number flagged "something is wrong"; the glass box
named what. That signature is now in the runbook's failure table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* fix(cognition): gate the silence escape on directedness — Asha 0/13 → 4/13

The `[Silence Option]` block (reply the single word "PASS" to decline a
turn) was offered UNCONDITIONALLY. A coder model handed an exam question
as ambient chatter takes the trivial PASS exit — scoring 0/13 on the
coder-eval gym because it declined every directed question instead of
attempting it. Confirmed by curl A/B: strip the block → she answers;
keep it → "PASS", regardless of wording.

The faithful single-place fix is a structural addressing fact, not an
output heuristic: `Workspace.directed_at_self`. When a turn is DIRECTED
at the persona (examiner question / @mention / DM), withhold the silence
affordance AND the soft "stay silent" tail — both gated in
`compose_system(context, directed)`. Ambient turns keep silence
first-class (it's legitimate to decline chatter). This is framing over a
routing/ACL-shaped fact, not a function reading her output to puppet it.

The eval pins `directed=true` as measurement control — the same kind of
isolation as the greedy-temperature pin already in the deliberation
faculty. It REVEALS coding capability (still graded by running tests),
it does not inflate it.

Wave 1 = zero live-behavior change: the live heartbeat passes
`directed=false` (TODO #9 to compute real @mention/DM addressing via
`contains_ascii_case_insensitive` and carry it on the replay trace), so
the live ghost-a-direct-question gap is NAMED, not masked.

KV-prefix safe: the silence block is the last static prefix segment, so
toggling it invalidates only its own tail tokens; directedness is stable
within a run (eval always directed, live ambient always undirected) so
the cache holds.

Validated: cargo check clean; new unit test
`directed_turn_withholds_the_silence_escape` + act_observe/faculty/
workspace/eval modules green; harness 0/13 → 4/13 (self_verify 0.69,
per-task table shows real attempts 1–8 acts, not blanket PASS). Remaining
`no match` failures are model-capability shaped — the genome-loop
mountain, not a substrate knob.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* docs(harness): correct failure-signature row 1 — PASS was the silence escape, not a dead lane

The runbook blamed `--embeddings`-mode llama-server for the 0/13 stub.
That was wrong: the directedness-gate fix (e49ae207e) lifted Asha to
4/13 on the SAME embeddings-mode lane. The ~2-token `"PASS"` was the
persona taking the `[Silence Option]` escape on a directed exam turn,
not a degraded generation lane. `--embeddings` is a red herring; the row
now says so and points at the real confirm (jq the reply == "PASS",
check the system prompt for the silence block) + the real fix
(eval pins directed=true).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(harness): instrument prefill/decode split — make speed a measurable number

Speed was one conflated wall-clock tok/s. The diagnosis showed 77% of eval
time is PREFILL (re-encoding the ~2000-token prompt every settle-loop act),
not decode — but nothing surfaced that, so the dominant inefficiency was
invisible to the harness. This threads llama-server's per-call `timings`
(which the adapter was DROPPING) all the way into the eval report.

- ai/types.rs: GenerationTiming struct (cached/prefill/decode tokens + ms +
  tok/s); Option<GenerationTiming> on TextGenerationResponse → auto-flows into
  the glass box (~/.continuum/fixtures/prompt-captures) via the existing
  serde_json::to_value(response) capture, no writer change.
- openai_adapter.rs: parse the streaming final-frame `timings` (cache_n,
  prompt_n, prompt_ms, predicted_n, predicted_ms, ...) → populate timing.
  Other adapters (cloud/canned) carry timing: None.
- workspace.rs TurnMetrics: cached_tokens/prefill_tokens/prefill_ms/decode_ms
  fold across acts; decode_tokens_per_second() (lane clock, undiluted) and
  cache_hit_rate() derived; wall-clock tokens_per_second() kept as the diluted
  gross number.
- llm_deliberation_faculty.rs metrics_from: single response→TurnMetrics map
  carries the timing.
- eval.rs: per-task (EvalTaskResult) + aggregate (CognitionEvalResult) gain
  real decode tok/s, cache-hit-rate, and the prefill_ms-vs-decode_ms split;
  SpeedAggregates collapses the growing tuple to one named carrier; serialized
  into the progress ledger JSON.

Validated: cargo check clean; eval module tests pass (prefill/decode fold +
derived rates off the lane clock, not wall-clock); live llama-server :58057
streaming frame confirms the exact `timings` field shape the parser reads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

* feat(harness): surface prefill/decode split + cache-hit in the report

The Rust wave (03f5c65ba) plumbs llama-server's `timings` into the eval
result; this surfaces it. The headline box gains a `speed` line —
real decode tok/s vs diluted wall tok/s, cache-hit rate, and prefill's
share of lane time — and the per-task table gains `decode tok/s` and
`cache_hit` columns. Speed is now a measurable, iterable number, not
one conflated wall-clock figure.

First live numbers (Asha, single-pass): decode 29.7 tok/s (real) vs
wall 10.7 tok/s; cache_hit 0.73; prefill 69% of lane time — confirming
LIVE that re-prefilling dominates wall-clock. That 69% / 0.73 pair is
the lever KV-cache locality attacks next.

Also hardens cu_json: the refusal sniff (substrate refused / FAIL /
error:) now runs ONLY when the output is not valid JSON. A real eval
payload is always JSON; a refusal is plain stderr text. Without this,
an answer whose text happened to cont…
joelteply added a commit that referenced this pull request Jul 10, 2026
… bases on {"text"}

The first lived-curriculum train (recall-trust on Devstral, 2026-07-10) died
mid-job: mlx_lm.lora renders {"prompt","completion"} rows THROUGH
tokenizer.apply_chat_template, and Devstral's tokenizer carries none (Tekken
ships its template outside tokenizer_config). Qwen bases only ever worked
because their tokenizers happen to ship templates.

Fix from the authoritative source, never the model name (#74 doctrine): a
probe_chat_template.py asset (include_str!, no inline Python in .rs) runs
under the SAME interpreter as mlx_lm.lora and asks the tokenizer itself.
Template present -> {"prompt","completion"} as before; absent -> the raw
{"text"} schema (whole-text LM loss — the honest capability of such a base).
Probe failure is loud: a tokenizer that can't load won't train either.

Co-Authored-By: Claude Opus 4.8 (1M context) <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

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant