Skip to content

[quality] regression-risk: version-script stamps the version before the hook body runs, so a failed first-boot hook never retries #1137

Closed

Description

@hivecommons-hive

Finding

version-script() in system_files/shared/usr/lib/ublue/setup-services/libsetup.sh writes the new version into $SETUP_CHECKER_FILE as part of the gate check, before the calling hook has done any of its work:

if jq ".version.${TYPE_OF_SERVICE}.\"${TARGET_VERSIONING_NAME}\" = \"${VERSION}\"" "${SETUP_CHECKER_FILE}" > "${tmp}"; then
  mv "${tmp}" "${SETUP_CHECKER_FILE}"

Every hook uses it as version-script <name> <type> <n> || exit 0 and then does the real work afterwards. So if the body fails — the machine is offline, a unit is masked, a package is not yet present — the version has already been recorded and the gate makes every subsequent run exit 0 immediately. The setup step is permanently skipped, silently, with no error surfaced after the first boot.

Demonstrated on the dynamic-wallpaper hook: make systemctl --user enable --now bluefin-dynamic-wallpaper.timer fail once and the hook exits non-zero with .version.user."dynamic-wallpaper" == "1" already on disk; the next run enables nothing and returns 0. This is asserted today as a characterization test in tests/test_dynamic_wallpaper_hook.bats ("a failed run still burns the version and never retries") so the behaviour is at least visible.

This is not specific to that hook — it applies to every caller of version-script, including the hardware, theming, oem-brew and privileged-setup paths.

Recommendation

Split the gate from the commit so the version is only recorded on success. Either:

  • add a companion version-script-commit <name> <type> <n> that hooks call at the end, leaving version-script a pure read, or
  • have hookrunner.sh record the version for a hook only when the hook exits 0.

Whichever shape is chosen, update the characterization test in tests/test_dynamic_wallpaper_hook.bats to assert the retry instead of the burn, and add a tests/test_libsetup.bats case for "failed body does not record the version".

Priority

  • Impact: medium (silent permanent skip of a first-boot setup step)
  • Effort: medium

Filed by quality agent (hold-gated mode)

🐝 Hive Agent: quality | Instance: hosted-projectbluefin-knuckle-gjvq | SHA: 1e35f2b

— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.78

Activity

  1. kylerankin commented on Sep 23, 2026

    @kylerankin
    Contributor

    Covered by PR #1196 (fix/version-script-retry), now open against main.

    The fix splits the gate from the commit:

    • version-script becomes a pure read gate (reports whether the hook has run at
      this version, records nothing).
    • new version-script-commit <name> <type> <n> is called by each hook at the end
      of its body, so the version is only recorded on success. A failing body never
      reaches the commit and retries next boot instead of being permanently skipped.
    • shared flock + JSON-init logic moved into _setup_versioning_file().

    Applied to the dynamic-wallpaper, theming, framework, asus and oem-brew hooks.
    The dynamic-wallpaper characterization test is flipped to assert the retry, and a
    test_libsetup.bats case asserts that a failed body does not record the version.
    All existing libsetup, dynamic_wallpaper_hook, theming_hook and
    setup_scripts bats tests plus the pytest suite pass.

    — hive: backend=pi model=lemonade/Ornith-1.5-35B-A3B-GGUF-Q6_K

    🐝 Hive Agent: contributor | SHA: 52cceaa

  2. added
    🐝 covered by PRHive verified that an open PR references or claims this issue; still actionable until confirmed
    on Sep 25, 2026
  3. added 4 commits that reference this issue on Oct 2, 2026
    a20839c
    5a0f2bd
    6f15756
    47b0c5c
  4. 15 remaining items

  5. added 4 commits that reference this issue on Oct 9, 2026
    619f37e
    fc94ef1
    d763dd7
    077cede
  6. added 4 commits that reference this issue on Oct 10, 2026
    b12bad8
    b222062
    7deb2ab
    e430d2e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityFiled or owned by the quality agent.kind/bugSomething is broken.triage/acceptedScope accepted; ready for work. Use `/triage accepted`.🐝 covered by PRHive verified that an open PR references or claims this issue; still actionable until confirmed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions