Skip to content

fix: account for install source and replaced files - #595

Merged
chubes4 merged 1 commit into
mainfrom
fix/claude-cli-version-drift
Sep 15, 2026
Merged

chubes4 merged 1 commit into
mainfrom
fix/claude-cli-version-drift

Conversation

@chubes4

@chubes4 chubes4 commented Sep 15, 2026

Copy link
Copy Markdown
Member

Problem

Every managed file this tool writes to a site is copied out of $SCRIPT_DIR. That is not a release tarball — it is the operator's working checkout, and whatever is in that working tree when setup.sh or upgrade.sh runs is what lands on the site. Two failure modes follow, and both were silent.

Stale source. A checkout parked on an old feature branch faithfully reinstalls artifacts predating fixes that landed on the default branch months ago, and the operator sees a successful upgrade.

Observed in the wild: a site pinned to claude-cli/2.1.75 by repeated upgrades run from a checkout 79 commits behind main, long after b6ca654 shipped 2.1.259 on main. Anthropic then rejected the session:

Claude Code 2.1.75 does not support this model; version 2.1.251 or newer
is required. Run 'claude update' ... (400)

The error points at the site and at the locally installed claude binary (which was current, at 2.1.269). Neither was the cause. The installer was. There was no signal anywhere in the upgrade output that the artifacts came from a tree 79 commits stale.

Clobber. A managed file hand-edited on the site was overwritten with no backup and no notice, so the edit evaporated on the next run. In the case above this compounded the first problem: manually bumping the site's plugin appeared to work, then silently reverted on the next upgrade.

Neither is fixed by pinning a better value somewhere. They are fixed by making the installer honest about where its inputs came from and what it destroyed.

Change

New lib/install-source.sh with two public functions.

install_source_report_integrity runs at the top of setup.sh and upgrade.sh. Against the checkout described above it now prints:

[wp-coding-agents] Installing from a working checkout that is not clean main:
  source:  /path/to/wp-coding-agents
  branch:  feat/544-local-wordpress-service (default is main)
  behind:  79 commit(s) behind origin/main as of the last fetch
  dirty:   2 uncommitted path(s)
  Managed files installed by this run come from this tree, not from origin/main.

It warns; it never aborts. Developing wp-coding-agents means running setup.sh and upgrade.sh from a feature-branch worktree on purpose, and a hard failure there would break the tool's own development loop. The defect was never that operators install from a branch — it is that they could not tell they had. No fetch is performed: setup and upgrade should not acquire a network dependency to report on local state, and the warning says the behind-count is measured as of the last fetch.

install_source_sync_managed_file <source> <dest> <label> copies a managed artifact while preserving anything it would destroy. Identical is a no-op with no output and no UPDATED_ITEMS entry; a new destination is a plain install; a diverged destination is backed up to .backup.<ts> alongside, then replaced, with both the install and the backup path reported.

The backup is unconditional on divergence rather than gated on a heuristic for "was this a local edit". There is no reliable way to distinguish an operator's hand-edit from an artifact left by an older release, and guessing wrong destroys work. A cheap redundant backup is the correct trade, and the AGENTS.md path in this repo already settled on the same convention.

Both Claude Code auth plugin installers now route through the helper, replacing a bare cp in runtimes/opencode.sh and a cmp-only no-op guard in upgrade.sh that still overwrote local edits whenever content differed.

Verification

Two new tests drive real git checkouts and a real temp filesystem, asserting resulting bytes on disk:

  • tests/install-source-managed-file-sync.sh — fresh install, identical re-run is a true no-op (mtime unchanged), diverged destination is recoverable from the backup and the backup path is reported, dry run changes nothing, missing source does not clobber.
  • tests/install-source-integrity-report.sh — clean default branch is silent, behind-default-branch is reported by name and count, uncommitted paths are reported, a non-git source is silent rather than fatal.

Full shell suite green. tests/opencode-local-plugin-path.sh caught a genuine break from this change (isolated sourcing of runtimes/opencode.sh without the new lib) and is fixed here; it already asserts the plugin is really installed to the site path, so it covers the changed path end to end. tests/ci-coverage.sh caught that the new tests were not wired into CI; added to shell.yml.

tests/datamachine-worker.sh fails locally on an absolute wp path, and fails identically on origin/main — pre-existing and unrelated.

shellcheck is clean on all new files.

Note for reviewers

tests/upgrade-opencode-auth-plugin-sync.sh asserts that string literals such as source_path="$SCRIPT_DIR/..." appear in upgrade.sh. It passes while the installer ships a months-stale artifact, because it never inspects installed output. Left unchanged as out of scope, but it is not covering the behavior its name implies.

AI assistance

Investigated and implemented with Claude (claude-sonnet-4-6) via opencode. The model traced the 400 to the spoofed user-agent header, established that the version pin was already fixed on main and that the install source was a stale checkout, designed and wrote lib/install-source.sh and both test files, and ran the full shell suite. Reviewed and directed by @chubes4.

Managed files are copied out of $SCRIPT_DIR, which is the operator's
working checkout rather than a release artifact. Two silent failures
followed from that.

A checkout parked on an old branch reinstalls stale artifacts on every
run while reporting success. A site was pinned to claude-cli/2.1.75 by
repeated upgrades from a checkout 79 commits behind main, long after
main shipped 2.1.259, and Anthropic then rejected the session for an
unsupported client version.

A managed file edited on the site was overwritten with no backup and no
notice, so the edit was unrecoverable.

Add lib/install-source.sh. install_source_report_integrity describes the
install source at the top of setup and upgrade when it is not a clean
default branch. It warns rather than aborts, because running from a
feature worktree is how this tool is developed; the defect was that the
operator could not tell. install_source_sync_managed_file backs a
diverged destination up to .backup.<ts> before replacing it and reports
the backup path, matching the convention already used for AGENTS.md.

Route both Claude Code auth plugin installers through the helper,
replacing a bare cp in the setup path and a cmp-only no-op guard in the
upgrade path.

Cover both behaviors against real git checkouts and a real filesystem.
@chubes4
chubes4 merged commit 5cea500 into main Sep 15, 2026
69 checks passed
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