Skip to content

fix: three bug fixes and the session's records (staged update fallback, LFS update check, idle clock) - #202

Merged
REPPL merged 12 commits into
mainfrom
integrate/fixes-2026-10-07
Oct 7, 2026
Merged

REPPL merged 12 commits into
mainfrom
integrate/fixes-2026-10-07

Conversation

@REPPL

@REPPL REPPL commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Three bug fixes, each built test-first on its own branch and checked by an independent reviewer. The review findings are applied. This PR also carries the records from the same session.

Fixes

  • A staged update keeps the old version until the new one has loaded (iss-2610042101430192, impact: fix). The old copy stays aside until the new version's first load passes readiness. If that load fails, the old copy goes back automatically and the card says why. A second update before any load keeps the version that last served. Reviewed by an adversarial code reviewer, whose major finding (a second update discarded the version that served) is fixed with a regression test that fails without the fix.
  • The update check reads a config.json kept in Git LFS, and never offers an unchecked version (iss-2610042101439623, impact: fix). The check used to fail open when the Hub redirected to its content CDN. It now follows the redirect, bounded and checked against the listed sha256, and strips the token off-origin. A version whose bytes cannot be anchored is marked cannot_check and is not offered. Security review: approved. Its info notes are filed as follow-ups.
  • The idle-time jobs count every client request the server admits (iss-2610041945030758, impact: fix). A request whose model has left the pool, or that never reached it, now holds the self-test and the context probe, both while it runs and for the threshold after. Security review: approved. Its two low findings (docs wording, a comment) are fixed.
  • The principle file quotes ~/.abcd.noindex/, so internal/archtest passes again on machines with abcd installed. CI skips that test.

Records

  • The MLX serving-performance survey, routed to four issues and three draft intents. The note is graded in the decomposition calibration.
  • The Kolibri 1 assessment (waits for upstream mlx-lm support).
  • The decision-model over-charge triage: folded into the decision-model spec, with a decision line.
  • Three follow-ups from the LFS fix's security review.

Gates (on the head pushed)

  • gofmt -l . empty; go build ./...; go vet ./... (darwin and linux)
  • make test: 26/26 packages pass under -race
  • abcd lint: 168 findings, the same as main

REPPL added 12 commits October 7, 2026 07:08
A staged update removed the old copy as soon as the new record was
written, after the directory checks and the launcher's Precheck. A new
version can pass all of those and still fail to load, and then nothing
was left to fall back to: the model stayed unusable until the operator
downloaded an older version by hand.

The old copy is now held aside (App.fallbacks) until the pool reports a
load of the new version whose readiness probe was answered, and is
removed then. A first load that fails on its own puts it back through
the same swap an update makes, claimed as a download so Download, Delete
and Close wait for it; the record is rewritten for the old version with
no load failure, update_failed "load" (new class, with words on the
card), and the newer commit still offered. Deleting the model removes
the held copy too, and a later update replaces it.

Restart: the held copy is not leaked or lost. Nothing new is persisted;
at start the newest complete aside copy beside a model folder that
checks out is held again, unless a copy of that model was put back at
the same start. This replaces the earlier rule that removed such a copy
at start as stale. Its version is not known after a restart, so one put
back then is recorded as version unknown, which Update brings to the
current version.

Resolves: iss-2610042101430192
Assisted-by: Claude
…hecked

The update check reads a newer version's config.json to tell whether it
names a model_file, code Dessau will not run. A repository that keeps that
file in Git LFS has the Hub hand it to its content CDN; the check's read
refuses any answer off the Hub's origin, and the check treated that refusal
as "offer it". So a newer version that ships its own code was marked
available with Update offered, and only the update's own checks refused it
after the whole download.

SmallFileAt now takes the file as the listing gives it. A git file is read
through do exactly as before. A file the listing keeps in LFS with a sha256
is read through doContent, which follows the CDN hop as a download does,
drops the token from any hop off the Hub's origin, and the body is held to
the bound (a listed size past it is refused before asking) and to the
sha256. When the config cannot be held to that hash (off-origin with no
sha256, a mismatch, or past the bound) the version is recorded with the new
cannot_check status, which the card shows as "newer version not checked"
and both Update() and the panel refuse. A CDN that does not answer is
counted unreachable, as a Hub that does not answer is.

Resolves: iss-2610042101439623
Assisted-by: Claude
The idle check the self-test and the context probe share read only the
pool's resident entries: each model's in-flight count and last use. A
model's figures leave the pool with it, so a request that ended
unreachable on a wedged model that was then unloaded or stopped left no
trace, and a request the pool never admitted (still uploading, refused,
failed before a model was chosen) never touched the clock at all. On
2026-10-04 that let the self-test start a minute after a client's last
unreachable request with the idle threshold at an hour: the remaining
models' last use was 17:34, over the hour by 18:45. The record guessed
the clock counted only requests that reached a model; the cause is that
it lived on the model's pool entry.

The gateway now counts every completion request and every Ask from the
handler's start to its return (selftest.Clients, wired from App.Clients
through gateway.Options.Clients), and the idle loop holds while one is
in flight and for the threshold after the last. A long request keeps
the Mac busy while it runs, not only when it ends. The context probe
drives its model through the gateway, so a last request no later than
the loop's own latest release is discounted, as the pool's stamp of
that release already is. The pool is unchanged; the gateway change is
the counter alone.

Resolves: iss-2610041945030758
Assisted-by: Claude
A second update made before the first update's version had loaded
replaced the held copy with the version the second update displaced.
That version had never loaded, so when the newest one failed its first
load too, the copy put back was one that had never served, and once it
failed nothing was left to fall back to. The held copy now stays, and
the second update's own aside copy is removed instead.

The restore's comment now says that a Download is refused while a
restore runs, rather than waiting for it, and the update docs say that
both versions take disk space until the first load.

Found by an independent review of the previous commit.

Refs: iss-2610042101430192
Assisted-by: Claude
A request turned away for a missing or wrong key, or from an unpaired
client, is refused before the gateway counts it. The docs said every
request counts however it ends, including refused ones. The idle check's
comment also said a request in flight is never the loop's own. A context
probe step that timed out can leave its handler running past the run, so
the comment now says what that costs: one idle threshold.

Found by an independent security review of the previous commit.

Refs: iss-2610041945030758
Assisted-by: Claude
A state-of-the-art survey of MLX serving performance found two budget
defects Dessau has today: the prompt cache has no byte limit, and the
pool admits twice the requests it charges for. It also found two seeds
(measure batching on this Mac; MTP once upstream releases it) and three
capabilities that comparable servers ship: compact KV memory per model,
a prompt cache kept on disk across unloads, and structured output to a
schema. The maintainer adopted the routing unchanged, as graded in the
calibration note.

Assisted-by: Claude
abcd v0.13 renamed its home from ~/.abcd to ~/.abcd.noindex, and the
OPINIONS rule it injects quotes the new name. The principle file still
quoted the old one, so the archtest that holds the file to the rule
verbatim failed on every machine with abcd installed. CI skips the test
because abcd is not on its PATH, so main stayed green there.

Assisted-by: Claude
…triage

Kolibri 1 (an Aleph Alpha MoE in 4-bit MLX) cannot load while mlx-lm
0.32.0 lacks its architecture. Its repo ships the architecture as Python,
which Dessau never runs, so the issue waits for upstream support to be
released and then a pin bump.

The decision-model over-charge turned out to be the automatic served
window filling the budget, which every model gets. The maintainer folded
it into the decision-model spec rather than ship a stopgap, and the
issue's remedy, triage note and decision line now say so.

Assisted-by: Claude
The security review of the update check's LFS fix approved it and left
info notes. The content fetch's token strip is not sticky across a
leave-and-return chain, and the fetch follows an https-to-http hop. The
download path's redirect policy is laxer than the new fetch's. A CDN
refusal is also worded as a token problem. None blocked the fix; each is
filed with its remedy.

Assisted-by: Claude
@REPPL
REPPL enabled auto-merge October 7, 2026 12:01
@REPPL
REPPL added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit 5e4ec33 Oct 7, 2026
7 of 8 checks passed
@REPPL
REPPL deleted the integrate/fixes-2026-10-07 branch October 7, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant