Skip to content

fix(reconcile): rewrite pins in place, not by re-serializing the document - #9

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/reconcile-preserve-formatting
Aug 31, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
fix/reconcile-preserve-formatting

Conversation

@atrawog

@atrawog atrawog commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

The remedy the workflow rule prescribes for a version-mismatch warning is currently unusable.

charly box reconcile aligns cross-repo @github pins so a repo resolves to one version — the
git-workflow rule names it as the fix for a resolved to multiple versions warning. Run on a
160KB charly.yml, it changed the 4 pins it reported and 405 lines in total.

Pass 2 mutated the parsed scalar and then yaml.Marshal'd the whole document, round-tripping
every other node through the emitter. yaml.v3 does not reproduce a folded block scalar
(>-): it re-emits the body as one long line and drops the blank lines around it. Every
authored description: in the file was reflowed, across entities the reconcile had no reason to
touch:

-            Sole proof of the punktfunk CONTAINER venue and of the `punktfunk:` check verb
-            against a live host. Builds punktfunk-host on cachyos, deploys it with a headless
-            sway compositor, and runs the candy's baked package/key/host.env/wrapper checks
+            Sole proof of the punktfunk CONTAINER venue and of the `punktfunk:` check verb against a live host. Builds punktfunk-host on cachyos, …

A literal scalar (|-) round-trips byte-exactly, which is why the damage looks selective and
went unnoticed: files with only literal prose are unaffected.

The consequence is circular — the diff the remedy generates is itself a review blocker, and it
silently reformats prose someone wrote deliberately. So the warning it exists to clear cannot be
cleared with it.

What changed

A pin is always a single-line scalar, so the rewrite is now line-local: the original bytes
are retained from pass 1, and only the lines carrying a changed ref are touched. Every other byte
is preserved exactly.

Where the node position and the bytes disagree, applyPinEdits returns an error rather than
replacing the value somewhere else — a blind fallback would corrupt the file it was asked to
edit.

 candy/plugin-box/reconcile.go             |  52 +++++++++++---
 candy/plugin-box/reconcile_format_test.go | 115 ++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+), 8 deletions(-)

Generated from git diff --stat origin/main...HEAD.

How tested

$ go vet ./... && go test ./...
ok  	github.com/opencharly/plugin-box/candy/plugin-box

Executed live, on the file that exposed it. charly was built against this branch
(go mod edit -replace …/plugin-box/candy/plugin-box, then task build:binary; the binary
carries the new path — strings bin/charly | grep 'reported at line .* outside the' → 1 match),
and charly box reconcile was run on the same 160KB project. It reported the same 4 pins as
before the change:

before   2 files changed, 170 insertions(+), 235 deletions(-)
after    2 files changed,   4 insertions(+),   4 deletions(-)

and the after-diff is exactly the four pin rewrites, nothing else:

-            - '@github.com/opencharly/pod-pipewire:v2026.239.1555'
+            - '@github.com/opencharly/pod-pipewire:v2026.243.0508'
-            - '@github.com/opencharly/pod-sway:v2026.239.1611'
+            - '@github.com/opencharly/pod-sway:v2026.241.2004'
-            - '@github.com/opencharly/layer-xdg-portal:v2026.239.1637'
+            - '@github.com/opencharly/layer-xdg-portal:v2026.243.1016'
-            - '@github.com/opencharly/layer-punktfunk:v2026.241.2026'
+            - '@github.com/opencharly/layer-punktfunk:v2026.242.1658'

Three guards:

  • only the pin lines change — the property the fix exists to hold; it also asserts the line
    count is unchanged, so a dropped blank line fails it.
  • the round trip reflows authored prose — the negative control, pinning why the code
    exists. It logs the damage (647 bytes -> 610 bytes) and would skip itself if yaml.v3 ever
    round-tripped faithfully, so it cannot silently become vacuous.
  • a position mismatch refuses — line past the end, line zero, and a value not on the named
    line all return an error and no output.

The fixture uses a FOLDED scalar deliberately. An earlier revision of this test used a
literal one; yaml.v3 round-tripped it byte-exactly, so the negative control SKIPPED and the
suite passed while proving nothing. That is recorded here because the skip was invisible in
go test output without -v — the failure mode this repo should not repeat.

