build(deps): Bump @anthropic-ai/sdk from 0.52.0 to 0.71.0 - #205
Closed
dependabot[bot] wants to merge 1 commit into
Closed
build(deps): Bump @anthropic-ai/sdk from 0.52.0 to 0.71.0#205dependabot[bot] wants to merge 1 commit into
dependabot[bot] wants to merge 1 commit into
Conversation
Bumps [@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript) from 0.52.0 to 0.71.0. - [Release notes](https://github.com/anthropics/anthropic-sdk-typescript/releases) - [Changelog](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md) - [Commits](anthropics/anthropic-sdk-typescript@sdk-v0.52.0...sdk-v0.71.0) --- updated-dependencies: - dependency-name: "@anthropic-ai/sdk" dependency-version: 0.71.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
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 If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
dependabot
Bot
deleted the
dependabot/npm_and_yarn/anthropic-ai/sdk-0.71.0
branch
November 25, 2025 14:49
This was referenced Jun 7, 2026
joelteply
added a commit
that referenced
this pull request
Jun 7, 2026
…#205) (#1544) The three `modules::sentinel::checkpoint::tests` were writing JSON under the developer's real `~/.continuum/sentinel/checkpoints/` — visible as residual `test-list-*.json` files surviving across test runs on this machine (Jun 3, Jun 5 timestamps). Two real failure modes from this pollution: 1. On dev machines whose `~/.continuum/sentinel/` is root-owned (left over from any docker compose run that bind-mounted `$HOME` and chmod'd dirs under root), the writes fail → prepush flake. 2. Cross-test interference: `test_list_checkpoints` asserts the freshly-written handle appears, but pre-existing `test-*.json` files from other test invocations leak into that listing and could mask real regressions. Fix is two-part and minimal: - `checkpoints_dir()` reads `CONTINUUM_CHECKPOINT_DIR` first; falls back to the production `~/.continuum/sentinel/checkpoints/` path unchanged when the env var is unset. Production code path is unaffected. - `tests::ensure_checkpoint_dir_isolated()` installs a per-process `tempfile::TempDir` into the env var (held alive by `OnceLock` so Drop fires at process exit). Every test calls it at entry. The TempDir's path is `set_var`'d per test rather than once, because cargo runs tests in parallel and another test in the same binary could legitimately clear the env var (e.g. a future test that validates the fallback path). Carried forward from PR #971 (closed as too stale to rebase against current canary). Reapplied fresh against current module path (`src/workers/continuum-core/src/modules/sentinel/checkpoint.rs`). Tests: 3/3 `modules::sentinel::checkpoint::tests` pass, and confirmed post-fix that no new files appear under `~/.continuum/sentinel/`. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
joelteply
added a commit
that referenced
this pull request
Jul 20, 2026
…ndows (#205) The eval already streams eval:progress per graded task, but the BEFORE/AROUND- generation windows — workspace-template wait, ephemeral-lane cold-load — emitted nothing, so `progress: null` during a legit 5-min 14B cold-load looked identical to a hang (it just cost me exactly that confusion). No positronic layer could show "loading <model>… 45s" vs "stuck". emit_eval_phase(phase, detail) publishes an eval:phase bus event (+ a probe) at those seams: "preparing" (per-second during the template-wait retry) and "loading_lane" (before an ephemeral base/gene lane cold-load). Fire-and-forget, Noop when the bus is unwired — observability at zero hot-path cost. Positron (and any layer) subscribes to eval:phase to render live feedback across the whole eval lifecycle, not just the grading tail. [[observability-as-substrate]] Substrate side only; the positron widget that consumes eval:phase + eval:progress is the continuation (#141 / #184). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 20, 2026
…o more silent glacial wedge (#205) READY_TIMEOUT (90s) only bounds the /health POLL, and wait_ready checks its deadline BETWEEN attempts — so a hang INSIDE an attempt (a stalled decode_smoke_ok, a wedged model-mmap, a process-launch that never returns) waits past it forever. Glass-boxed this session: an ephemeral eval lane hung 11 min with NO process and free VRAM, no timeout ever firing — a silent glacial wedge that violates fail-loud. EphemeralServingLane::spawn now wraps the whole proc.serve() in tokio::time::timeout(DEFAULT_SERVING_WAIT) (READY_TIMEOUT + 30s = the daemon's own load budget). On elapse it returns LlamaServerError::NotReady and drops proc, whose Drop kills any child it launched. The eval then fails LOUD to a failed ledger row instead of hanging. Eval lanes only — the live lane keeps its fail-loud-not-fast bring-up policy untouched. Deeper source fix (decode_smoke_ok / launch each want their own timeout so the deadline loop can't be starved) noted on #205; this net guarantees the outcome regardless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 20, 2026
…TEMPTS it instead of Passing (#205 real root cause) Glass-boxed via --capture_dir: hard-rs/coder-eval scored 0 not from any serving wedge (clean Qwen-14B generated 400 tok on the exact prompt+stops) but because her deliberation settled on Decision::Pass — SILENCE — with the task fully in view, twice. The shared [Conversational Presence] block answers "a task → answer it now" only when the model RECOGNIZES the message as asking something OF it; a terse gym imperative ("Implement `fn eval`…") delivered as `peer: <prompt>` reads to a coder model as ambient text about a task, so it takes the pleasantry-rest PASS hatch and scores 0 WITHOUT ATTEMPTING. humaneval — whose prompt already says "Give your final answer as the complete function…" — engaged and scored 10/10 (validated vs the known 80% baseline). `directed=true` alone stopped withholding the hatch once the directed block was softened for live spiral-breaking. Fix: the examiner poses the ask itself — the eval delivers the task with a directed preamble ("This is a task for you to complete now. Provide your complete solution:") ahead of the verbatim prompt. This standardizes at ONE seam the imperative framing the stronger gym prompts already carry inline, so the exam measures CAPABILITY, not whether a terse line happened to read as directed. Lift stays valid (both A/B arms see identical framing); absolute numbers re-baseline. Chased three wrong theories first (peg-native template, per-spawn serving wedge, decode-smoke) — one --capture_dir run reading her actual DECISION cut through all of it. Lesson: for "she scored 0", read what she decided before theorizing about the layers beneath. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 20, 2026
…il, not the system prefix (kill the per-act re-prefill) The eval-lane crawl was re-prefill, NOT off-GPU (Metal live, decode 18 tok/sec): every settle-act re-prefilled ~4000 tokens (~30s). Root cause was POSITION. The working-memory faculty bids its ledger as a context Contribution, which the deliberation faculty assembles into the SYSTEM message's volatile tail. The system message precedes the whole conversation, so appending one [action #n] line each act shifted every conversation token after it — the KV prefix broke at the working-memory block and everything downstream re-prefilled. The faculty already formats append-only (working_memory.rs) explicitly to be cache-friendly, but that optimization was defeated by sitting mid-stream: append-only only pays off when the growing block is genuinely LAST in the token stream. Fix: a Contribution can now be marked .trailing() — proprioception that grows each act renders nearest generation (a trailing conversation turn), never in the system message. The working-memory faculty marks its contribution trailing; the deliberation faculty excludes trailing contributions from render_assembled_context_within (the system block) and emits them as the newest user content in messages_within, after the conversation and the perception facts. Standing framing and byte-stable grounding (roster, doctrine, map, recall) stay in the system prefix. Now a settle-act appends only to the very end, so the system prefix AND the prior conversation keep their KV cache and only the small new tail re-prefills — ~30s/act to near-zero. Scope: POSITION only. The working-memory faculty's content (its reasoning trail + the [action #n] receipts + full most-recent result) is unchanged — it remains the mind's proprioception-of-its-own-hands channel, so the act_observe organism tests pass untouched. The pre-existing receipt overlap between this faculty and the StepsLedger perception fact is orthogonal and left for a follow-up (they were already both in the prompt, one in system + one trailing; now both trailing). Tests: trailing_proprioception_renders_in_the_tail_not_the_system_prefix (growing WM does NOT mutate the system prefix — the invariant that IS the fix) plus working_memory_contribution_is_marked_trailing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 28, 2026
…a masked 240s /health timeout (#205 unmask) Glass-boxed 2026-07-27 running the live coding measurement (agent/solve): every `agent/solve` on this Mac failed with a bare "llama-server not ready after 240s (/health request failed)". The real cause was thrown away — `wait_ready` polled the health port for the WHOLE budget without ever checking whether the child it spawned had died, and never surfaced the child's stderr. ROOT CAUSE, proven this session: the ephemeral eval lane forges a SECOND Devstral-24B (~14 GB) while the live persona lane already holds ~26 GB. With only ~9.6 GB free, macOS jetsam SIGKILLs the second llama-server the instant it maps the model — before llama.cpp prints a single byte (reproduced by hand: exit 137, zero-byte log). It is an OS out-of-memory kill, NOT Metal-context contention and NOT a hang. The masking bug made it look like a mysterious timeout. Two unmask fixes in `wait_ready`, benefiting live AND ephemeral lanes: 1. **Fail loud the instant our child EXITS** — `child_exit_status()` (non-blocking `try_wait`) turns any crash-at-launch — including the jetsam SIGKILL/137 above — into an immediate `Spawn` error carrying the exit status + stderr tail, instead of polling a dead port for 240s. This is the arm that fires for the memory-wall failure. 2. **Fingerprint the empty stderr on the hang-timeout** — `tail_or_hang_marker` turns an empty log into a marker that, read with the exit status, names the two empty-log causes: an OOM/jetsam kill (SIGKILL/137, child exited) vs. a genuine early-init hang (no exit status). A crash from bad args / model-load fault prints its banner first, so a non-empty tail carries that directly. `tail_or_hang_marker` is a pure fn (unit-tested: empty→OOM/hang marker, non-empty→last 20 lines in order) so the load-bearing decision is tested without touching the real `~/.continuum/logs` path (#72 env-dependent-test lesson). This makes the failure DIAGNOSABLE; the underlying fix (don't forge a second 24B for eval while the live 24B is resident — reuse the live lane's weights or serialize via the governor, #59/#234) is a follow-up. Validated: continuum-core compiles (--features metal,accelerate, 0 errors); `tail_or_hang_marker_fingerprints_empty_and_tails_nonempty` green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 28, 2026
…ot just pressure LEVEL — kills the jetsam SIGKILL at the root (#205) The companion to the unmask commit: don't just REPORT the eval-lane OOM loudly, PREVENT it. The gate (`await_eval_lane_memory_headroom` → `refuse_eval_lane_under_memory_pressure`) vetoed only on macOS *pressure LEVEL* + the sustained-pressure gate. But on unified memory the level reads "Normal" while sitting atop only a few GB of real free RAM — it counts compressible/cached pages as available. So the gate green-lit standing up a SECOND llama-server of a known ~14 GB footprint into 9.6 GB of actual headroom, and the OS jetsam-SIGKILLed it (exit 137, zero-byte log — the exact failure this session reproduced by hand). Neither the pressure gate NOR the GPU/CPU placement lease caught it: on unified memory the weights need the RAM on *either* device, so "spill to CPU" doesn't save you. Fix — size against the honest free-bytes number: - `MemoryPressureMonitor` already reads `sysinfo::available_memory()` each poll; publish it to a new lock-free global `current_available_bytes()` alongside the pressure level (the level is a ratio and lies; the bytes don't). - `eval_lane_ram_veto(available, footprint, headroom)` — a PURE, unit-tested guard that refuses ONLY when a KNOWN footprint won't fit in the KNOWN free bytes (+2 GiB headroom), and NEVER when either number is unknown (an unread probe must not starve a node — the pressure gate + placement lease stay the backstops). The refusal names the OOM wall, so the detached ledger carries a real cause and `await_eval_lane_memory_headroom` retries it as deferrable load instead of crashing. - One footprint sizing (`eval_lane_footprint`) now shared by the gate and the placement decision (compression — was duplicated inline). - Both eval-lane spawn sites resolve `base` and size the lane BEFORE the gate, so the RAM check runs pre-cold-load. This is the reliability doctrine [[reliability-is-it-works-not-that-it-reports-failure-well]]: the prior commit made the failure legible; this makes the machine refuse cleanly instead of being OOM-killed. Sibling of the #175 GPU-OOM-poisons-the-backend class — the level-vs-real- bytes gap is the same shape. Validated: continuum-core compiles (--features metal,accelerate, 0 errors); new `eval_lane_ram_veto_refuses_only_a_known_oversize_lane` + existing pressure-veto test green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 30, 2026
…a masked 240s /health timeout (#205 unmask) Glass-boxed 2026-07-27 running the live coding measurement (agent/solve): every `agent/solve` on this Mac failed with a bare "llama-server not ready after 240s (/health request failed)". The real cause was thrown away — `wait_ready` polled the health port for the WHOLE budget without ever checking whether the child it spawned had died, and never surfaced the child's stderr. ROOT CAUSE, proven this session: the ephemeral eval lane forges a SECOND Devstral-24B (~14 GB) while the live persona lane already holds ~26 GB. With only ~9.6 GB free, macOS jetsam SIGKILLs the second llama-server the instant it maps the model — before llama.cpp prints a single byte (reproduced by hand: exit 137, zero-byte log). It is an OS out-of-memory kill, NOT Metal-context contention and NOT a hang. The masking bug made it look like a mysterious timeout. Two unmask fixes in `wait_ready`, benefiting live AND ephemeral lanes: 1. **Fail loud the instant our child EXITS** — `child_exit_status()` (non-blocking `try_wait`) turns any crash-at-launch — including the jetsam SIGKILL/137 above — into an immediate `Spawn` error carrying the exit status + stderr tail, instead of polling a dead port for 240s. This is the arm that fires for the memory-wall failure. 2. **Fingerprint the empty stderr on the hang-timeout** — `tail_or_hang_marker` turns an empty log into a marker that, read with the exit status, names the two empty-log causes: an OOM/jetsam kill (SIGKILL/137, child exited) vs. a genuine early-init hang (no exit status). A crash from bad args / model-load fault prints its banner first, so a non-empty tail carries that directly. `tail_or_hang_marker` is a pure fn (unit-tested: empty→OOM/hang marker, non-empty→last 20 lines in order) so the load-bearing decision is tested without touching the real `~/.continuum/logs` path (#72 env-dependent-test lesson). This makes the failure DIAGNOSABLE; the underlying fix (don't forge a second 24B for eval while the live 24B is resident — reuse the live lane's weights or serialize via the governor, #59/#234) is a follow-up. Validated: continuum-core compiles (--features metal,accelerate, 0 errors); `tail_or_hang_marker_fingerprints_empty_and_tails_nonempty` green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 30, 2026
…ot just pressure LEVEL — kills the jetsam SIGKILL at the root (#205) The companion to the unmask commit: don't just REPORT the eval-lane OOM loudly, PREVENT it. The gate (`await_eval_lane_memory_headroom` → `refuse_eval_lane_under_memory_pressure`) vetoed only on macOS *pressure LEVEL* + the sustained-pressure gate. But on unified memory the level reads "Normal" while sitting atop only a few GB of real free RAM — it counts compressible/cached pages as available. So the gate green-lit standing up a SECOND llama-server of a known ~14 GB footprint into 9.6 GB of actual headroom, and the OS jetsam-SIGKILLed it (exit 137, zero-byte log — the exact failure this session reproduced by hand). Neither the pressure gate NOR the GPU/CPU placement lease caught it: on unified memory the weights need the RAM on *either* device, so "spill to CPU" doesn't save you. Fix — size against the honest free-bytes number: - `MemoryPressureMonitor` already reads `sysinfo::available_memory()` each poll; publish it to a new lock-free global `current_available_bytes()` alongside the pressure level (the level is a ratio and lies; the bytes don't). - `eval_lane_ram_veto(available, footprint, headroom)` — a PURE, unit-tested guard that refuses ONLY when a KNOWN footprint won't fit in the KNOWN free bytes (+2 GiB headroom), and NEVER when either number is unknown (an unread probe must not starve a node — the pressure gate + placement lease stay the backstops). The refusal names the OOM wall, so the detached ledger carries a real cause and `await_eval_lane_memory_headroom` retries it as deferrable load instead of crashing. - One footprint sizing (`eval_lane_footprint`) now shared by the gate and the placement decision (compression — was duplicated inline). - Both eval-lane spawn sites resolve `base` and size the lane BEFORE the gate, so the RAM check runs pre-cold-load. This is the reliability doctrine [[reliability-is-it-works-not-that-it-reports-failure-well]]: the prior commit made the failure legible; this makes the machine refuse cleanly instead of being OOM-killed. Sibling of the #175 GPU-OOM-poisons-the-backend class — the level-vs-real- bytes gap is the same shape. Validated: continuum-core compiles (--features metal,accelerate, 0 errors); new `eval_lane_ram_veto_refuses_only_a_known_oversize_lane` + existing pressure-veto test green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 30, 2026
…media-plane split (#2059) * fix(serving): eval-lane bring-up fails LOUD with the real cause, not a masked 240s /health timeout (#205 unmask) Glass-boxed 2026-07-27 running the live coding measurement (agent/solve): every `agent/solve` on this Mac failed with a bare "llama-server not ready after 240s (/health request failed)". The real cause was thrown away — `wait_ready` polled the health port for the WHOLE budget without ever checking whether the child it spawned had died, and never surfaced the child's stderr. ROOT CAUSE, proven this session: the ephemeral eval lane forges a SECOND Devstral-24B (~14 GB) while the live persona lane already holds ~26 GB. With only ~9.6 GB free, macOS jetsam SIGKILLs the second llama-server the instant it maps the model — before llama.cpp prints a single byte (reproduced by hand: exit 137, zero-byte log). It is an OS out-of-memory kill, NOT Metal-context contention and NOT a hang. The masking bug made it look like a mysterious timeout. Two unmask fixes in `wait_ready`, benefiting live AND ephemeral lanes: 1. **Fail loud the instant our child EXITS** — `child_exit_status()` (non-blocking `try_wait`) turns any crash-at-launch — including the jetsam SIGKILL/137 above — into an immediate `Spawn` error carrying the exit status + stderr tail, instead of polling a dead port for 240s. This is the arm that fires for the memory-wall failure. 2. **Fingerprint the empty stderr on the hang-timeout** — `tail_or_hang_marker` turns an empty log into a marker that, read with the exit status, names the two empty-log causes: an OOM/jetsam kill (SIGKILL/137, child exited) vs. a genuine early-init hang (no exit status). A crash from bad args / model-load fault prints its banner first, so a non-empty tail carries that directly. `tail_or_hang_marker` is a pure fn (unit-tested: empty→OOM/hang marker, non-empty→last 20 lines in order) so the load-bearing decision is tested without touching the real `~/.continuum/logs` path (#72 env-dependent-test lesson). This makes the failure DIAGNOSABLE; the underlying fix (don't forge a second 24B for eval while the live 24B is resident — reuse the live lane's weights or serialize via the governor, #59/#234) is a follow-up. Validated: continuum-core compiles (--features metal,accelerate, 0 errors); `tail_or_hang_marker_fingerprints_empty_and_tails_nonempty` green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(serving): eval-lane gate refuses on real free-RAM-vs-footprint, not just pressure LEVEL — kills the jetsam SIGKILL at the root (#205) The companion to the unmask commit: don't just REPORT the eval-lane OOM loudly, PREVENT it. The gate (`await_eval_lane_memory_headroom` → `refuse_eval_lane_under_memory_pressure`) vetoed only on macOS *pressure LEVEL* + the sustained-pressure gate. But on unified memory the level reads "Normal" while sitting atop only a few GB of real free RAM — it counts compressible/cached pages as available. So the gate green-lit standing up a SECOND llama-server of a known ~14 GB footprint into 9.6 GB of actual headroom, and the OS jetsam-SIGKILLed it (exit 137, zero-byte log — the exact failure this session reproduced by hand). Neither the pressure gate NOR the GPU/CPU placement lease caught it: on unified memory the weights need the RAM on *either* device, so "spill to CPU" doesn't save you. Fix — size against the honest free-bytes number: - `MemoryPressureMonitor` already reads `sysinfo::available_memory()` each poll; publish it to a new lock-free global `current_available_bytes()` alongside the pressure level (the level is a ratio and lies; the bytes don't). - `eval_lane_ram_veto(available, footprint, headroom)` — a PURE, unit-tested guard that refuses ONLY when a KNOWN footprint won't fit in the KNOWN free bytes (+2 GiB headroom), and NEVER when either number is unknown (an unread probe must not starve a node — the pressure gate + placement lease stay the backstops). The refusal names the OOM wall, so the detached ledger carries a real cause and `await_eval_lane_memory_headroom` retries it as deferrable load instead of crashing. - One footprint sizing (`eval_lane_footprint`) now shared by the gate and the placement decision (compression — was duplicated inline). - Both eval-lane spawn sites resolve `base` and size the lane BEFORE the gate, so the RAM check runs pre-cold-load. This is the reliability doctrine [[reliability-is-it-works-not-that-it-reports-failure-well]]: the prior commit made the failure legible; this makes the machine refuse cleanly instead of being OOM-killed. Sibling of the #175 GPU-OOM-poisons-the-backend class — the level-vs-real- bytes gap is the same shape. Validated: continuum-core compiles (--features metal,accelerate, 0 errors); new `eval_lane_ram_veto_refuses_only_a_known_oversize_lane` + existing pressure-veto test green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(deploy): install the continuum CLI as a real COPY on PATH, not a symlink into the ephemeral cargo target dir The flaky mess where `continuum` vanishes post-boot ([[deploy-cli-binary-deleted-from-target-dir-post-boot]]): `start-server.sh` symlinked ~/.local/bin/continuum → the cargo target-dir binary. But that dir is a BUILD artifact — cargo replaces the binary mid-rebuild, `cargo clean` and rust-analyzer's feature-mismatched rebuilds delete it — and the PATH symlink then dangles, so `continuum <cmd>` dies with "no such file or directory" (hit twice this session). Fix: COPY the binary to ~/.local/bin (atomic temp+mv so a concurrent `continuum` invocation never sees a half-written file), and `rm -f` any pre-existing entry first so a leftover symlink from an old install can't make `cp` follow it back into the target dir. The PATH binary is now decoupled from cargo's churn — it changes only on deploy. Same self- provisioning intent, minus the ephemeral-artifact coupling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(benchmark): BenchmarkAdapter trait + registry — the grid-transparent, reusable, learn-from benchmark interface (rail 1 of #123) The goal (Joel): "have all these benchmarks IN OUR SYSTEM, AUTOMATED so we or others can REUSE them (just the optional download + the adapters). Run, TARGET and — more importantly — LEARN from these." And: "any command can run anywhere, so can benchmarks — a persona in any continuum can bench anywhere." That settles the architecture and this is rail 1 of it: - `BenchmarkAdapter` — ONE trait per benchmark: `dataset()` (OPTIONAL download spec, never bundled), `tasks()` (items in the canonical `EvalTask` shape so the SAME `agent/solve` path runs them — persona as a whole AGENT, not a bare LLM), `grade()` (defaults to the EvalTask's own test/expect verdict; real-repo benchmarks like SWE-bench override to grade the workspace after the agent acted), `resources()` (a hint for grid placement). - `DatasetSpec` / `DatasetKind` (HF / URL / Git) + `BenchResourceHint` (dataset bytes, needs_container, needs_network) so the runner fetches on demand and the governor can place the run on a capable node — the same demand-vs-resource negotiation serving/eval already do. - A process-global registry (`register`/`get`/`names`) — the single lookup seam the `benchmark/run` DynCommand resolves against; an unknown benchmark is a clean miss so the runner can fail loud with the known list, never a silent skip. - `TaskOutcome` / `BenchGrade` — the agent/solve artifacts (spoken + patch + workspace + harness verdict) handed to `grade`, and the per-task pass/score/reason that aggregates into the scorecard AND, on failure, feeds salience→curriculum→train (#116/#122) — benchmarks as CURRICULUM, not just a scoreboard. Why Rust-native (not the ad-hoc benchmarks/*.py): a DynCommand is the grid-transparent primitive — `Commands.execute("benchmark/run", …)` routes local-or-remote over airc; python can't route the mesh. Adapters shell to python/docker graders ON whatever node runs them. Outlier-validation order (next rails): OUTLIER A = HumanEval (tiny download, static, test- graded); OUTLIER B = the Terminal-Bench ContinuumAgent adapter (agentic meta-harness that unlocks TB's registry — docs/architecture/BENCHMARK-HARNESS-INTEGRATION.md). Then the `benchmark/run` DynCommand + grid dispatch + the learning tie-in. Validated: compiles (--features metal,accelerate); `registry_round_trips_and_reports_unknown` + `default_grade_delegates_to_harness_verdict` green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(benchmark): HumanEval-rs adapter — outlier A on the BenchmarkAdapter trait (rail 2 of #123) The quick-cognition smoke rung (a step above arithmetic, per Joel): 156 Rust-translated HumanEval tasks our EXISTING Rust grader runs directly. Deliberately the SIMPLEST possible adapter — tiny, RESIDENT (no download), static, test-graded — so pairing it with a maximally-different outlier B (the Terminal-Bench agentic meta-harness: big download, real-repo, container-graded) proves the interface across both extremes. Nearly free because the in-repo `docs/genome/humaneval-rs.jsonl` rows ARE serialized `EvalTask`s (`{id, prompt, test, lang}`) — the same shape `cognition::eval` already deserializes — so the adapter is a per-line `serde_json::from_str`. This also exercises the trait's NO-DOWNLOAD branch (`dataset() == None`): a benchmark small enough to bundle needs no fetch; only the big ones (SWE-bench) do. Pure `parse_humaneval_rs` (line-by-line deserialize, honors `limit`, skips blanks, FAILS LOUD with the offending line number rather than silently dropping a task and inflating the pass rate) is unit-tested without the filesystem. Validated: compiles (--features metal,accelerate); `parse_maps_rows_to_evaltasks_honors_limit_and_fails_loud` + `adapter_identity_and_no_download` green. Next rail: the `benchmark/run` DynCommand (resolve adapter → tasks → agent/solve → grade → scorecard, grid-transparent) so this scores a model on any node — incl. BigMama's live Kimi-Linear-48B CUDA lane on :58057. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(memory): MemoryRecord (origin_node, origin_seq) — the RAID replication-provenance seam for Persona-RAID (#2056, co-designed w/ BigMama) Two additive fields on MemoryRecord, the target BigMama's Persona-RAID slice-2 receiver stamps on replay: - `origin_node: Option<String>` — node that ORIGINALLY admitted the record (None = local/lived) - `origin_seq: Option<u64>` — monotonic per-(persona, origin_node) admit seq, the newest-wins replay key (None = pre-replication) The KEY invariant (my review edge, now the agreed shape): replication is an ORTHOGONAL axis, NOT a new experience kind. `memory_type` (lived vs `shared-by`) is untouched — a replicated record keeps its experience (a replayed lived memory stays lived; a shared-by lesson stays taught) and merely gains (origin_node, origin_seq) as auditable/replayable metadata. So recall needs zero changes and audit gets everything. Both `#[serde(default)]` = zero migration for existing rows; every current construction site is a local admit → (None, None), semantically exact. ts-rs regenerated: `MemoryRecord.ts` gains `origin_node?: string` + `origin_seq?: number` (number not bigint, per the #120 drift rule). Validated: continuum-core compiles (--features metal,accelerate); memory::types tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(benchmark): wire BenchmarkAdapter registry into benchmark/run — inventory self-registration, catalog-then-adapter resolution (#123) Converges the two benchmark systems instead of forking a third. The static known_benchmarks() catalog keeps its proven, DEPLOY-SAFE embedded-gym path for resident benchmarks (resolve_gym finds the gym even without a repo checkout). The BenchmarkAdapter trait becomes the EXTENSION seam for benchmarks the catalog can't express — downloadable / custom-graded ones (Terminal-Bench, SWE-bench) now land as pure adapters with zero change to benchmark/run. - benchmark/run resolves catalog FIRST (name it knows → embedded gym), else the adapter registry (benchmark::get), else fail loud listing BOTH sets. A downloadable adapter (dataset() = Some) fails loud 'download not wired yet' rather than silently scoring empty; resident adapters (dataset() = None, e.g. humaneval-rs) run today. Delegates to the ONE grader (cognition/eval) exactly as before — never reimplements grading. - Adapters self-register via inventory (the SAME mechanism commands use), so a builtin needs NO boot hook and NO central list (the dynamic-discovery contract). get()/names() fold the link-time inventory set in over runtime registrations. - HumanEvalRsAdapter submits itself; new test pins that benchmark::get('humaneval-rs') resolves with no boot hook. 19 benchmark tests green; 0 errors. This makes rail 1+2 of the adapter framework LIVE on the grid-transparent benchmark/run command, ready for the Terminal-Bench ContinuumAgent adapter (outlier B) to slot in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(boot): macOS core boot — bash-3.2 manifest gate + rt_handle.spawn off-runtime panic (#194) TWO regressions that made the macOS core un-bootable since the last long-running core died (glass-boxed live 2026-07-28; nobody could reboot on macOS): 1) start-server.sh sourced generated/manifest.macos.sh (bash-4 `declare -A`) under `set -e`; macOS ships bash 3.2, so the source aborted the whole boot before cargo ran. Regression from #2046 'serve on Windows' regenerating the manifest with associative arrays. Fix: only source the bash-4 manifest on bash 4+ (it solely feeds the Windows/CUDA runtime-PATH augmentation, whose own guard already tolerates absence). 2) ipc/mod.rs:1704 used bare `tokio::spawn` in the SYNC boot region (after the rt_handle.enter() guard drops) → 'there is no reactor running' panic → the IPC listener thread died → socket never bound → whole core a zombie. Regression from #2051's AircInterceptor block; every other spawn in the fn already uses `rt_handle.spawn`. Only reached when airc deps are present, so it bricked boot on every airc-configured host. Fix: rt_handle.spawn, matching the siblings. Verified: core boots to socket-live + answering commands in 60s; personas resume from disk and respond in live chat (Anwen answered a direct question with memory recall). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(boot+ui): macOS core boots + ONE automatic door into the positron interface (#194/#29) Two boot regressions that made the macOS core un-bootable since the last long-running core died, plus the missing automatic entry into the interface (you can't ship a beta a user can't open). BOOT (both verified — core now reaches socket-live in ~60s, personas resume + answer live chat): 1. start-server.sh sourced the bash-4 `declare -A` manifest under `set -e` → macOS bash 3.2 aborted the whole boot before cargo ran (regression from #2046). Gate the source on bash 4+. 2. ipc/mod.rs:1704 bare `tokio::spawn` in the sync boot region (after rt_handle.enter() dropped) → 'no reactor running' panic killed the IPC listener → socket never bound → zombie core (regression from #2051). Use rt_handle.spawn like every sibling. UI DOOR (#29): nothing tied the built positron web client to the running core, so finding 'how do I open the interface' required archaeology — exactly how a user (and an agent) gets lost. - New tools/scripts/open-ui.sh + `npm run ui`: resolves the core WS (8974) + call/video WS (8790) + a stable identity, ALWAYS rebuilds apps/web from current source (a stale dist renders an old shell — the 'interface looks lost' trap, glass-boxed today: a Jul-18 dist showed a bare chat view, not the current positron HUD), serves it, opens the browser with everything pre-wired. Verified live in headless Chrome: the full positron HUD (SYSTEM CPU/MEM/GPU sparkline, NODES, genome-paging tiles per persona, Go-live, rooms, live persona cognition) renders against the live core. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(live): auto-start the LiveKit avatar rail with the core (server + bridge sidecar) The persona's talking avatar is Bevy-rendered and published to a LiveKit room via the livekit-bridge sidecar; the browser's 'Go live' subscribes to that room. But NEITHER the SFU nor the bridge was started by `continuum start` — glass-boxed 2026-07-28 by joining the call plane headless: LiveKit :7880 was DOWN → get_or_create_agent fails → no avatar video pump ever runs, and clients only ever saw the native call_server's test-pattern default. The avatar was un-launchable without manual, undocumented steps (start livekit-server, build + start the bridge) — a beta can't ship a 'Go live' button that needs hidden setup. start-server.sh now runs start_livekit_rail() before the core, idempotently + NON-FATALLY: 1. livekit-server --dev on :7880 (dev creds devkey/secret = the bridge defaults) if not already up; warn+skip if the binary isn't installed (core still boots, only live A/V off). 2. build (release, once — links webrtc-sys) + start the livekit-bridge sidecar on its unix socket BEFORE the core, so the core's bridge_client finds the socket at boot. A missing/failed rail never blocks the core (chat/cognition/serving unaffected). system-stop.sh gets symmetric teardown of the bridge (server teardown already existed). Verified: syntax clean; idempotent against an already-running rail (both start steps skip when :7880 is up + the bridge is running). The rail itself is proven live this session — livekit-server + bridge up, core connected, an STT participant joined the LiveKit room. Follow-up (functional, not automation): the per-persona avatar VIDEO agent (get_or_create_agent + spawn_avatar_video_pump) still doesn't publish — only the STT listener joins the room. Next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(live): tee avatar frames into the native call plane — everyone sees the real face (#193/#172) The avatar published to LiveKit, the bridge received it (640x360), but the native call plane (call_server WS 8790) that BOTH the positron web client AND the glass-box harness read still emitted its own 160x120 SMPTE test pattern. The real avatar was stranded on a rail no native viewer subscribed to. Convergence (render once, two sinks): the single Bevy slot the avatar pump already allocates now feeds BOTH LiveKit and the native plane. Per frame the pump tees the same RGBA into CallManager::push_avatar_frame, encoded in the exact [VideoFrameHeader][pixels] contract native clients decode, labeled with the persona's uuid. No second Bevy slot, no parallel renderer. - call_server.rs: push_avatar_frame (new seam) + retire the auto-start test pattern to an opt-in debug affordance (CONTINUUM_CALL_TEST_PATTERN=1), honoring the TODO that sat on the auto-start block since real sources were 'not yet connected'. - video_pump.rs: tee each frame (stable source Handle + monotonic seq/clock) alongside the LiveKit publish. - modules/live.rs + ipc/mod.rs: thread the native CallManager through VoiceState to the register-session pump spawn. - example: standalone CallManager (tee is a no-op there). Live-verified on the native plane after deploy: 640x360 real avatar (Asha's VRM, not the test pattern), 13.5 fps wall / 13.3 median (tracking the 15fps Bevy target), inter-frame jitter min 56.8 / median 75.4 / max 86.4 ms, 0 dropped/out-of-order frames, sole sender = Asha's uuid. Random-frame spot-check shows her real face, live-animating. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(voice): one canonical model root — kill the CWD-relative path bug that silently broke every local TTS/STT (#195) Root cause (glass-boxed this session): the workers→core/tools restructure moved the runtime model download root to `tools/models/` (gitignored — see .gitignore's own note), but every audio adapter kept stale, CWD-relative `models/…` path constants. When the core runs from the repo root (its normal CWD), `models/piper/…` pointed at the *tracked* avatar dir, not the voice models in `tools/models/piper/…` — so Edge returned empty, and Piper / Kokoro / Moonshine / Whisper all reported 'model not found'. Every local voice model was silently dead. Kokoro even mutated the process CWD (`set_jtag_cwd`) to paper over it. Proper fix — single source of truth, absolute, CWD-independent: - New `live/audio/model_root.rs`: `voice_model_root()` / `voice_model_path()`. Resolves `CONTINUUM_MODELS_DIR` (config.env single-owner, then process-env boot injection), then `tools/models`/`models` CWD candidates, then `~/.continuum/models`. - Routed EVERY voice adapter through it — piper, kokoro (15 sites), orpheus, moonshine, whisper, pocket-tts, silero VAD, tts_service. No more scattered `models/…` literals, no per-adapter candidate ladders, no `set_current_dir`. - start-server.sh exports `CONTINUUM_MODELS_DIR=$REPO_ROOT/tools/models` before exec so the binary resolves models from any CWD. Live-verified after deploy: fresh core has CONTINUUM_MODELS_DIR injected, no symlink present, `voice/synthesize-handle --adapter piper` resolves the model and synthesizes (was 'model not found' before). (The short synth duration is a separate phonemizer/espeak-data issue, not this.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(live): tee persona voice into the native call plane — client hears her, not hold music (#193 audio convergence) Sibling of the avatar-video tee. A persona speaks via LiveKit (speak_in_call → bridge), but native clients (positron web, glass-box harness) read the native mixer — which, with only a lone listener, plays HOLD MUSIC. So a native viewer SAW her avatar but HEARD hold music: her voice was stranded on the LiveKit rail. Render once, two sinks: speak_in_call now returns the synthesized PCM, and the voice/speak-in-call handler tees the SAME samples into the native plane via CallManager::push_persona_audio — which registers the persona as a virtual AI participant in the call's mixer (the mixer already has an AI ring buffer built for 'dump a whole TTS utterance, drain frame-by-frame') and her presence stops the lonely-listener hold-music fill. Self-heals across call recreation via a stable per-(call,persona) handle. Live-verified: with Asha registered + speaking (kokoro), a native-plane capture shows audio sender = her uuid (90e758b2) for 4.58s, matching the 4.55s utterance — was 100% 'hold-mus' before. Video (640x360 avatar) + audio (her voice) now both reach the client: e2e see+hear on the native plane. Also this session: espeak-ng installed so kokoro TTS produces real full-length speech (piper's Rust phonemizer truncates — kokoro is the good local path). Follow-ups: hold-music still fills her SILENCE between utterances (minor polish — suppress when a persona is present); STT/audio-in blocked on moonshine ONNX format mismatch; livekit-bridge needs supervision (dies on every core restart). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(positron): durable local state + self-healing feed — inherent to the SDK, adapter-driven (the Twitter model) Glass-boxed 2026-07-29: routine core reboots orphaned every open tab — the state feed died once, the shell silently degraded to the bare chat view, and four months of positron HUD looked 'lost' until a manual refresh. Per Joel: this is normal app craft (cache-first boot, live reconcile, visible reconnect) and it must be POSITRON architecture — from the thin-client SDK out, adapter-driven local state, never an app-level hack or a localStorage bodge. SDK (sdk/typescript — the contract every platform SDK mirrors): - StateStorage.ts: StateStorageAdapter — the ONE local-durability seam. The whole renderable state is latest-envelope-per-kind (each envelope is a full snapshot), so the cache is tiny + complete. Adapters: IndexedDbStateStorage (browser), MemoryStateStorage (tests/ephemeral + conformance reference); swift/kotlin/ flutter implement the same interface over native stores. Cache is an accelerant, never a dependency (storage failure -> live-only, logged once). - StateConnection: durability + resilience are now INHERENT — - hydrate-first connect(): cached envelopes paint before the network is touched (instant last-known UI, even against a dead core), status 'cached'; - write-through: every live envelope replaces its kind's row (fire-and-forget); - auto-reconnect (default ON): capped 1s->10s ladder using the wire's existing last_seen replay; a failed FIRST connect rides the same ladder (core booting); - onStatus surface (cached/connecting/live/reconnecting/closed): recovery is LOUD — reconnecting stays visible while the core is away, so self-heal can never mask a dead core; close() stops the ladder (intentional shutdown); - reconnect:false preserves the legacy one-shot fail-loud contract for probes. - Fail-loud config errors (no registered kinds) never enter the retry ladder. App (apps/web): shrinks to what an app should be — pass IndexedDbStateStorage, render envelopes + ONE status chip. Zero resilience logic app-side. Tests: 97/97 SDK suite green; 4 new pins (default-resolve+reconnecting status, hydrate-before-open, write-through, drop->reconnect->resubscribe w/ last_seen). Live-verified: rebuilt app renders the full positron HUD through the new feed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(mobile): positron durable-state contract on Android + iOS — Dart mirror of the SDK, tested One Dart implementation (byte-identical on both OSes) mirroring sdk/typescript's inherent resilience contract, per [[positron-durable-state-is-sdk-inherent-adapter-driven]]: - lib/positron_state.dart: StateEnvelope + StateStorageAdapter (the ONE seam) with MemoryStateStorage (conformance reference) and FileStateStorage (dart:io JSON — durable on Android + iOS app dirs, ZERO plugin deps, corrupt-tolerant, cache is an accelerant never a dependency); StateConnection with hydrate-first connect, write-through, capped 1s->10s reconnect ladder w/ last_seen replay, loud status (cached/connecting/live/reconnecting/closed), reconnect:false one-shot fail-loud. Injectable StateSocketFactory — tests drive the lifecycle without a core. - lib/live.dart: LiveConnection now RIDES the contract (was a one-shot connect that died silently — the same disease the web had). App keeps only the ChatViewState->MobileScreen mapping + optional status/cacheDir wiring. - test/positron_state_test.dart: 5 pins mirroring the TS spec — hydrate-before- socket, write-through, drop->reconnect+last_seen, fail-loud vs self-heal, FileStateStorage conformance (round-trip, replace-by-kind, corrupt-tolerant). - ios/: runner scaffolded (flutter create --platforms=ios). Verified: flutter test 6/6 green, flutter analyze clean. Platform note: the Dart contract tests prove BOTH OSes (same code); Android SDK present for APK builds; iOS device/simulator builds need full Xcode on this machine (CLI tools only). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): tab strip renders from ONE open tab — the focused room IS a tab Glass-boxed live: the strip + CSS + nav plumbing all shipped (38b60caae), but the render gate was cells.length > 1 while the node's room-set fold knows exactly one room (cambriantech) — so the whole bar hid and the interface read as 'tabs don't exist'. One open activity is still an open tab; the strip now draws from 1 up, and fills out as the room set grows. Substrate follow-up (separate card): seed spawn_room_set_fold from airc's subscribed-room registry (durable membership), not just observed traffic — today a room with no traffic since core boot never becomes a tab. Verified live: playwright screenshot + widget-state dump (tabBarTabCount 1), web tests 21/21, typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(web): Discord shell geometry — full-height rails, center-scoped tabs/header/compose The reference is the Discord/VS Code shell: columns run window-top to window-bottom; no chrome bar spans the whole width. The left rail now opens with the continuon (the server-header slot), the tab strip sits centrally over the content column only, the room header/transcript/composer all live inside the center column, and the ROOM context rail runs full height. Mechanically: RenderTarget.workspace grows an optional WorkspaceChrome<Out> slot (patterns) — the compose bar stays HOST-owned (input state + send handler) but SHELL-placed (chrome.centerFooter), so the widget no longer appends full-width rows after the surface. litTarget nests tabs + header + what + footer in a .center flex column inside the same .panels grid the universe skins and mobile rules already key off. Also: /// <reference lib="dom" /> on sdk StateStorage.ts — the IndexedDB adapter's DOM types broke typecheck for non-browser consumers (tui) since e4fedac8f; scoped ambient types fix every consumer without forcing lib:dom. Verified live: playwright screenshot (full-height rails, central tab, center compose), web 21/21 + chat-view 56/56 + patterns 5/5, typecheck clean on web/patterns/tui. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(chat): beforeMessageId scroll-back cursor on chat/poll — history pages out of durable storage The Twitter endless-scroll's storage half: chat/poll gains the backward cursor. beforeMessageId resolves the anchor's stored timestamp (same lookup as afterMessageId, now ONE anchor_timestamp helper), filters $lt, queries DESC, and normalizes chronological — the limit messages immediately preceding the anchor, straight from the durable chat_messages store. The two cursors are mutually exclusive and reject loud BEFORE any storage round-trip; the result echoes the cursor so the caller's paging loop just keeps passing the oldest id it holds. Client loop (web/mobile/tui alike): render the live 50-row tail, then scroll-back = chat/poll {roomId, beforeMessageId: oldest-on-screen} — prepend, repeat until an empty page says history is exhausted. The render-side wiring is the follow-up slice; the trigger idiom stays per-target (IntersectionObserver on web, ScrollController on Flutter), the cursor mechanics live here, once. Tests: before-anchor $lt+DESC+chronological, both-cursors reject (pinned to fail before data/query), absent-not-null echo. 32/32 chat module green; ts-rs bindings regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(sdk): export the drifted wire types — 64 registered-command params/results never emitted ts-rs bindings The pre-existing drift that has blocked the TypeScript SDK re-emit (and forced nav/select onto the raw-wire path): TS-deriving wire types across commands/{benchmark,help,tool}, cognition, runtime, and modules lacked #[ts(export, export_to)] — the emit's vendoring walks the registry and fails loud on the first missing binding. Swept every one onto the same protocol/typescript/<module>/ convention its file siblings use. export_bindings: 1236 green (was 1160). Remaining emit blocker (separate card): bare #[ts(export)] types land in the crate-local bindings/ dir while the vendorer expects protocol root. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(web): Twitter endless scroll — scroll-back pages durable history into the transcript The render half of the beforeMessageId cursor (02d5c7701): - chat-view: historyRowsFromPoll — one chat/poll storage page (raw entities: {content:{text}, ISO timestamp, no sender name}) onto the SAME MessageRowVM rows the live tail renders; roster-resolved identity, short-id + metadata.source fallback, live-tail dedup, malformed-row skip. 3 specs. - widget: scroll-near-top pages one older window and prepends with the viewport anchored (scrollTop compensated); rows that slide OUT of the live 50-row window RETIRE onto the buffer so no gap opens; buffer clears on room switch; an empty page latches exhausted. - scroll-yank fix (reported live): pin-to-bottom now only fires when the reader was AT the live edge (_wasNearBottom, measured pre-render) — a scrolled-back reader is never forced down by a new message. - host: chat/poll over the same raw-wire seam as nav/select (the typed CommandMap re-emit is still blocked; path documented in-code). - sdk drift: corrected the swept export_to paths to the proven ../../../protocol/typescript/ convention (benchmark/help/tool/agent); strays under core/protocol removed; export_bindings 1172 green. Live activation needs the rebuilt core (beforeMessageId lands on the next core restart — held deliberately: a restart replays the room log until #242 consumer cursors land). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): transcript times in the VIEWER's timezone + genome panel goes two rows of four Time: formatTimeOfDay was hardcoded UTC ('4:36' at Joel's 11:36 PM — wrong for every human off-meridian). Now local getHours/getMinutes; determinism moves to the test scripts (TZ=UTC pinned in chat-view + web package.json) instead of being baked into the product. Genome: 8 slots in a 4×2 grid (was one row of 4) — the loadout is heading past four as skills go per-domain and expert granularity (#226) lands; slots stay honest-dark until genes page in, top-8 lit with overflow named in the tooltip. Tests 59+21 green, typecheck clean, rebuilt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): genome-click anchor re-lands after layout settles — the card sticks at the pane top Verified with a headless click receipt: the genome block DOES open that citizen's persona-home tab anchored at the #genome card (element navigation, card 95844639), but the single scrollIntoView fired before avatar-image decode / meter layout settled — the card drifted ~400px down-pane, reading as 'nothing happened'. Two follow-up rAF re-lands pin it. Harness note: headless clicks were steering the LIVE view — the harness shared Joel's ?me= citizen (nav focus is per-citizen, server-side). Minted a dedicated harness citizen (~/.continuum/ui-test-id); future interaction tests use it, never the operator's scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(nav+web): persona selects OPEN durable tabs (never swap) + sender names link to profiles + history-trigger guards Three live reports from Joel, one slice: 1. ONE shape-shifting persona tab: the nav reader derived the persona tab from the single current focus — selecting a second persona REPLACED the first. NavFocus now keeps the citizen's open non-room activity SET (activity == room == tab: a persona select OPENS a durable tab, a second select adds a SECOND tab) + a close() for the future nav/close verb. Reader surfaces every open activity. 9 nav tests green. (Core-side — live at the next core restart.) 2. 'History load failed: chat/poll rejected: unknown error' on persona click: two stacked bugs — the persona home OPENS at scrollTop 0 which tripped the transcript's near-top history trigger (now guarded off all non-transcript faces), and the bare-wire success check treated every response as a rejection (no success field on the raw path — only an explicit false is an in-band rejection; failures reject the promise). 3. Sender names in the transcript now open that citizen's profile — the SAME composed roster LISTING_SELECT the tiles fire, one nav verb, no parallel route. Keyboard-accessible, element-link affordance. Web 21/21, typecheck clean, rebuilt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(nav+airc): tab close wired end-to-end + consumer cursor kills the reboot replay (#242) Tab close (Joel: 'tab close not wired yet' + 'super small hitbox'): - core: nav/close verb — removes one open activity from the citizen's tab set (NavFocus::close), clears focus if it was current, publishes nav:changed. Registered + in NavModule::commands(). 12 nav tests green. - web: the × is LIVE on non-room tabs (rooms are membership, not tab state) — composed NAV_TAB_CLOSE → widget → injected nav/close over the raw-wire seam; substrate-truth removal, no optimistic local state. Hitbox grown to ~22px square (padding + negative margin — glyph stays compact, target meets the pointer minimum). Consumer cursor (#242 — 'chat still loading literally every message in existence when you reboot', bitten 3× today): the attach stream asked for AttachStart::FromTranscriptStart on EVERY attach. Now a per-channel IpcCursor watermark persists under ~/.continuum/state/; attach resumes AttachStart::After(cursor) (gap only, no seam duplicates), advances to the daemon's RoomTip after attach, and persists AttachCursorAdvanced frames. Only the first-ever attach (no watermark) seeds from transcript start — once per state dir, never per reboot. Documented trade-off: a hard crash loses a slice of LIVE perception, never storage — the durable transcript + scroll-back serve history; replaying the log into every persona's mind each reboot was the worse failure. Live at the next core restart (which will be the LAST replaying one). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(web): durable-store hydration + Activities rename + rooms-only facet default Post-cursor reality check (Joel: 'No messages yet — say hello' after the reboot): the live projection now honestly starts AFTER the consumer watermark, so a rebooted room painted empty — the data was never gone (the durable transcript held every message; chat/poll served it), the window just never pulled it. Fix: a sparse first snapshot (<10 rows) auto-pages the LATEST stored window via the anchor-less chat/poll and prepends — the Twitter model complete: durable tail + live wire, one transcript. Verified live: 51 rendered rows from 1 wire message. Also per Joel: - the left rail's Rooms widget is now titled ACTIVITIES (they are rooms; the widget lists activities) — title flows from the projection. - the facet defaults to [Rooms]: open persona/content tabs are real activities but reached via their own controls (roster tiles, the tab strip) — the 'rooms' facet now excludes persona/content groups, and All remains one click away. Web 21/21 + chat-view 59/59, typecheck clean, rebuilt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): 'Send failed' on every SUCCESSFUL send — only explicit success:false is a rejection Same response-shape bug as the history handler, now on the send path: chat/send's success payload is {eventId, messageId} with NO success field, so `!result.success` threw 'chat/send rejected: unknown error' while the message actually landed on the wire (glass-boxed live — Joel's 'How are you, I am Joel' arrived as peer-a5ded599 despite the strip). Failures reject in the transport; in-band rejection = explicit false. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(positron): projection hydrates from the durable transcript — the SUBSTRATE owns room fullness, never the renderer Joel's charge, accepted: 'why was the room history cleared? that's invalid positron — are you coding just regular lit?' The old full-backlog attach was secretly the chat projection's ONLY hydration mechanism; killing the replay (#242) starved the accumulator, and I patched it in the WIDGET — app-level durable-state logic, the inverted shape. Correct shape, now built: positron_source::spawn takes a durable seed (executor + bootstrap room). Before folding live events the projector hydrates its accumulator with the room's stored tail (the same chat_messages query chat/poll serves), pushed through the SAME classify path as wire events — one message semantics, two sources. Data-module warm-up is retried; a seed that never comes degrades to wire-fed-only, logged loud. Every client gets a full room with zero client logic. Also: WebSocketTransport discriminates push frames from replies — the ingress fans state envelopes to command-only sockets, which spammed 'reply for unknown correlation id undefined' 22× in Joel's console while every command actually worked. id-less frames are pushes, ignored; the server-side fan-out fix is a follow-up card. positron_source 16/16, sdk + web green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * revert(airc): consumer cursor OFF until it ships as one unit with perception hydration (#249) The cursor (#242) starved MORE than the UI window: glass-boxed live — Asha's turn prompt was a system prompt + ONE EMPTY user message, and Benchy answered Joel's direct question with 'you haven't provided any context'. The perception substrate (channel digest) drinks from the same cursored bus, so personas were left conversationally blindfolded: minds intact (engrams verified — Asha 11,667 rows, Atlas 10,035, writes minutes old), sensory feed empty. The greeting loop was the honest response to an empty world. Rollback: attach returns to FromTranscriptStart (full replay — the known, working behavior), watermark files deleted. The projection's durable seed (44a8b2606) stays — it is correct independent of the cursor. The cursor relands ONLY together with #249 (perception-tail hydration from durable storage), tested against a live persona turn BEFORE deploy: the lesson is that the replay was load-bearing for TWO consumers, and I verified only one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): auto-scroll tracks READER INTENT, not bottom-distance — the position heuristic silently killed pin-to-bottom Joel: 'you designed it dumb, we did this before — you have to keep track of whether they scrolled up themselves.' Correct. The _wasNearBottom threshold died the moment a tall message grew the bottom-distance past 150px — after which every new message grew it further and auto-scroll never returned. Replaced with intent: a USER scroll away from the bottom parks auto-scroll; returning to the bottom re-arms it; programmatic pin-to-bottom scrolls are guarded out (_autoScrolling) so they never read as intent. Stream deltas (_typing) now also pin, so the token rail stays in view. Web 21/21, typecheck clean, rebuilt — reload to pick up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(web): code is SHOWN, not hidden — line-numbered open-by-default code cards + fence-aware digest Joel: 'the code should have line numbers and show where its placed into code context like you do… i feel like for BOTH persona and humans we show it unless its huge.' Three fixes, one policy (show the start, expand for the rest, same rule at every density): 1. Renderer (parts.ts): blocks ≤40 lines render fully open (the old n<=3 collapse hid a 4-line snippet behind a '▸ RUST' bar); bigger blocks show the first 25 lines with a '+K more lines' expander whose gutter numbering continues seamlessly. Every block gets a line-number gutter. Templates whitespace-TIGHT — the pretty-printed newlines inside the pre-wrap bubble were the giant-empty-padding bug. 2. Digest (messageDigest.ts): fence-aware. Flood bounds now count each fenced block as ONE projected line (the code card self-truncates, so code can't flood pixels), and the head cut treats fences as atomic — live bug: the digest cut Claude's wordstats reply MID-FENCE and the dangling ``` rendered as literal backtick noise. 3. Specs: 3 new regressions (never-split-a-fence, whole-fence-in-head, unterminated fence) — chat-view 62/62, web 21/21, verified live in cambriantech (Asha's 2-liner open+numbered; Claude's 16-line card). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): retire the typing bubble on the settled message, not only on the done delta The stream-end contract had ONE signal (delta.done) and no fallback: a dropped/raced done flag left the cursor blinking forever over a message that had already landed (live 2026-07-30: Atlas + Benchy both looked hung after their turns settled). The settled post IS the ground truth that the stream ended — willUpdate now diffs new-arrival senders on every state change and retires their bubbles. done still works for the common path; this makes the stale-cursor state unreachable. Act pauses mid-turn still show a cursor (real work, no visual vocabulary yet) — that rendering is #254/#253 core-side work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(cognition): FAIL LOUD on a starved prompt — never deliberate on a blank mind The 2026-07-30 outage mechanism, made impossible to miss: when window arithmetic (reserve + tool schemas + framing) squeezed msg_budget to ~0, the fitter's last-resort arm emitted ONE EMPTY user message and every persona greeting-looped for an hour while looking alive. Two guards now: 1. Fitter (delib.prompt.empty): a trimmed tail that comes back EMPTY is refused with an error probe carrying the budget arithmetic — never an empty ChatMessage. 2. contribute() (delib.prompt.starved): a view whose conversation is all empty while the room HAS turns skips the turn with an error probe — the room's messages stay queued, the next tick re-perceives; blind deliberation is never an option. The two verdict tests that broke were silently EXERCISING the bug path — ctor-default window, framing starved msg_budget to 0, scripted adapter masked the blank prompt. They now run at a real 32k window and test what they claim. 21/21 llm_deliberation green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(vitals): cognition compass AFTERGLOW — 6/s decay so the mind's glow is visible between turns + lowercase brand title DOM probe (.gymtool/vitals.mts, the new TS hot-path instrument) caught the wiring working — Anwen mid-turn: Reason 19, Recall 80, Act 16 — for exactly ONE 2s radiator sample before the 40/s decay blacked it out. With turns minutes apart the compass read as permanently dead (Joel: 'cognition not wired into the diamond' — it WAS wired; it was invisible). Decay 40/s → 6/s: a full pulse now eases to dark over ~17s — a readable afterglow of what the mind just did, still honestly dark at rest well inside a minute. Decay test re-pinned to the new contract (82 at 3s, 0 by 20s). Also: brand is always lowercase — <title>continuum</title>. Findings logged, not changed here: QUE pegged at 100 on every row is HONEST — staged digest unread never drains (personas never advance bookmarks, #43's territory); genome slots dark = honestly no genes paged in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(airc): RE-LAND consumer cursor on inbound attach — resume from watermark, never replay the whole transcript (#242, exonerated) Joel on tonight's boot storm: 'why would it replay the whole chat start to finish? its insane.' It replayed because the bus-fed transcript writer needed boot-time replay to fill the offline window — full replay was load-bearing by accident. The cursor (attach After(watermark), persisted per room) delivers exactly the missed window: no storm, no holes. This is a byte-identical re-land of d4dbc9982, which was reverted during the 2026-07-30 outage on the theory it starved persona perception. The deep trace EXONERATED it: perception pulls the daemon's durable tail every turn and never consumed the attach replay; the blank minds were the spawn-pinned window budget bug — since fixed (live-window reconcile) and guarded loud (delib.prompt.starved / delib.prompt.empty error probes). Verification protocol this time, per the incident doctrine: live persona capture non-empty + zero starved probes + quiet boot + transcript continuity, BEFORE calling it done. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(positron): renderer frames coalesce at 10Hz + 100-message window — bursts become beats, not storms Joel: 'if i were me, i would see like the last 100 messages, and scroll for more — positron ought to make this easy.' Two changes, one contract: 1. Subscribed renderers no longer forward EVERY revision (Unlimited). State kinds are latest-wins snapshots on a watch channel, so intermediate revisions are legally skippable — RENDERER_HZ=10 sends the first change instantly (lone message: zero added latency) and coalesces bursts to at-worst 100ms behind. The boot-replay load storm (thousands of folds → thousands of socket frames raining into the tab) becomes ≤10 latest-state frames/sec. Token streams ride the separate stream rail, untouched. 2. Snapshot window 50 → 100 (MAX_MESSAGES_PER_SNAPSHOT), seed query bound to the same constant — one source for the window size. Scroll-back keeps paging older history from the durable store. With the attach cursor (f8e4ae6cc) this closes the storm class: resume from watermark delivers only the missed window, and whatever bursts do occur render as a handful of coalesced frames. positron 105/105+5/5, core positron_source 16/16. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(live): turn-start beacon → 'responding…' under the last message + dynamic lowercase title + continuon favicon Joel, during the dead-looking-interface scare (four minds mid-turn, zero pixels moving): 'in other systems it says XYZ is responding — we could use that right below the last chat item.' Three pieces: 1. Core: the token forwarder emits ONE empty-token START BEACON the moment generation dispatches — before prefill, which on a cold lane runs minutes. No wire change: an entry with no text yet IS the signal. The done flush retires it even on a speechless settle. 2. Web: a typing entry with empty text renders 'responding…' instead of a bare cursor (the bare cursor was the hang-look); text flowing keeps the live tail + cursor. 3. Web: document.title mirrors the current activity — 'continuum — cambriantech' (the #252 short-title rule, brand always lowercase; the static <title> edit finally ships too — it was edited but never rebuilt into dist, my miss). Favicon: the continuon orb becomes the subject (it IS the fourth-wall being), ring as threshold. Web 21/21 + rebuilt; core cargo check clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(web): '(xyz, abc) is responding…' — ONE grey line between the last message and the compose box Joel's exact spec, third iteration tonight: not a transcript bubble — the Discord-convention grey status line pinned above the composer, one line max (nowrap + ellipsis), parenthesized dynamic name list that updates as turns start and settle. Driven by the stream map: the #254 start beacon adds a persona the moment their turn dispatches — minutes before the first token on a cold lane. Beacon-only entries (no text yet) no longer render an empty bubble; streams with real tokens keep the live bubble as before. Web 21/21, rebuilt — refresh to pick up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): STOP KILLING LIVE STREAMS — retire a typing bubble only on ITS OWN settle, and never hide a consecutive speaker's stream The streaming regression Joel caught ('did you just totally remove it'): the stale-cursor fix retired a persona's bubble on ANY arrival from them. With settles landing minutes late and reboot-echo dups landing constantly, delayed OLD messages executed LIVE bubbles mid-stream — streaming was functionally deleted. Retire now requires the arrived content to CONTAIN the streamed tail (it IS the settle); beacon-only entries are never retired by arrivals. Also removed the last-sender bubble suppression — a persona speaking twice in a row is normal, and the skip hid exactly the streams being watched for. done-flag retirement unchanged. Web 21/21, rebuilt — ONE refresh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): '(X) is responding…' clears when the answer lands — beacon-only entries retire on any arrival from their sender Joel, live: 'it didnt go away once responded.' Some settles never stream rail tokens, so the entry stayed beacon-only and its only exit was a done flush that never came. Beacon-only entries now retire on ANY arrival from the sender (a still-running turn's next token recreates the entry instantly — nothing lost); text-bearing entries keep the own-settle content match so delayed old messages can't kill live streams. Line lifetime == inference in flight: appears at generation dispatch, clears at settle. Web 21/21, rebuilt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(web): responding line = the PROMISE phase only — a name drops the moment their words visibly stream Joel: 'if it is responding stop showing it, for that user — it's when we are sure they're gonna respond, then you can show it.' The grey line now lists only beacon-only entries (inference dispatched, nothing visible yet); once tokens stream into a persona's bubble their name leaves the line. Complete lifecycle: dispatch → '(X) is responding…' → words stream in the bubble → settle lands → everything clears. Web 21/21, rebuilt — refresh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(web): dormant minds dim — roster rows recede when every cognition pulse is dark, brighten on any pulse/stream Joel: 'dim the entire row slightly in the user list.' A row with vitals wired but zero across focus/reason/recall/act/speaking is a resting mind: opacity 0.62 with a 0.9s ease, so waking is VISIBLE and the ~17s afterglow keeps recently-active minds bright — row brightness reads as recency of thought. Opacity-only (compositor-cheap); the dim treatment is the interim for #260's full presence lifecycle, where the universe/ theme layer owns the inactive look. Members without vitals (plain agents/humans) never dim on this signal. Web 21/21, rebuilt — refresh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(tests): pin TZ=UTC in the four time-asserting specs — CI-deterministic on any runner PR #2057 review blocker: formatTimeOfDay became viewer-local (by design) but four specs still asserted fixed UTC HH:MM strings — red on any non-UTC runner. The formatter stays viewer-local; the specs pin process.env.TZ before imports. Proven under TZ=America/Chicago and TZ=Asia/Tokyo: chat-view 62/62, web 21/21. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(airc): close #261's SKIP hole — attach never pre-persists the room tip; the daemon's cursor heartbeat is the sole watermark writer Both PR #2057 review findings on the cursor re-land, fixed as one mechanism with airc 9390c32e8 (feat/attach-cursor-advance-heartbeat): 1. SKIP hole: the attach-time room_tip probe persisted a cursor for events not yet processed — a daemon Error frame or transport read error mid-backlog resumed PAST the tip, permanently skipping the unprocessed remainder from live perception. The probe is deleted. 2. Whole-session redelivery: the daemon's AttachCursorAdvanced now rides live streaming (throttled 1/s), so the existing persist arm — previously fed only once at the coalesce seam — advances the watermark continuously. Every advance points at an ALREADY DELIVERED event: resume is always at-or-before what this consumer processed. No skip, and the reboot redelivery window shrinks from the whole session to ≤1s of events. inbound_attach 8/8. Deploys with the rebuilt airc daemon binary; verification protocol: reboot twice, zero duplicate persona messages, watermark file advancing during live traffic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * chore(airc): airc.cursor.advanced probe — every watermark advance gets a receipt The #261 verification found a silently-stale watermark with no way to distinguish 'heartbeat frames never arrived' from 'persist failed quietly' — the arm logged failures only. Glass-box: success needs receipts too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(cognition): predictive [settled] fact + room-speech ring — name the echo BEFORE it is born (#264) Glass-boxed live 2026-07-30: after the conway task completed, the room spent 40+ minutes in a full-room chorus — one sentence emitted verbatim by all three personas in sequence (specimen on #16/#259). The existing repetition facts are retroactive: they fire the turn AFTER the echo, one turn too late to prevent it, and closure statements re-trigger peers because a closure is still a new message. The room had no rest state. Two pieces, both precedented: 1. inbound_restates_fact — the PREDICTIVE member of the repetition family: fires when the NEWEST inbound peer message restates something already said (older visible turn, her own-speech ring, or the room ring), rendering "[settled] X's newest message restates what has already been said here … silence (PASS) is a normal response" BEFORE she replies. Same near_identical_substantial geometry as every other repetition axis (one definition), registered in the perception_facts registry (probe + A/B toggle for free). 2. record_room_speech / recent_room_speech — the room-side sibling of the #148 own-speech ring, and the same starvation fix: with live workspace windows of 2-6 turns, the older copy of every restatement had already scrolled out (verified: 0 fires across an entire live chorus until the ring landed). Recorded ONCE per message at the airc inbound-attach projection seam; the fact drops exactly one byte-exact copy so a message never matches its own record while a genuine re-send still fires. Live receipts post-deploy: perception.fact id=inbound_restates fired=true on all four room personas (one organic fire on their own chorus before the controlled probe ran); [settled] rendered in prompt captures; first fact-bearing tick redirected one persona from echo to a tool call. Behavioral compliance is partial by design — the fact names the fork, the mind chooses; sticky silence (the #264 scheduler half) is the follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(presence): durable room directory — grid citizens are grey when unreachable, never gone (#258/#262) Joel 2026-07-30: "Why won't bigmama's persona ever show up? Feels like you guys don't understand the goals." The goal is ONE directory per grid with cumulative citizens — a client that is a window into the whole grid. The implementation conflated membership with presence: the roster was rebuilt every 2s from a 120-second live window, so any citizen silent for 2 minutes ceased to EXIST. BigMama's citizens (Kimi, Sahar) were perpetually unborn on this node whenever their flaky relay dropped — existence gated on a live transport session. Fix — membership is durable, presence is live: - Per-room directory persisted at ~/.continuum/state/room-directory-<room>.json: every RosterSlotView ever projected, folded on each emit, seeded at boot by ONE deep transcript scan (14d/4000 events) so members whose last event predates the live window exist from the first publish. Steady-state daemon load unchanged (the 2s poll keeps the shallow window). - Published roster = live read ∪ remembered members as `active: false` ghosts with stale liveness signals (availability/vitals) cleared — the interface never lies about liveness (#260). Client renders ghosts dimmed via the existing `.member.idle` path: zero wire change, zero client change. - Identity adopted once: a real display name never regresses to the provisional peer label on a card-less sighting. - Persistence is the loop's concern, not emit_once's — tests stay disk-free (the #7 isolation lesson). Verified live post-deploy: presence.directory.seeded remembered=8 — ALL grid citizens recovered from the daemon transcript including Kimi (e2f0e022), Sahar (df72dbf2), and BigMama's agent (ce8b9074) while their node's inbound relay is down; web roster renders 8/8. Their names stay provisional until their card publish crosses (#262, their side) — and will be adopted permanently the first time it ever does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(identity): publish every persona's airc identity card at birth — no more info-devoid citizens (#262/#248) Joel 2026-07-30: "Neither my user, all her persona, and you have any bio info, which should come over airc… devoid of all info persona." He was right, and the miss was pure wiring: the card system existed COMPLETE on both sides for months — airc's set_local_identity_card persists + broadcasts to every subscribed room, whois renders name/pronouns/role/bio, role_template carries hand-authored bio_templates in each role's voice, and the durable PersonaCard at birth holds name/gender/pronouns/role — but not one continuum path ever called publish. Every persona attached as a bare display name. Fix, at the single birth path (birth_one, right where the durable card is already read for avatar/voice registration — wire identity coheres with presentation identity by construction): - name from the card; pronouns from her presentation spine (profile facet override wins); role tagged continuum-persona-<role>; bio from her role's authored bio_template ({name} substituted), profile "bio" facet overriding; cards minted before role threading get an honest generic bio instead of silence; continuum_persona_id in integrations for cross-system binding. - publish failure is a warn + next-boot retry, never a birth-killer; success fires persona.identity.published. Verified live: probes for all four personas; airc whois now returns identity: published with name + pronouns + role + bio fo…
joelteply
added a commit
that referenced
this pull request
Jul 31, 2026
…to stop (#264 glass-box) Joel asked to glass-box why a persona ACKNOWLEDGED the new [template-loop] fact and kept looping. Her prompt told the whole story: the fact rendered mid-prompt, but the TRAILING working-memory block — the maximum-recency position — carried two full verbatim copies of her looped template as receipts, each prefixed '[settled] I answered: <template>'. Two structural bugs, both fixed at the shared source: 1. Reserved-vocabulary collision: '[settled]' is the inbound-restates fact's word ('this TOPIC is settled; PASS is normal'), while WM used the same token for turn-completion receipts — so her tail literally read as 'it is settled that this is how I answer here'. WM_SETTLEMENT_PREFIX is now '[answered]' (one token, one meaning); all readers go through the const, so the repeat-perception boundary scans are untouched. 2. The trail rendered every receipt verbatim — a looping persona got an N-shot demonstration of the exact behavior the repetition facts (earlier in the prompt) named. Recency wins; she complied with the demonstration, not the fact. render_trail now collapses a receipt that is near-identical to an EARLIER receipt into a one-line stub ('not re-shown'), using the same near_identical_substantial geometry as the detectors — one definition of repeat. Comparing only against earlier receipts keeps every line's rendering stable after first appearance, preserving the append-only KV-cache property (#205/#266). Regression test pins the live specimen shape: dup receipt collapses, first copy stays full, distinct answers and action traces render untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
joelteply
added a commit
that referenced
this pull request
Jul 31, 2026
…ndwork (29 commits) (#2061) * fix(cognition): hydrate speech rings from the durable transcript at boot (#265) Reboot wipes the in-process repetition-fact rings exactly when they're most needed: the post-boot wake round, where every persona re-emits its greeting 2+ times with own_repetition/[settled] blind (observed live 2026-07-30, three times — Anwen's third verbatim greeting landed while this fix's test compiled). The durable transcript (#140) already holds the history; the persist listener now replays the latest 200 lines into the per-room and per-sender rings before consuming live events, with a bounded wait on the executor slot (the #201 ordering race) and a speech.rings.hydrated probe. Test: hydrate_speech_rings_seeds_room_and_own_rings_from_durable_store — fresh UUIDs per run so the process-global rings stay isolated under full-suite parallelism (the #7 lesson). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(cognition): wake hydrates the room tail from the durable transcript (#249) — kills the greeting chorus Post-reboot each persona's airc runtime log holds only events since ITS boot, and #242's cursors rightly never replay — so every mind's visible conversation collapsed to ONE message and the room devolved into a greeting chorus, each persona mirroring the only utterance it could see (glass-boxed live 2026-07-30: Atlas's 5352-token prompt carried '[context] you can currently see the last 1 message'). Fix: when the live window is shallower than the grounding target, AircRagSource tops up from the durable chat store (#140) via a new DurableRoomHistory trait (ChatStoreHistory rides the same chat/poll command as every other consumer — one read path). Hydrated lines enter at lamport 0: structurally grounding-only, never unread — history is context, never fresh perception (#242 intact). Dedup by content through the one room-turn decoder (event ids re-mint across runtime restarts). Loud degrade on store unavailability; airc_rag.hydrated {live,hydrated} probe per top-up. Mind-side sibling of #265 (speech rings) — one durable transcript, every consumer hydrates from it. Test pins the live scenario: 1 live event + durable tail -> merged window, dupes collapse, history precedes the live tail. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(persona/airc): adopt BigMama's heartbeat-cadence identity republish (cherry-pick 1225f2e) — union with the availability pump Her fix (grid-personas branch): the identity card emit at attach is one-shot, so any peer joining LATER renders peer-<uuid> + default glyph forever; a 60s republish grounds late arrivals within a heartbeat window. My branch had the newer availability-aware heartbeat (#260) in the same struct — kept mine, added hers: honest availability AND late-joiner identity, strictly better than either alone. This is the cross-node convergence half of #262 running on BOTH nodes once merged. Original-Author: BigMama lane (1225f2e) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * docs(k3): container format IS the fetch lever — WASTE convergence into the ExpertFetch contract Independent working engine (WASTE, 0.32-0.36 tok/s full K3 on a laptop) confirms the architecture both lanes converged on AND names the missing 50x: 4KiB-aligned per-expert records with gate/up/down adjacent = one pread at drive speed, vs raw GGUF's 32-byte-aligned scatter at 200MB/s. Contract now requires the aligned streaming container (a FOUNDRY output — layout for streaming, not just shrink), LFRU eviction, and pins the reuse baseline: ~13% at K3 sparsity, exactly-0 is always a bug. Task #268 tracks the foundry re-pack stage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * docs(k3): WASTE-EXTRACT — mined record layout + cache semantics for the container port (#268) Transport-proof copy of what I mined from sqliteai/waste for BigMama's lane: byte-exact ExpertRec, single (layer,expert) key, LFRU-vs-LRU numbers, the 16KiB dual-purpose alignment, the Gate-5 working-set CLIFF that explained her exactly-0 retention, and the measured death of non-uniform bits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): expert_ecache — LFRU cache + cliff-aware budget function (#269, M5 lane deliverable 1) WASTE ecache semantics ported: single (layer,expert) key with gate/up/down bundled, LFRU with sampled victims (their Gate 2: LRU collapses to 5% where LFRU holds 29%), and the budget as a FUNCTION — EcacheBudget::derive refuses loudly below one token's working set (their Gate 5 cliff: hit rate is exactly 0 below it; two nodes spent days debugging that silence as a keying bug). Four invariant tests green: cliff-refusal (600MB records fail at 8GB, VQ3 records pass — the container IS the fix), reuse-above-cliff, LFRU>LRU under a one-shot tail, packed-key stability. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(capacity): self-occupancy add-back + cliff probe in the residency planner (#269 slice 2) The 2026-07-30 audit finding, now fixed and pinned: capacities fed to the planner are LIVE free-byte readings that exclude bytes already-promoted experts hold, so every re-plan under unchanged demand under-counted each tier by its own resident set — slow thrash-to-cold. New plan_expert_residency_with_resident adds resident bytes back (from the pager's ledger, never the OS), making unchanged demand a FIXED POINT. Plus a loud expert_pager.below_cliff probe (WASTE Gate 5 arithmetic via EcacheBudget): fast tiers below one token's working set = structurally zero reuse — 'shrink records (#268), don't debug cache logic'. The activated_per_token input is an ARCH-PROFILE fact passed explicitly, never inferred from partial hit ledgers. Test proves all three: naive re-plan shrinks (bug real), add-back re-plan is byte-identical (fixed point), nothing demotes under unchanged demand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(capacity): add-back before the serving fraction + probe binding — planner fixed point green Two repairs to the red-shipped commit: (1) probe read a renamed binding; (2) add-back over-credited by applying AFTER the 0.80 serving fraction — budget must be frac x (free + resident), not frac x free + resident (the fixed-point test caught the hot set growing by one). 3/3 pager tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): expert container reader — the #269 side of the #268 seam Read-side of the WASTE-shaped streaming container (docs/reference/WASTE-EXTRACT.md): manifest.json is the versioned seam contract (record_bytes must be 4KiB-aligned, activated_per_token feeds the cliff arithmetic), experts-L{n}.bin banks open lazily (a grid node holding 2 of 60 shards must not stat the other 58), fetch = ONE positioned read + an 8-byte header identity check so a drifted offset table is an ERROR, never a silently-wrong expert (the reuse=0 bug shape). 4 invariant tests: round-trip every (layer,expert), geometry refusals, identity mismatch, version gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * test(capacity): composed seam invariant — ecache over container, warm pass = zero disk reads The #268/#269 halves proven to compose: above-cliff budget + recurring working set means the second pass is served entirely from cache. Reuse > 0 is now a pinned invariant, not an assumption. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): usage-hotlist warm-start — kill the cold ramp after restart WASTE's usage.waste analog, pure: usage() snapshots (key, hits) hottest-first; warm() pre-admits into empty slots only, with last=0 so live-proven residents always beat warm guesses on recency tiebreaks. Test pins first-pass-all-hits after warm plus budget-truncation (never overfills, never evicts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(persona): turns-that-fit conversation window — kill the 5-message world view (#259) Glass-boxed live 2026-07-30 (Joel's call: 'her RAG cannot be telling her the chat properly'): Asha's captures showed '[context] you can currently see the last 3 messages' while her L1 budget could hold dozens — the grounding=5 count pre-trim starved pack_digest, whose token walk is the REAL allocator. Two allocators, the dumber one binding: a compression-principle violation. Starved of the actual conversation, a capable 24B looped generic-assistant filler for an hour. Fix: grounding derives from the delivery budget (budget / 8-token physical per-turn floor, recipe floor .. 256), so the token budget — never a message count — decides what she sees. Durable top-up (#249) scales with it. The region's pre-staged digest is reused only when it covers the budget-derived window; an under-grounded stage is rebuilt wide. Regression test pins: fully caught-up persona + 4k budget = all 20 turns visible, not 5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * docs(reference): byte-exact ExpertRec header — CONFIRMED against upstream FORMAT.md The layout authority BigMama's GgufRvqSource is gated on: 32-byte LE header with field offsets, fmt enum values VQ3R=4/VQ2R=5 (NOT 0/1 — the guessable trap), offs relative to record start, zero-pad tail, no header CRC. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(capacity): pin activated_per_token = TOTAL across MoE layers in manifest v1 + optional top_k_per_layer Seam sync with the foundry before her first v1 emit: per-layer top-k here would under-state the cliff ~60x (8 vs ~488 on K3) and re-open the reuse=0 trap. top_k_per_layer is the auditable companion (serde-default optional). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * docs(reference): byte-exact codebooks.bin layout — CONFIRMED against upstream convert.py The foundry's RVQ-encoder gate: 16-byte WCBK header + 256x8 FP16 data = 4112B fixed records sequential by cid; cid = cb_base + kind*stages + stage; packer produces it (k-means artifact). Plus per-channel FP16 corrections layout + scale formula. Mined from the producer code, not guessed — the fmt-0/1-vs-4/5 lesson applied. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(cognition): narrated pass lifts to a SILENT Pass — a spoken pass is not a pass (#271) Glass-boxed live: models narrate their silence ('I'll pass my turn', 'I'll pass for now...') as Speak, and each announcement re-wakes every peer into announcing THEIR pass — a 30+ minute cascade of pure filler. Meet the idiom (same family as the narrated tool-call formats): first-person turn-passing collocations lift to Decision::Pass. Fail-open guards: transitive/declining/ peer-advice uses never match; code fences and >500-char messages stay Speak (swallowing substance is the worse error). Tests pin 6 verbatim live idioms silent + 6 substantive shapes speaking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(airc): accept 'current:' as well as 'room:' when parsing airc room output Forward-tolerance for the airc #270 membership-visibility format; the airc side also restored the stable 'room:' label, so both binary generations parse against both output shapes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(boot): never export an EMPTY AIRC_DEFAULT_CHANNEL/ROOM on parse miss A daemon-mid-start race (or output drift) made the awk derive '' and the script exported it — the core then rejected the env var as an invalid UUID instead of running its own discovery, refusing full-citizen boot. Leave unset on miss, warn loudly; the core's discovery is the fallback authority. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): MoeArchProfile — every expert-serving fact READ from the GGUF, never typed (#231) The adapter Joel's law demands ('if some random Mistral MoE or DeepSeek comes out, most of the code should be ready'): general.architecture keys the arch-templated GGUF convention ({arch}.expert_count, .expert_used_count, .block_count, .leading_dense_block_count, .expert_shared_count — two new readers in the ONE gguf_keys home), and the profile DERIVES the cliff input activated_per_token = top_k x n_moe_layers plus projects the container manifest, so hand-typing 488 is structurally impossible. Zero model-family knowledge: outlier A (Qwen3-MoE shape, all-routed, optional keys absent = the arch's own declaration) and outlier B (DeepSeek shape, dense leads + shared experts) profile through IDENTICAL code — tests pin both plus the refusal ladder (dense=NotMoe, missing key=refuse, inconsistent=refuse, no guessed defaults anywhere). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): VQ3R decode reference + golden vectors — the cross-backend contract (#268 kernels) The 1-10 tok/s lane's drift killer: BOTH GPU kernels (her CUDA, my Metal) validate against ONE backend-neutral Rust reference, mined verbatim from upstream convert.py — per-channel scale applied pre-quant, residual stages, decode = scale x SUM(stage codebook rows); blocked index layout [M/64][nvr][64][stages] (their measured 1.44x gather win) with the offset formula in ONE function and the byte order pinned as a golden literal. Padding path exercised; f16 LE conversion point pinned. A kernel is correct when it reproduces the goldens exactly — no more fmt-4/5-class surprises. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): EcacheBudget::derive_from_profile — the adapter-law entry point (#231) Cliff input flows GGUF -> MoeArchProfile -> budget; no caller types an activation count. Closes the read-and-derive chain from artifact to cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(commands): capacity/io-probe — node storage bandwidth as a first-class measured fact In-tree home of the scratchpad NVMe measurement behind the K3 ceiling numbers (M5: 14 GB/s @ depth 4 => 2.3-4.7 tok/s vs WASTE 0.32). Benchmark infra is COMMANDS with receipts, never bash: every grid node can now report sustained record-sized uncached random-read GB/s at several depths, which is the input the weighted layer-split planner (#180) needs per node. Adapter law: record_bytes is an INPUT from the container manifest; the probe characterizes the disk, no model constant lives here. Cache-defeat on the write fd (the 69-GB/s RAM-not-SSD trap, glass-boxed), bank deleted after, Privileged access (disk-saturating), stateless registration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(cognition): [template-loop] perception fact — topic-swapped scaffolds are still circling (#264) The structure axis the repetition family was blind to, glass-boxed live 2026-07-31: personas cycled 'I see that I've been repeating… ### New Exploration Area: <topic>' for HOURS with the topic rotating (security → docs → UI/UX → maintenance). Full-body Jaccard drops under NEAR_DUP_JACCARD the moment the content words swap, so own_repetition never fired — worse, each turn ACKNOWLEDGED repetition (the body fact had fired earlier) and then treated the topic swap as 'genuinely new', which no perception contradicted. template_loop_fact: a message's skeleton = its line-shape sequence (heading/numbered/bullet/prose) + opening-frame token set; only messages with ≥4 structural lines qualify (plain prose stays own_repetition's turf). Two messages share a template when shapes are equal OR openers are near-identical; cluster-counted like the sibling (period-agnostic), fires at ≥3. Pure fact, no output gate: names what happened and the PASS affordance she already has — with the narrated-pass lift (#271) landed, choosing silence now actually sticks, so an idle room can finally rest. One impl + one registry row (TemplateLoop, between OwnRepetition and PeerEcho); test pins the live specimen trio plus three inert cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(cognition): stop working memory re-teaching the loop it was told to stop (#264 glass-box) Joel asked to glass-box why a persona ACKNOWLEDGED the new [template-loop] fact and kept looping. Her prompt told the whole story: the fact rendered mid-prompt, but the TRAILING working-memory block — the maximum-recency position — carried two full verbatim copies of her looped template as receipts, each prefixed '[settled] I answered: <template>'. Two structural bugs, both fixed at the shared source: 1. Reserved-vocabulary collision: '[settled]' is the inbound-restates fact's word ('this TOPIC is settled; PASS is normal'), while WM used the same token for turn-completion receipts — so her tail literally read as 'it is settled that this is how I answer here'. WM_SETTLEMENT_PREFIX is now '[answered]' (one token, one meaning); all readers go through the const, so the repeat-perception boundary scans are untouched. 2. The trail rendered every receipt verbatim — a looping persona got an N-shot demonstration of the exact behavior the repetition facts (earlier in the prompt) named. Recency wins; she complied with the demonstration, not the fact. render_trail now collapses a receipt that is near-identical to an EARLIER receipt into a one-line stub ('not re-shown'), using the same near_identical_substantial geometry as the detectors — one definition of repeat. Comparing only against earlier receipts keeps every line's rendering stable after first appearance, preserving the append-only KV-cache property (#205/#266). Regression test pins the live specimen shape: dup receipt collapses, first copy stays full, distinct answers and action traces render untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * fix(cognition): working memory speaks in HER voice — interoception must not parse as a peer's message (#264 third glass-box finding) Asha, with BOTH repetition facts in-prompt and the receipt collapse live, still looped — and her opening line told us why: 'Thank you for the summary of recent actions and thoughts.' The WM block rides a user-role turn and its header was second person ('Your recent thoughts and actions…'), so Devstral parsed her own interior state as a PEER addressing her and politely replied to it, turn after turn — the reply itself being the templated summary the detectors keep flagging. The entries were already first person; only the header spoke in another's voice. It now reads 'My own recent thoughts and actions (working memory — this is my interior state, not a message from anyone)'. Self-knowledge is never phrased as someone else talking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): manifest v2 tiered reader — per-(layer,tier) banks, the read side of moec_pack_dir_tiered (#269/#268) The reader half of the tier contract locked with the foundry 2026-07-31 (her packer round-trips 82/82 on the write side; our specs crossed mid-wire and matched line-for-line). TierSpec {id, quant, record_bytes} descending fidelity; banks experts-L{n}-T{t}.bin at offset expert_id x record_bytes; fetch_tier(key, tier, buf) is pure mechanism — the pager's policy seam (importance x recency x quant-sensitivity -> tier/residency/prefetch, Joel's all-star/cruft control law) decides WHICH tier, never the container. v1 stays the degenerate case by construction: a manifest without tiers[] synthesizes one tier from the top-level record_bytes and keeps legacy experts-L{n}.bin names — version picks the naming, no heuristics. The alignment law holds per tier; tier ids must equal their index (order drift between writer and reader refuses at open); out-of-range tiers refuse. Tests mirror the packer's round-trip: every (layer, expert, tier) reads back its own header + tier-distinguishable payload; v1-without-tiers-field parses and serves via the tier-less fetch(); range/order violations refuse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): tier rides the identity end-to-end + byte-accounted cache + ExpertFetcher adapter seam (#269) Three moves that make the all-star/cruft allocator implementable as pure policy over pure mechanism: 1. ExpertKey gains tier — ONE identity from the pager's policy seam through the cache to the bank. A sharp and a cruft copy of the same expert are different bytes and never alias; promotion admits the sharp copy and the cruft copy ages out naturally. packed() widens to u64. The container's fetch(key, buf) honors key.tier (the redundant fetch_tier param died — compression). 2. The ecache goes BYTE-accounted (touch_sized): per-record real bytes against the governor-granted max_bytes, so cruft records pack denser than the sharp slot count implies, and an oversized admit evicts as many residents as its bytes require (regression pins the two-eviction case). Slot count stays as the secondary bound; warm-start accounts at the sharp size (a warm guess never over-fills). resident_bytes() is the number the footprint reporter reconciles against the lease. 3. ExpertFetcher adapter trait — the fetch seam adapterized (Joel's directive): outlier A is the local NVMe container; outlier B (remote peer shard over the grid) and the blended layer-split fetcher slot in behind the same two methods without the pager learning a second shape. 89/89 capacity tests green (round-trips, cliff, tier-mix packing, refusals, v1 back-compat). BigMama's TieredContainerFetcher (85/85, k3-adopt) is the same envelope from her side — the tier contract now has writer+reader on BOTH nodes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * feat(capacity): tier-aware pager policy seam — the all-star/cruft allocator v1 (#273) The last mechanism→policy gap to the K3 targets (5/1/10 tok/s). TierPolicy is the seam locked with BigMama's EXPERT-PAGING-CONTROL-LAW.md: policy(importance, recency, quant_sensitivity, budgets) → {tier, residency, prefetch}, so the v1 classic-control heuristic and the sim-trained ML policy (v2) swap behind one signature. ClassicTierPolicy, two orthogonal axes: - Residency (WHERE) rides the existing ranked_experts priority (hits ≫ predicted ≫ gate seed) with FIRST-FIT byte fill per residency tier — cheaper records backfill space sharp ones couldn't use, which is exactly how cruft tiers multiply cache capacity (pinned by the density test). - Fidelity (WHICH quant) is earned importance: weighted demand = hits × quant_sensitivity (Option until her trace sensor lands; None = frequency- only), tier = decades of weighted demand below the live leader — self- normalizing, no absolute thresholds. Control-law overrides: observation window forces tier 0 on new actives (small-quanting a new active corrupts the importance measurement itself); speculative-verify divergence promotes via a caller-side step integrator (self-calibrating, no logit threshold in the policy); unobserved/predicted- only park cheapest (distortion is free until activation; speculation earns no fidelity). Zero-signal experts stay Cold — no fetch bandwidth for what nothing points at. Wire-through: assignment.tier IS ExpertKey.tier (fetch_key refuses u16 overflow, never truncates); planned_bytes per residency tier is the governor- lease reconciliation number against the ecache's resident_bytes(). The #269 fixed-point contract extends to tiers: unchanged demand + resident add-back ⇒ byte-identical plan (test proves the thrash without add-back too). ranked_experts goes pub(crate) — one ranking authority, no consumer re-derives. 9 new tests, 98/98 capacity green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo * docs: proper GitHub social-preview card — ring mark on dark, never blown out on white The repo's share card rendered as a washed-out white rectangle (the logo PNG is transparent, so GitHub composed cyan rings onto white at crop scale — live-spotted on X 2026-07-31). This is a purpose-built 1280x640 card: dark blue-biased ground, the ring mark with its own bloom, wordmark + 'AI citizens, not AI agents.' + the README tagline. Upload target: repo Settings → Social preview (GitHub has no API for it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps @anthropic-ai/sdk from 0.52.0 to 0.71.0.
Release notes
Sourced from
@anthropic-ai/sdk's releases.... (truncated)
Changelog
Sourced from
@anthropic-ai/sdk's changelog.... (truncated)
Commits
3852b08chore: release mainf86b7d4chore: fix ci errorsbdb7439feat(api): adds support for Claude Opus 4.5, Effort, Advance Tool Use Feature...ccf4cacchore: include publishConfig in all package.json filesa2b5cdfchore(readme): fix example import0cc9ef7chore: release main7e0c08afix(structured outputs): use correct beta headerb1e02c3Merge pull request #848 from anthropics/next811e045foundry: fix publish config3eeda6echore: release mainMaintainer changes
This version was pushed to npm by packy-anthropic, a new releaser for
@anthropic-ai/sdksince your current version.Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot mergewill merge this PR after your CI passes on it@dependabot squash and mergewill squash and merge this PR after your CI passes on it@dependabot cancel mergewill cancel a previously requested merge and block automerging@dependabot reopenwill reopen this PR if it is closed@dependabot closewill close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill 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 versionwill 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 dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)