Skip to content

Add the updater scaffolding and own the silent-check stamp - #271

Merged
turinglambdaai merged 8 commits into
mainfrom
feat/updater-scaffolding
Oct 17, 2026
Merged

turinglambdaai merged 8 commits into
mainfrom
feat/updater-scaffolding

Conversation

@turinglambdaai

Copy link
Copy Markdown
Owner

Closes #240 and #251.

Scaffolding (): a runtime binds the updater config, destination directory, settings bridge, and injectable transport hooks. fetches, selects, and drives the download while the phase box ( + percent + message + downloaded path) feeds the product's update-state event — the 109 duplicated lines across products collapse into one tested module, with the same extraction move as keel.

Throttle stamp (#251): the stamp is written only when a check settles — an answer or a definitive failure — so a threadly-macOS-style pre-check stamp (one offline launch = silent for 4 h) and the movebit no-stamp behavior both end. silent-check-due? reads through the product's settings adapter; hosts stop hand-rolling timestamps in three languages.

Acceptance for product deletions lands in the products as they rebase on the next rivet release.

Every product backend re-implemented the same updater flow: background
worker, phase state box for the RVT1 update-state event, error mapping,
and the silent-check throttle stamp — with three different write-time
semantics, one of which silenced four hours of launches after a single
offline start. The scaffolding module binds config, destination, and a
settings adapter; check and download settle synchronously against the
deadline-bounded updaters while the phase box feeds the RVT1 layer, and
transport hooks stay injectable for deterministic tests. The stamp is
written only when a check settles — answer or definitive failure — and
silent-check-due? reads it through the product's settings interface.
…ucket

Three defects the full-suite runs exposed:

1. The test helper sorted the keyword list without sorting the value
   list, so keyword-apply silently swapped #:fetch into #:settings' slot
   on every fetch-only call (the CI red on Racket 9.0/core). The helper
   now sorts (keyword value) pairs together; a second site used apply
   with a keyword list, feeding keywords as positional arguments —
   replaced with a direct keyword call.

2. The public-api expectation missed four exports of the new module
   (updater-runtime?, updater-settings-adapter?, and two accessors) and
   duplicated struct:updater-config, which made the list comparison fail
   no matter what shipped.

3. Two assertions disagreed with the module's real contract:
   downloaded_path is stored as a string (the test re-wrapped it in
   path->string), and the failure-settle scenario compared the stamp
   against an injected clock while the runtime stamps current-seconds.

resolve-rollout-bucket! and draw-rollout-bucket join the module (issue
#240's settings-interface proposal covers the bucket as well as the
throttle stamp): one 0-99 value per installation, drawn from OS entropy,
recovered on later runs, redrawn on corrupt stored values, degrading to
an unpersisted draw when the store cannot write. The writer binding
cannot be named set! — (set! key value) parses as the assignment form
and silently rebinds the parameter instead of calling the adapter.
@turinglambdaai
turinglambdaai force-pushed the feat/updater-scaffolding branch from 5c953ef to 6af1521 Compare October 17, 2026 02:35
@turinglambdaai

Copy link
Copy Markdown
Owner Author

Taking over the finishing pass (the branch had been idle since 2026-10-16 12:55 with two red Racket jobs). Pushed to this branch, rebased onto current main:

  1. The two CI reds: the scaffold test helper sorted the keyword-apply keyword list without sorting the value list, so every fetch-only call silently swapped #:fetch into #:settings' slot; a second site used apply with a keyword list (keywords became positional arguments). The public-api expectation also missed four exports of the new module and duplicated struct:updater-config, which fails the list comparison no matter what ships.

  2. Rollout bucket delivered (the gap noted in the closed duplicate PR Managed updater: own the silent-check throttle stamp and sticky rollout bucket #273): resolve-rollout-bucket! + draw-rollout-bucket — one 0-99 value per installation via the same settings adapter, OS-entropy draw, corrupt values redraw, non-persisting stores degrade gracefully. Documented in rivet.scrbl; public-api expectations updated.

  3. Test-suite fixes where assertions disagreed with the module's contract: downloaded_path is stored as a string, and the failure-settle scenario compares the stamp against the real clock the runtime stamps with.

Full suite green locally: 10002 tests. One gem from the fix pass: the writer binding inside resolve-rollout-bucket! cannot be named set! — (set! key value) parses as the assignment form and silently rebinds the parameter instead of calling the adapter.

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.

distribution: backend updater scaffolding module (109 lines duplicated verbatim across products)

1 participant