Rulebook compliance

  • R1 (RCA) — root-caused to yaml.v3's folded-scalar emission by round-tripping the real file
    and diffing, not by guessing; the literal-vs-folded distinction explains why the damage looked
    selective.
  • R3 (no duplication) — one rewrite path; the reporting (rewrites) is unchanged.
  • R4 (no workarounds) — the emitter is not configured or post-processed; the re-serialization
    is removed. No retries, no formatting cleanup pass.
  • R5 (delete legacy) — the yaml.Marshal write path and its now-unused fileChanged flag are
    removed in this PR, not deprecated.
  • R6 (git safety) — feature branch, no force-push, no hook bypass.
  • R7 / R10 (prove the gate) — go vet + full package tests on the committed tree, plus the
    changed code compiled into charly and run against the 160KB file with the resulting diff
    pasted above.

Change classification

  • Change class: plugin behaviour — how reconcile writes its result. The set of pins it
    selects, and its output, are unchanged.
  • Verification gate: go vet + go test ./... exit 0, three guards (one a negative
    control), and a live charly box reconcile on a real 160KB project reduced from 405 changed
    lines to 8.
  • Attribution tier: fully tested and validated — the changed path executed inside a built
    charly against the file that exposed the defect, the before/after diffs are recorded, and the
    guards fail without the change.

Assisted-by: Claude Code Anthropic Opus 5 (fully tested and validated)

…ment

`charly box reconcile` is what the workflow rule names as the remedy for a
`resolved to multiple versions` warning. Running it on a 160KB charly.yml changed
the 4 pins it reported -- and 405 lines in total.

Pass 2 mutated the parsed scalar and then yaml.Marshal'd the whole document, which
round-trips every OTHER node through the emitter. yaml.v3 does not reproduce a
FOLDED block scalar (`>-`): it re-emits the text as one long line and drops the
blank lines around it. So every authored `description:` in the file was reflowed,
across entities the reconcile had no reason to touch. A literal scalar (`|-`)
survives, which is why the damage looks selective and went unnoticed.

The effect is that the prescribed remedy is unusable: the diff it produces is
itself a review blocker, and it silently reformats prose someone wrote deliberately.

A pin is always a single-line scalar, so the rewrite is now line-local -- the
original bytes are retained from pass 1 and only the lines carrying a changed ref
are touched. Every other byte is preserved exactly. Where the node position and the
bytes disagree it returns an error instead of replacing the value somewhere else;
a blind fallback would corrupt the file it was asked to edit.

Measured on the same file that exposed it, same 4 pins:

  before   2 files changed, 170 insertions(+), 235 deletions(-)
  after    2 files changed,   4 insertions(+),   4 deletions(-)

Three guards. The fixture uses a FOLDED scalar because a literal one round-trips
byte-exactly -- an earlier revision of this test used a literal, so its negative
control SKIPPED and the suite proved nothing while passing.

Claude-Session: https://claude.ai/code/session_016fnPmYiC6ZkqXSi6D8PAP6
@github-actions

Copy link
Copy Markdown

Review — PASS

Head SHA: 3d0f91319db1 (branch: fix/reconcile-in-place-rewrite)

Security screen: PASS — no scope mismatch, no secrets/exfiltration, no weakened guardrails, no supply-chain changes; the change does exactly what the description claims (line-local pin rewrite replacing whole-document re-serialization).

Change class: plugin behaviour (Go code in candy/plugin-box).

Checklist: All applicable rules PASS or NA.

  • A1/A2/A3 — Body is complete, accounts for both files, names the gate (go vet + go test ./... + live reconcile), pastes coherent output, and ends with the exact *Assisted-by:* footer. Tier fully tested and validated is justified: the changed path (applyPinEdits) executed inside a rebuilt charly against the real 160KB file, with before/after diffs recorded and the binary path confirmed via strings. Diff stat (159+/8-, 2 files) matches meta.
  • B5 (R1) — RCA is real and specific (yaml.v3 folded-scalar reflow), not flake-framing; test output is ok with zero warnings.
  • B10 (R5) — the yaml.Marshal write path and fileChanged flag are removed in the same commit, not deprecated.
  • B12 (R10) — evidence is internally consistent; the change ships reconcile_format_test.go with three guards (only-pin-lines, negative control, position-mismatch refusal) that fail without the change.
  • B16 (Architecture) — placement: CORRECT. The reconcile write behavior is a capability living in the candy layer; no alias files, no new core→sdk imports, no Legacy identifiers.
  • B13/B17/B18 — N/A (no schema/candy-def changes, no destroy/rebuild, no generation changes); Go code is gofmt-clean with no visible lint issues.

No comments in the thread to adjudicate.

Verdict: PASS


ollama/deepseek-v4-flash — pi-review-action.

View action run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant