Skip to content

v0.9.8 — v0.9.8 - the graphical installer can open its own window

Choose a tag to compare

@github-actions github-actions released this 22 Aug 13:03
· 163 commits to master since this release
8a85bb1

Added

  • recall calibration drift says whether the corpus under a live calibration has moved far
    enough to need refitting.
    Until now the drift question could only be asked after a rebuild:
    resolve compares fingerprints and answers STALE on any mismatch, which is a yes/no about
    identity rather than a statement about magnitude, and carry-forward needs a new generation to
    already exist. Nothing could be asked the question an operator actually has between rebuilds.

    Two tiers, and which one produced the verdict is always in the output. The screen is a
    manifest comparison over (uri, sha256): no embedding, no retrieval, not even a model load. The
    probe replays the calibration's own stored labelled query set and measures what the frozen
    threshold now costs, per class, by the same two conditions carry-forward enforces. ⛔ The
    screen firing is never reported as a verdict
    : where the probe cannot run the strongest verdict
    is recalibrate_recommended, and the report names the check that was not made, so a directory can
    never reach recalibrate_required however total its delta.

  • RECALL_AUTO_CALIBRATE and recall calibration auto re-establish a calibration without asking
    for labels.
    After a generation build: off does nothing, warn (the default) reports, auto
    additionally carries the threshold forward, or refits it on the same stored labelled evidence
    when it has to move. Neither path loosens certification, so the automation is in what gets
    run and never in what gets accepted, and neither invents questions: a tenant with no published
    calibration reports skipped, because deciding what the labelled questions should be is not a
    decision to make unattended.

  • recall calibration carry-forward and the corpus_delta / threshold_error_rates
    primitives, rescued from an unmerged branch and rebased onto master.

Changed

  • ⚠️ There is deliberately no corpus delta at which recalibration is demanded outright, and this
    reverses the design this feature started with.
    Measured over 57 snapshots of three real corpus
    histories (docs/preregistrations/2026-08-21-calibration-drift-trigger.md): the frozen threshold
    first crossed the 0.10 error bound at a delta of 0.945 and never below it, so a delta-only
    rule at 0.25 fires on 56 of 57 snapshots and is right about 5, a precision of 0.09.
    The labels also proved far more durable than that rule assumed: at delta 0.981 only 27.5% of
    the answerable queries' original evidence still existed and the false-abstain rate was 0.025.
    What moved was the false-confirm rate, tracking corpus growth rather than change as such.

    This does not license raising --max-corpus-delta on carry-forward, and it was left at 0.25.
    That bound governs whether a threshold may be inherited, and what the numbers establish is that
    a delta is a poor alarm, not that a large delta is safe.

  • recall uninstall also removes the desktop app's handoff file from the user config directory
    (%APPDATA%/RE-call/runtime.json), which the previous release looked for in the wrong place.
  • --selftest resolves the embedder only when a model cache already exists, and says which branch
    it took. It was downloading model weights on a cold cache.

Fixed

  • A desktop upload no longer silently shrinks the corpus. Carried-forward files that live
    outside the upload staging directory (which is every file a wizard install indexed) are kept and
    the build reader is widened to reach them. Only a file whose bytes are genuinely gone is dropped,
    and the count is now named in the upload's own message rather than passing unmentioned.
  • A failed docker compose down is reported. With the docker daemon unreachable, recall uninstall recorded no failure, printed Removed N item(s)., and deleted the stack file naming
    the containers that were still running — which then made them unnameable by the tool that left
    them. The stack file and wizard.json are now kept whenever the teardown fails, so the uninstall
    can be retried.
  • An uninstall no longer overwrites the install-time backup of the MCP client config. It writes
    its own under a name that is never reused, and copies the source file's mode rather than creating
    it at the umask default, since that file carries bearer tokens.
  • A volume the stack declared external is never removed. A fallback that derived the
    historical volume name could reinstate exactly the volume that had just been excluded as not
    ours. The fallback now applies only to a legacy stack that declares no volumes at all.
  • A failure inside build() no longer strands a generation. The desktop upload's cleanup path
    called only abandon, which refuses any state but ready, so failures before validate() left
    a full copy of the corpus that gc could not collect.
  • A blank or relative data folder is refused rather than silently becoming the process's
    working directory, and the terminal interview reports it as a refusal instead of a traceback.
  • corpus_version may no longer begin with desktop-. That prefix decides which generations
    the desktop upload path abandons; a wizard corpus carrying it would have been reclaimed.
  • The graphical installer could not open its own window, and 0.9.7 shipped that way.
    install_main called run_window(InstallerWindow(...)). Python evaluates an argument before the
    call, so the window was constructed before run_window had created the QApplication it needs.
    Qt answers a widget built with no application by printing QWidget: Must construct a QApplication before a QWidget and aborting the process on its fatal handler: one line of stderr, no window,
    no dialog, exit 0xC0000409. Every copy of the 0.9.7 installer did this, on every machine.
    run_app had the identical call shape, so the main desktop window carried the same defect.

    run_window now takes a factory rather than a window, so the application is created first and
    the ordering cannot be decided at a call site. Passing an already-built widget raises a TypeError
    naming the mistake instead of killing the process.

  • The self-test that exists to prove the bundle runs was green for that build, and now cannot
    be.
    --selftest constructed its own QApplication and then its own window, in the correct
    order, which is not the order the entry point used. It rehearsed a launch sequence nothing ships.
    Every desktop test in the suite had the same shape, and not by carelessness: once any test in a
    pytest session creates a QApplication it cannot be unmade, so no in-process test can observe a
    process that has none. The self-test now builds its window through application_and_window, the
    same code the entry point orders its launch with, and tests/test_desktop_launch_order.py adds a
    check that starts the real entry point in a fresh interpreter and asserts on the exit code,
    which is the only thing here that reproduces a double-click.