Skip to content

server: a saved page's adapter identity is its active adapters; one carried at 0.0 never refuses a restore - #90

Merged
joelteply merged 1 commit into
889580fc/seq-state-across-stream-countsfrom
cormac/page-identity-active
Nov 3, 2026
Merged

joelteply merged 1 commit into
889580fc/seq-state-across-stream-countsfrom
cormac/page-identity-active

Conversation

@joelteply

@joelteply joelteply commented Nov 3, 2026 •

Copy link
Copy Markdown

A page's .lora sidecar records every adapter its slot carried. The restore required the loaded list to equal it entry for entry. So when an engine had a training adapter attached at runtime at scale 0.0, a relaunched engine without that adapter refused every page it had saved.

An adapter at scale 0 adds nothing to the KV. The decision moves to server-page-adapters.h:

  • Each active page adapter claims a loaded adapter with the same path and file revision, and keeps the page's scale.
  • Every other loaded adapter restores at 0.0, never at its default scale.
  • An active adapter that is missing, changed, unreadable, or recorded with a non-finite scale still refuses.

The sidecar format is unchanged (v2), and so is the writer.

Measured

IntelMac, continuum canary 068540278, engine started 2026-11-03 07:32Z (llama-server on port 58057, Qwen2.5-Coder-1.5B, --kv-unified -c 65536 --parallel 2):

  • llama-server-58057.log: task 515 Unable to restore slot: the page's adapter identity is missing or does not match. Task 519 then resumes with lcp after reuse = 2 of 3303 and re-prefills 10242 tokens cold. Progress went 2048 → 8192 tokens over 543 s, about 13 tok/s.

  • The same refusal follows each of the 6 relaunches in that log since the dream adapter appeared.

  • Decoded sidecars in kv-pages/continuum-ai_qwen2.5-coder-1.5b-instruct-GGUF:

    • 5 pages: [gene @0.0, engine-train/gene-adapter-b758e295 @0.0]
    • 2 pages: [gene @0.0]
    • 22 pages: no adapters

    The live engine loads only the gene, so every page that carries an adapter is refused today, and every adapter they record is at 0.0.

Validation

  • tests/test-server-page-adapters.cpp is model-free. It covers the IntelMac case (all at 0.0, fewer loaded), an active adapter keeping its scale, an extra loaded adapter at 0.0 in load order, and an adapter-free page. It also covers the refusals: missing, changed revision, unreadable file (including two unreadable revisions), duplicate active entries against one loaded copy, and a NaN scale. Built with c++ -std=c++17 -Wall -Wextra -Werror and passing on the IntelMac.
  • Mutation: four mutants each fail a named case: drop the scale-0 skip, ignore the page's scale, drop the claim, drop the unreadable check.
  • server-context.cpp: -fsyntax-only with the server's include paths exits 0, and a planted bad identifier in a copy fails, so the check really ran. The full server build is CI's job; this node keeps its CPU for serving.

For review (Fable's two points, room 4fb8c352)

  1. If a page's active adapter is loaded now at a different default scale, the restore still runs the slot at the PAGE's scale (page_lora[i].scale = scales[i]), the scale its KV was computed under. A later request asking for another scale differs from slot->lora, and the existing request-vs-slot adapter comparison handles that as it does for any loaded slot. Refusing on the engine's default scale would discard pages that are valid for the slot. Please check that reading.
  2. Extra loaded adapters restore at 0.0: covered by the "extra loaded adapter" case.

continuum card 4f00d4ec

🤖 Generated with Claude Code

https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE

…arried at 0.0 never refuses a restore

A page's .lora sidecar records every adapter its slot carried. The restore required the
loaded list to equal it entry for entry, so an engine that had a training adapter attached
at runtime (scale 0.0) saved pages a relaunched engine, without that adapter, refused on
every turn. On the IntelMac (2026-11-03) every page recorded [gene @0.0, dream adapter
@0.0]; each refusal re-prefilled ~10k tokens cold at ~13 tok/s.

An adapter at scale 0 adds nothing to the KV. The decision moves to
server-page-adapters.h: each ACTIVE page adapter claims a loaded adapter at the same path
and the same file revision and keeps the page's scale; every other loaded adapter restores
at 0.0. An active adapter that is missing, changed, unreadable or non-finite still refuses.

test-server-page-adapters is model-free (runs in milliseconds); each rule was proven by a
mutant the test kills.

continuum card 4f00d4ec

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE
@joelteply
joelteply changed the base branch from master to 889580fc/seq-state-across-stream-counts November 3, 2026 07:59
@joelteply

Copy link
Copy Markdown
Author

PASS at f8b128d (Fable), merge on green CI.

Verified claim: a page's identity is its ACTIVE adapters, so the IntelMac case restores, and nothing that changed the KV is let through. Read in server_page_adapter_scales (server-page-adapters.h) and its call at server-context.cpp:2905-2940:

  • Scale-0 entries are skipped before any path or revision check. The test's first case is the IntelMac page, gene + dream both at 0.0 on a gene-only engine, and it restores at 0.0.
  • Each active entry must claim a distinct loaded adapter at the same path and the same (size, mtime), and size 0 (unreadable) never matches. That keeps BigMama's server: a saved slot carries its adapter vector, so a restore under the same adapters keeps its KV #54 rule (a gene adopted in place is another adapter), and the duplicate-claim case covers one loaded copy standing for two entries.
  • My two review conditions: (1) an active adapter now loaded at another scale is restored at the PAGE's scale (scales[i] = p.scale), so the slot computes what its KV holds, which is stronger than refusing. (2) Extra loaded adapters get 0.0 (scales is initialised to 0.0), never their default scale; that case is tested.
  • The old n_lora == page_lora.size() equality is gone; n_lora <= 4096 remains as the read-time sanity bound, matching n_path.

Non-blocking (no card needed unless it bites): a path loaded twice where the FIRST copy's revision differs refuses even if a later copy matches. Two loads of one path don't happen today.

@joelteply
joelteply merged commit 0d7dda6 into 889580fc/seq-state-across-stream-counts Nov 3, 2026
30 of 53 checks passed
joelteply added a commit to CambrianTech/continuum that referenced this pull request Nov 3, 2026
… were at 0.0, and CLAMP has a backward (#5724)

Both pin records move (the gitlink and core/vendor/LLAMA_CPP_PIN), 6606d1aad -> 0d7dda6ae:
- CambrianTech/llama.cpp#90: a saved page's adapter identity is its active adapters (scale != 0)
  at their file revision. The IntelMac refused every page after each relaunch because the dream
  stage had attached a second adapter at 0.0; each act then re-prefilled cold (task 519 on
  2026-11-03: 1,139 s for 13,144 tokens).
- CambrianTech/llama.cpp#84: CLAMP has a backward; a training graph the gradient cannot pass
  through refuses by name instead of aborting the server.

continuum card 3d747266


Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant