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
- Project with a locally-customised
prj-*-standard.md that diverges from canonical (no manifest yet → bootstrap).
marr update --project → file is drifted → choose [s]kip to keep local edits.
- Manifest is written; the skipped file's baseline = its current local hash.
marr update --project --check now reports the file as modified, not drifted.
- Next
marr update --project (no --force) silently refreshes it to canonical.
Root cause
buildPostUpdateManifest → buildManifest(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 !== canonicalHash → modified. 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:
- 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.
- Add an explicit per-file pin / detach flag in the manifest that excludes a file from silent refresh.
- 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.
Summary
When
marr update --projectruns in bootstrap mode (no prior.marr-version.json) and the user skips adriftedstandard to keep local edits, the post-update manifest records the local (edited) hash as that file's baseline. On the nextmarr update, the file is then classifiedmodified(canonical advanced, user has not edited since baseline) and refreshed silently — discarding the intentional local override without a prompt.Repro
prj-*-standard.mdthat diverges from canonical (no manifest yet → bootstrap).marr update --project→ file isdrifted→ choose [s]kip to keep local edits.marr update --project --checknow reports the file asmodified, notdrifted.marr update --project(no--force) silently refreshes it to canonical.Root cause
buildPostUpdateManifest→buildManifest(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):A skipped-drifted file satisfies
localHash === baselineHashandbaselineHash !== canonicalHash→modified. Incommands/update.js,modifiedgoes intoplan.refreshSilentand 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:
localHash !== baselineHashkeeps itdrifted→ prompted on every update, never silently overwritten.baselineandcanonical_at_baselineso 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
drifteduntil they actively reconcile it.Workaround
Hand-edit
.marr-version.jsonto set the skipped file's baseline to the current canonical hash. It then staysdrifted(prompted) on subsequent updates.