Skip to content

docs/auto-update.md documents an unconditional updater and never mentions the compatibility boundary that ships today #181

Description

@pablontiv

Description

Classification: documentation drift. The code is right; docs/auto-update.md is stale.

rootline 9.12.1 sets u.VersionPolicy = autoupdate.SameMajorOnly (cmd/rootline/main.go:21) and prints a stderr notice when a staged release crosses that boundary (cmd/rootline/main.go:36-38). That policy shipped for #118 in 2ccc7c0 ("fix(autoupdate): enforce compatibility boundary (#119)").

docs/auto-update.md — the page a user is sent to when the binary does not update — never mentions the boundary. Not in the flow diagram, not in the version-comparison table, not in the section literally titled When Auto-update Does NOT Run, not in Troubleshooting. Every claim on that page still describes the pre-#119 unconditional updater.

The consequence: after any major release, a user on the previous major sees rootline --version stop changing forever. The page's Troubleshooting section walks them through three causes (dev build, PATH shadowing, empty staging directory) and none of them is the real one — the staging directory is not empty in the withheld case, which is the exact opposite of the symptom step 3 describes.

Steps to Reproduce

Isolated fixture, no network, using the installed 9.12.1 binary as both the running binary and the staged payload (the payload's bytes are irrelevant — only the staged directory's version tag drives the policy):

FX=$(mktemp -d)
mkdir -p "$FX/home/Library/Caches/rootline/staged/v10.0.0" "$FX/bin" "$FX/work"
cp "$(command -v rootline)" "$FX/bin/rootline"
cp "$(command -v rootline)" "$FX/home/Library/Caches/rootline/staged/v10.0.0/rootline"
chmod +x "$FX/home/Library/Caches/rootline/staged/v10.0.0/rootline"

cd "$FX/work" && HOME="$FX/home" "$FX/bin/rootline" --version
ls "$FX/home/Library/Caches/rootline/staged/v10.0.0/"

Control — same fixture, only the staged tag changes from v10.0.0 to v9.99.0:

FX2=$(mktemp -d)
mkdir -p "$FX2/home/Library/Caches/rootline/staged/v9.99.0" "$FX2/bin" "$FX2/work"
cp "$(command -v rootline)" "$FX2/bin/rootline"
cp "$(command -v rootline)" "$FX2/home/Library/Caches/rootline/staged/v9.99.0/rootline"
chmod +x "$FX2/home/Library/Caches/rootline/staged/v9.99.0/rootline"

cd "$FX2/work" && HOME="$FX2/home" "$FX2/bin/rootline" --version
ls "$FX2/home/Library/Caches/rootline/staged/v9.99.0/"

Expected Behavior

Per docs/auto-update.md:11-13, ApplyStagedIfAvailable() has exactly two outcomes:

  1. ApplyStagedIfAvailable()  — sync: checks ~/.cache/rootline/staged/ for a newer binary
     → if found: atomic rename over current binary, re-exec (process is replaced)
     → if not found or not newer: continues normally

v10.0.0 is found and is newer, so per the documented flow it should be renamed over the current binary and re-exec'd, silently — docs/auto-update.md:3 promises the update is "transparent — no prompts, no interruptions, no downtime".

Actual Behavior

Cross-major staged release (v10.0.0) — withheld, and it says so on stderr:

rootline: incompatible update withheld: current 9.12.1, available v10.0.0; run the installer to upgrade deliberately
rootline version 9.12.1
EXIT:0
$ ls "$FX/home/Library/Caches/rootline/staged/v10.0.0/"
rootline          # 12M, still there — not applied, not removed

Control, same-major staged release (v9.99.0) — applied, silently:

rootline version 9.12.1
EXIT:0
$ ls "$FX2/home/Library/Caches/rootline/staged/v9.99.0/"
                  # empty — the staged binary was renamed over the running one

The control is what makes this a policy gate rather than a broken staging path: the only difference between the two runs is the version tag on the staging subdirectory. 9.99.0 > 9.12.1 is applied; 10.0.0 > 9.12.1 is refused. That is SameMajorOnly working exactly as #118 asked for it, and it is invisible on the page that documents this subsystem.

Affected documentation sites

file:line Claim Why it is now wrong
docs/auto-update.md:3 "The update is transparent — no prompts, no interruptions, no downtime." A withheld update writes a line to stderr (cmd/rootline/main.go:36-38). Not a prompt, but not silent either.
docs/auto-update.md:11-13 Two-branch flow: found+newer → apply; not-found-or-not-newer → continue Misses the third branch: found + newer + out of boundary → withheld, notice, staged file retained.
docs/auto-update.md:47 "isNewer then compares with a strict >" > is necessary but no longer sufficient. 10.0.0 > 9.12.1 and the update is still refused.
docs/auto-update.md:50-54 Replaced?-yes/no table, four rows, all within one major No row covers a major-version change, which is the only case where the answer changed.
docs/auto-update.md:60-64 "When Auto-update Does NOT Run" — one row, version == "dev" This is precisely where the boundary belongs, and it is the section's only entry.
docs/auto-update.md:78 "All auto-update errors are suppressed" The withheld condition comes back as an error value and is reported, not suppressed (cmd/rootline/main.go:32-39).
docs/auto-update.md:86-90 Troubleshooting "The binary is not updating" — three causes None is the boundary. Step 3 reads an empty staging directory as the symptom; in the withheld case the staging directory is populated and stays populated.

Provenance — how the page was missed

#118 asked for it explicitly: "Cover same-major allowance, cross-major withholding, and notice emission exactly once only for withheld updates. Update the documented auto-update behavior."

The implementing commit updated one of the two documentation surfaces:

$ git show --stat 2ccc7c0
fix(autoupdate): enforce compatibility boundary (#119)

 CLAUDE.md                       |  2 +-
 cmd/rootline/autoupdate_test.go | 77 +++++++++++++++++++++++++++++++++++++++++
 cmd/rootline/main.go            | 35 +++++++++++++++++--
 go.mod                          |  2 +-
 go.sum                          |  4 +--

CLAUDE.md:133 carries the full, correct policy today — same-major for stable, same-minor pre-1.0, retained-not-applied, stderr notice, deliberate-reinstall guidance. docs/auto-update.md was not in the commit and contains zero occurrences of withheld, compatibility, major-as-boundary, or VersionPolicy:

$ grep -ric "withheld\|compatibility\|SameMajorOnly\|VersionPolicy" docs/auto-update.md
0

So the fix is a one-pass edit of docs/auto-update.md to match what CLAUDE.md:133 already states. No code change is implied by this issue.

Environment

  • Rootline version: rootline version 9.12.1 (equal to master at 7d95e35)
  • OS: macOS (darwin/arm64)
  • Fixture: HOME-isolated temp dir, no network access, staged payload is a copy of the installed binary

Related work

Labels owed by a maintainer if --label documentation did not stick: documentation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions