reconcile classifies content only, so a permission-bit drift that status and diff report (and, after #229 PR-C, explain too) is invisible to it. The one command whose job is resolving drift offers no way to resolve this one.
Repro. Apply a managed file, chmod it, then:
$ agentsync status --json → {"items":[{"path":".../CLAUDE.md","class":"drift"}],"summary":{"drift":1}}
$ agentsync diff --json → {"hunks":[{"pointer":"mode","source":"mode 0644","dest":"mode 0600"}]}
$ agentsync reconcile → nothing to reconcile
Measured at the builder level on 7f2d4ad: status summary drift:1; collectReconcileItems yields one item with cls=clean; needsPrompt=0.
Where. internal/cli/reconcile.go — collectReconcileItems projects the walk's content class and never consults planItem.opModeDrifted(); requiresAction switches on the content class alone; the run loop counts requiresAction(it.cls) || it.orphan, so the item never reaches a prompt. status --exit-code fails a CI gate on this file while reconcile says there is nothing to do.
Expected. Either reconcile offers the item, or it says why it cannot.
Why #229 did not fix it. Deliberate (plan §7.2). Making it prompt needs a design for what [w]rite-back means for a permission bit: the canonical model has no mode field for most components, so "adopt the destination's mode" has nowhere to land. [o]verride is already well defined (re-apply → render.Writer.Write chmods to op.Mode), so a one-sided prompt may be the answer. That is a UX decision, not a walk fix, and belongs outside a behavior-preserving refactor.
Related: #229 (the shared walk now carries opModeDrifted for every whole-file item, so the data is there).
reconcileclassifies content only, so a permission-bit drift thatstatusanddiffreport (and, after #229 PR-C,explaintoo) is invisible to it. The one command whose job is resolving drift offers no way to resolve this one.Repro. Apply a managed file,
chmodit, then:Measured at the builder level on
7f2d4ad:statussummarydrift:1;collectReconcileItemsyields one item withcls=clean;needsPrompt=0.Where.
internal/cli/reconcile.go—collectReconcileItemsprojects the walk's content class and never consultsplanItem.opModeDrifted();requiresActionswitches on the content class alone; the run loop countsrequiresAction(it.cls) || it.orphan, so the item never reaches a prompt.status --exit-codefails a CI gate on this file whilereconcilesays there is nothing to do.Expected. Either
reconcileoffers the item, or it says why it cannot.Why #229 did not fix it. Deliberate (plan §7.2). Making it prompt needs a design for what
[w]rite-backmeans for a permission bit: the canonical model has no mode field for most components, so "adopt the destination's mode" has nowhere to land.[o]verrideis already well defined (re-apply →render.Writer.Writechmods toop.Mode), so a one-sided prompt may be the answer. That is a UX decision, not a walk fix, and belongs outside a behavior-preserving refactor.Related: #229 (the shared walk now carries
opModeDriftedfor every whole-file item, so the data is there).