Skip to content

clodex patch --restore never inspects the live binary, so it can restore over a replaced install #231

Description

@bman654

This is #204's fix 1, which #204 deliberately did not implement — it shipped fix 2 (per-install
provenance records), because that is what closes the laundering case. The remaining gap is the one
fix 1 was for.

The gap

clodex patch's implicit restore extracts the source and checks for a /*ccpatch: marker before
deciding the live binary is patched. clodex patch --restore never looks at the live binary at
all
— it decides from the manifest and the provenance records and then copies.

So when the executable at one path is REPLACED with a different build of the same claude version, the
record still names that path, and its bytes are no longer the ones the record was written for. The old
backup is published over the new artifact.

Reproduced:

TWEAKCC_CC_INSTALLATION_PATH=/fixed/claude clodex patch
TWEAKCC_CC_INSTALLATION_PATH=/fixed/claude clodex patch --restore
rm /fixed/claude
cp /other-channel/claude-same-version /fixed/claude   # different bytes, same version
TWEAKCC_CC_INSTALLATION_PATH=/fixed/claude clodex patch --restore   # exit 0, old bytes published

How narrow it now is

#204 narrowed it rather than closing it. Two established records that disagree about one install
refuse, and so does a manifest that disagrees with a record and cannot be dated
(manifest.patchedSha256 !== liveSha256). What remains is the case where neither of those exists yet
— a single record, written before the replacement.

Normal reinstalls of one immutable channel produce identical bytes, so reaching this needs path reuse
across distinct artifacts or a same-version republish. The behaviour predates #204 through the
manifest; per-install records make it outlive the manifest.

What implementing it requires

Requiring either a manifest for this install or a /*ccpatch: marker in the live bytes closes it:
a replaced, pristine artifact has nothing to restore. readClaudeBundle is read-only, so a native
binary needs no copy and an npm install is plain JavaScript.

  • The per-format probe is mandatory (scripts/probe-patch-mechanism.mjs, Mach-O + ELF + PE) —
    this read path is executable-format-sensitive, and clodex patch was broken on every ELF build
    across two releases while macOS stayed green.
  • A fallback is required for when extraction fails, or a binary broken by a bad patch stops being
    rescuable — which is the case pristine backups exist for. The unprobeable-binary rescue path must
    keep working.

Documented under "Pristine backups are content-addressed" in .claude/docs/patcher.md.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions