Skip to content

marr update (bootstrap): skipping a drifted standard records local hash as baseline → intentional overrides silently reverted next update #121

@virtualian

Description

@virtualian

Summary

When marr update --project runs in bootstrap mode (no prior .marr-version.json) and the user skips a drifted standard to keep local edits, the post-update manifest records the local (edited) hash as that file's baseline. On the next marr update, the file is then classified modified (canonical advanced, user has not edited since baseline) and refreshed silently — discarding the intentional local override without a prompt.

Repro

  1. Project with a locally-customised prj-*-standard.md that diverges from canonical (no manifest yet → bootstrap).
  2. marr update --project → file is drifted → choose [s]kip to keep local edits.
  3. Manifest is written; the skipped file's baseline = its current local hash.
  4. marr update --project --check now reports the file as modified, not drifted.
  5. Next marr update --project (no --force) silently refreshes it to canonical.

Root cause

buildPostUpdateManifestbuildManifest(localStandardsDir, …) hashes the local file regardless of whether it was refreshed or skipped. So for a skipped-drifted file, baselineHash == localHash.

In compareManifest (utils/marr-manifest.js):

else if (localHash !== baselineHash) diff.drifted.push(name);   // user edit — prompted
else if (baselineHash !== canonicalHash) diff.modified.push(name); // canonical advanced — SILENT

A skipped-drifted file satisfies localHash === baselineHash and baselineHash !== canonicalHashmodified. In commands/update.js, modified goes into plan.refreshSilent and is applied with no prompt.

Impact

Intentional, ongoing local overrides (e.g. a project whose release workflow legitimately differs from the canonical standard) are silently reverted on the first subsequent update. The user's explicit "skip / keep mine" choice does not persist.

Suggested fix

Distinguish "in sync with an older canonical" from "intentionally diverged". Options:

  1. For a skipped drifted file, record the canonical hash as baseline (not the local hash). Then localHash !== baselineHash keeps it drifted → prompted on every update, never silently overwritten.
  2. Add an explicit per-file pin / detach flag in the manifest that excludes a file from silent refresh.
  3. Track both baseline and canonical_at_baseline so the classifier can tell whether the local file equals the canonical that was current when the user last decided.

Option 1 is the minimal change and matches user intent: a file the user edited away from canonical should stay drifted until they actively reconcile it.

Workaround

Hand-edit .marr-version.json to set the skipped file's baseline to the current canonical hash. It then stays drifted (prompted) on subsequent updates.

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