Skip to content

feat(installer): tee'd log, failure report, pull retry, --summary-json - #2243

Draft
thinmintdev wants to merge 3 commits into
mainfrom
feat/installer-forensics
Draft

thinmintdev wants to merge 3 commits into
mainfrom
feat/installer-forensics

Conversation

@thinmintdev

Copy link
Copy Markdown
Contributor

Summary

Ports ODS's installer forensics shapes into hal0's install.sh: module
headers, a tee'd install log, a redacted failure report on abort, per-step
time estimates + measured durations, a --summary-json machine-readable
summary, and backoff/classification for container-image pulls (bash side
in installer/lib/pull-retry.sh, Python side in
hal0.registry.runner_pull). hal0 doctor bundle now includes the latest
install log. See installer/README.md's new "Installer forensics" section
and docs/getting-started/install.mdx for full details.

Risk grade

  • med — user-facing change, new code path, or non-trivial refactor

Touched surfaces

  • Installer (installer/, systemd units)
  • Docs (docs/, CONTRIBUTING.md)

§14.1 high-risk surfaces

None checked — no shell-out to new untrusted input, no download/signature
changes, no privilege changes. The pull-retry paths retry an
already-authorized podman pull/runner-image pull with the same image ref;
they add backoff and a stricter non-retryable classifier, nothing new is
executed.

Rollback

Revert this PR's 3 commits; no schema/migration/on-disk-format changes to
unwind (the install log and failure report are new files under
/var/log/hal0 or /tmp, and --summary-json is opt-in).

Rollback: git revert <merge-commit>

Test tiers run

  • α unit (make test) — uv run pytest tests/ -q -n 8
  • β integration (make test-integration)
  • γ release-gate (make release-test)

Also run and green: uv run ruff check src tests, uv run ruff format --check src tests, uv run mypy on every file this PR touches,
python3 scripts/check_sunset.py (scar baseline unchanged at 193),
shellcheck on every .sh touched (clean apart from pre-existing
SC1091/SC2034-cross-file/SC2119/SC2120/SC2069 baseline noise).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bpxd3dvWgxPymLQLtmtujs

thinmintdev and others added 3 commits September 5, 2026 12:52
…ht.sh, run-as-hal0.sh

Adopt the Purpose / Expects / Provides / Modder notes convention (ported
from ODS's installer/lib module headers) on the three installer/lib files
that did not yet carry it, so every sourced installer module documents the
same four facts a reader (or a future step-extraction pass) needs: what
it's for, what has to be true before sourcing it, what it exports, and
what to know before changing it. Content-only — no behavior change.

installer/README.md documents the convention (added in a follow-up commit
that also touches install.sh and lib/ui.sh, where the header sits among
this brief's other changes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bpxd3dvWgxPymLQLtmtujs
Signed-off-by: thinmintdev <alexander@awideweb.com>
…line, step timing, --summary-json

Ports ODS's installer forensics shapes (module headers already landed in
the previous commit; this lands the behavior) into hal0's install.sh:

- Tee'd install log (installer/lib/logging.sh): every narrator line and
  spawned command's stdout/stderr is teed to
  /var/log/hal0/install-<ts>.log (falling back to /tmp/hal0-install-<ts>.log
  when not root). The path is printed at the start and end of the run.
  Degrades to no log, never aborts the install, if neither location is
  writable.

- Failure report (installer/lib/failure-report.sh): install.sh's ERR trap
  now also writes hal0-install-report-<ts>.txt next to the install log —
  a redacted environment dump (bash mirror of hal0.api._redact's
  key-name pattern), the owner of the hal0-api/OpenWebUI ports (ss -ltnp),
  systemctl status of the hal0 units, /etc/hal0/hardware.json, and the
  last 200 lines of the install log — and prints its path alongside the
  existing step-specific recovery advice.

- Pull retry discipline (installer/lib/pull-retry.sh + Python side in
  hal0.registry.runner_pull): the OpenWebUI background warm-cache pull in
  install.sh and the dashboard's runner-image pull job now retry
  transient failures with backoff (default 5/15/30/60s, doubling past the
  table, HAL0_PULL_RETRY_DELAYS / HAL0_PULL_MAX_ATTEMPTS), classify
  auth/404/manifest-unknown/disk-full failures as non-retryable so they
  fail fast, and verify the image actually landed in local storage before
  trusting an apparent success. run_runner_pull's own default
  (max_attempts=1) preserves every existing direct caller's behavior
  byte-for-byte; hal0.registry.runner_pull_jobs.enqueue opts the real
  dashboard-triggered pull path into 4 attempts.

- Per-step timing: ui_step gained an optional estimate argument
  (`ui_step "Title" "~30s"`, printed as "EST. TIME: ..."), every one of
  the 16 existing steps now carries an honest range, and ui.sh measures
  real per-step wall-clock time into UI_STEP_DURATIONS (ui_step_finalize
  closes out the last step). Total elapsed time is printed at the end.

- --summary-json=PATH: writes a versioned (schema hal0.install-summary.v1),
  atomically-written (tempfile + fsync + os.replace) JSON summary —
  versions, dev/no-start flags, network bind info, hardware class, the
  brain model pulled, warning/error counts, and per-step durations. No
  auth-posture field: teammate w0a's auth-posture summary line has not
  landed on main yet, and hal0 has no ODS-style "tier"/"lane" concept, so
  hardware_class{id,label} (from /etc/hal0/hardware.json) stands in for
  ODS's tier field.

- `hal0 doctor bundle` now includes the latest install log (redacted,
  tail-capped) under logs/install.log, picked by mtime across both the
  root and --dev/non-root log locations.

install.sh and installer/lib/ui.sh also gain the module header convention
(Purpose / Expects / Provides / Modder notes), matching the previous
commit's lib/distro.sh, preflight.sh, run-as-hal0.sh.

Fixes two pre-existing tests whose regex assumed a ui_step call had no
second argument (test_prebuilt_ui_install.py's Node.js/Dashboard UI block
extraction) — now tolerant of the trailing estimate.

UI_STEP_TOTAL stays 16 — no step was added or removed, only instrumented.

Verified: bash -n and shellcheck clean on every touched .sh (remaining
warnings are pre-existing SC1091/SC2034-cross-file/SC2119/SC2120/SC2069
baseline noise); ruff check/format clean; mypy clean on every file this
commit touches (doctor_bundle.py's one pre-existing List[Diagnosis] error
predates this change); python3 scripts/check_sunset.py unchanged at
baseline 193.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bpxd3dvWgxPymLQLtmtujs
Signed-off-by: thinmintdev <alexander@awideweb.com>
- installer/README.md: new "Installer forensics" section covering the
  module header convention, the install log, the failure report,
  --summary-json=PATH (with a full example payload), and pull retry
  discipline; adds HAL0_PULL_MAX_ATTEMPTS / HAL0_PULL_RETRY_DELAYS to the
  environment variable table.
- docs/getting-started/install.mdx: --summary-json in the flags table plus
  a short "Install log and failure reports" / "--summary-json" pair of
  subsections.
- README.md: --summary-json added to the "Common install knobs" table.
- CHANGELOG.md: Unreleased ### Added entry (this Unreleased section had
  no ### Added subsection yet — added it ahead of the existing ### Fixed,
  matching Keep a Changelog's category ordering).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bpxd3dvWgxPymLQLtmtujs
Signed-off-by: thinmintdev <alexander@awideweb.com>
present = None # inconclusive — don't punish a flaky verifier
if present is False:
last_error = f"pull reported success but {job.image_ref} is not in local storage"
outcome = "failed"

This branch has not been deployed

No deployments
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