Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ source layout, CLI surface, and state schema are stabilizing but may still chang

### Fixed

- **A FIFO at a managed destination no longer hangs `status`, `diff`, `explain`,
or `reconcile`'s drift walk and write-back.** (A directory there never hung — `os.ReadFile`
fails it immediately with `EISDIR` — and for `status` and `diff` nothing about
it changes; only `reconcile`'s write-back now names the shape rather than
reporting `EISDIR`.) `os.ReadFile` on a FIFO does
not fail — it blocks in the open waiting for a writer that never comes — so
the read's own error path never runs and the command never returns. Measured
on the previous release: a FIFO at a whole-file destination wedged `diff` and
`reconcile`, and a FIFO-shaped key-merge destination (a `~/.claude.json`,
say) additionally wedged `status`, which is advertised as read-only. Each had
to be killed.

`status` was already safe for the whole-file shape, because its `hashFile`
applied `render.IsRegularOrAbsent` and said it "shares render's predicate so
the destination-read guards cannot disagree about what is safe to read". That
was true of the hash and false of every other destination read. Every
destination read in `internal/cli` — `hashFile` included, which held a second
copy of the rule — now goes through one gate, `readDestBytes`, which refuses a
non-regular path before the open and lets an ABSENT one through to the read,
whose ENOENT is the answer every caller already handles. A `reconcile`
write-back (`[w]`) is covered too: with only the classification reads guarded,
a non-regular destination would classify as drift and then hang one keystroke
later.

**`apply`, `apply --dry-run`, `reconcile`'s `[o]verride`, `import <agent>` and
`doctor` are NOT fixed by this** and still hang on the same fixture — their reads are
in `internal/render` and the adapter `Ingest` paths, a far wider sweep.
`[o]verride` re-applies through `render.Writer.Write`, so it shares `apply`'s
unguarded read; the refusal message therefore points at removing or replacing
the file rather than at `[o]`, which would wedge. `doctor` reads no
destination itself but reaches one through its plugin check — a FIFO at
`~/.claude/settings.json` wedges it after it prints `Plugins`. Tracked as
[#241](https://github.com/spxrogers/agentsync/issues/241) and
[#242](https://github.com/spxrogers/agentsync/issues/242).

- **`agentsync check` no longer rejects a `[secrets].backend` that `apply`
accepts.** `secrets.SelectBackend` — the function `apply` actually resolves
through — lower-cases the backend name, but `check` compared it against the
Expand Down
13 changes: 12 additions & 1 deletion docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,18 @@ symmetric with the dest→source write boundary (see architecture §7).
this component KIND reclaimed at all — drives reconcile's prompt wording) and
`OrphanDeleteWillProceed` (will THIS destination actually be removed on this
run — keeps the apply summary from counting a skipped delete). `IsRegularOrAbsent`
is shared with `internal/cli`'s destination reads so a FIFO cannot block them.
is shared with `internal/cli`'s destination reads so a FIFO cannot block
**those**: every one of them goes through `readDestBytes`
(`internal/cli/destread.go`), which applies this predicate before the open.
`TestEveryDestinationReadGoesThroughTheGate` backs this up by failing on a
bare `os.ReadFile(op.Path)` anywhere under `internal/cli` — a two-spelling
text matcher, so it catches the copy-paste that happened rather than every
possible spelling.
It is **not** yet true of this package's own `Writer.Write` convergence read
or of the adapter `Ingest` paths, so `apply`, `apply --dry-run`,
`reconcile --auto-override` (which re-applies through `Writer.Write`),
`import <agent>` and `doctor` (through its plugin check) still block on a
non-regular destination — issues #241 and #242.
- **Depends on:** adapter, secrets, source, state, paths, iox, drift.
- **Files:** `pipeline.go`, `writer.go`, `state_apply.go`, `report.go`.

Expand Down
Loading
Loading