Port flake-update-cascade to Go #173

Merged
vnprc merged 2 commits from agent/go-flake-update-cascade into master 2026-09-08 18:20:32 +01:00
Member

Nothing changes for a running machine yet: no consumer references cmd/flake-update-cascade.
This adds the Go port while the Bash tool and every consumer wrapper stay identical.
The parity suites and nix flake check provide the validation evidence.
If the port is wrong, revert this PR's commits.

Risk

R3, per the migration plan's P3 cascade row. Residual risk is untested subprocess environments, not the logic covered by parity fixtures. Consumer cutovers and human rebuild validation follow in separate PRs after this merges; dependency ordering and pin propagation remain allod/tools#171.

Documented divergences and limits:

  • Update-path and repository ordering use byte order where Bash sorts by locale collation; these are identical for the workspace's names.
  • A malformed flake.lock ends the Go run with the file named and status 1; Bash dies with jq's parse error and status 2.
  • No automated timeout or Ctrl-C witness exists because the 120 s bound cannot be shortened without a test-only knob. The port keeps nix in the same process group so terminal SIGINT reaches it. The pty nixconfig suite pins the #143 non-interactivity contract for both programs.
  • The Bash bug is carried: an origin URL yielding no owner/repo aborts after the push with the checkout left on the PR branch, in both programs.

Validation

  • gofmt -l .
  • go vet ./...
  • go test ./...
  • All suites under tests/flake in oracle, Go (CASCADE_UNDER_TEST), and CASCADE_PARITY=1 modes, including the pty flake-update-cascade-nixconfig.sh. Mock-driven parity compares stdout, stderr, exit status, subprocess trace, and workspace tree; the real-nix pty suite applies the same assertions to both implementations because nix's pty stderr is not byte-stable.
  • nix build .#flake-update-cascade
  • nix flake check (cascade-parity, go-checks, allod-parity). The pty suite runs separately outside the sandbox, with TERM=xterm and NO_COLOR/NOCOLOR unset so Nix's prompt logger is active.

Refs allod/tools#159

Nothing changes for a running machine yet: no consumer references cmd/flake-update-cascade. This adds the Go port while the Bash tool and every consumer wrapper stay identical. The parity suites and nix flake check provide the validation evidence. If the port is wrong, revert this PR's commits. ## Risk R3, per the migration plan's P3 cascade row. Residual risk is untested subprocess environments, not the logic covered by parity fixtures. Consumer cutovers and human rebuild validation follow in separate PRs after this merges; dependency ordering and pin propagation remain allod/tools#171. Documented divergences and limits: - Update-path and repository ordering use byte order where Bash sorts by locale collation; these are identical for the workspace's names. - A malformed flake.lock ends the Go run with the file named and status 1; Bash dies with jq's parse error and status 2. - No automated timeout or Ctrl-C witness exists because the 120 s bound cannot be shortened without a test-only knob. The port keeps nix in the same process group so terminal SIGINT reaches it. The pty nixconfig suite pins the #143 non-interactivity contract for both programs. - The Bash bug is carried: an origin URL yielding no owner/repo aborts after the push with the checkout left on the PR branch, in both programs. ## Validation - `gofmt -l .` - `go vet ./...` - `go test ./...` - All suites under `tests/flake` in oracle, Go (`CASCADE_UNDER_TEST`), and `CASCADE_PARITY=1` modes, including the pty `flake-update-cascade-nixconfig.sh`. Mock-driven parity compares stdout, stderr, exit status, subprocess trace, and workspace tree; the real-nix pty suite applies the same assertions to both implementations because nix's pty stderr is not byte-stable. - `nix build .#flake-update-cascade` - `nix flake check` (`cascade-parity`, `go-checks`, `allod-parity`). The pty suite runs separately outside the sandbox, with `TERM=xterm` and `NO_COLOR`/`NOCOLOR` unset so Nix's prompt logger is active. Refs allod/tools#159
Add cmd/flake-update-cascade, a standard-library-only port of
flake/flake-update-cascade, and internal/flakelock for the lock-graph walk
that finds reachable direct pins and resolves revisions through follows
edges. The Bash program stays in place as the oracle: every suite under
tests/flake runs against whichever CASCADE_UNDER_TEST names, and with
CASCADE_PARITY=1 runs both on one fixture and requires identical stdout,
stderr, exit status, subprocess trace, and tree under the workspace.

The port is bug-for-bug, directory order included: the same git, nix and
forge invocations in the same sequence, the same per-repository flock in
/tmp, the same 120-second update bound with stdin from /dev/null, and the
same unguarded failures ending the run with the failing command's status,
including the abort after a push when the origin URL yields no owner/repo.
Dependency ordering and pin propagation are allod/tools#171.

New witnesses: failures.sh covers a failed nix update, a broken evaluation,
a blocked commit, a rejected push, a stale tracking ref, first-time PR
creation, a failed pull, an unguarded restore failure, and continuation to
the next repository; lock-contention.sh covers the held per-repository lock;
preflight.sh gains the active-pr-branches skip. Generated mocks now carry
the running bash's path so the suites run inside the Nix sandbox, where
nix flake check runs the mock-driven suites three ways as cascade-parity.

Refs allod/tools#159
Author
Member

The read-only review found two gaps to resolve before merging: the pty validation claim is not reproducible in this environment, and combined preflight errors differ from Bash. The branch remains unchanged.

  1. P2 — The pty witness fails before exercising either implementation. tests/flake/flake-update-cascade-nixconfig.sh:138 expects the raw update to block and exit 124, but Nix 2.34.8 returns 0 with ignoring untrusted flake configuration setting 'extra-substituters'. This reproduces in oracle, Go, and CASCADE_PARITY=1 modes, including with a terminal attached. Consequently the body claim that this suite pins #143 for both programs is not established by this review. Provide the environment and evidence for the earlier successful run, or revise the validation claim and resolve the lost witness according to the suite's own control-arm guidance.

  2. P2 — Combined preflight errors break stdout parity. cmd/flake-update-cascade/main.go:381 joins errors with ", ", while Bash's IFS=', '; echo "${repo_errors[*]}" joins with only the first IFS character, ",". A repository on feature with unstaged changes prints expected 'master'),dirty working tree in Bash and expected 'master'), dirty working tree in Go. The existing preflight suite exercises one error at a time and misses this. Restore the Bash separator and add the combined-error case to the parity suite.

Reproduction for finding 2, from the branch worktree with the Go package built:

source tests/flake/flake-update-cascade/testlib.sh
new_home review-combined-errors
write_direct_lock "$HOME/work/app"
export MOCK_SCENARIO=wrong-branch MOCK_FAIL_GIT='diff --quiet'
export CASCADE_UNDER_TEST=/nix/store/974ag1nci38l545qm8gygc7sl51izg1i-flake-update-cascade-0.1.0/bin/flake-update-cascade
export CASCADE_PARITY=1
run_cascade demo

This returns 99 with PARITY MISMATCH for stdout. Both implementations still refuse the operation.

Validation rerun: gofmt -l . (empty), go vet ./..., go test ./..., nix build .#flake-update-cascade, and nix flake check passed; Nix reused existing successful check outputs. All nine mock-driven suites under tests/flake passed in oracle, Go, and parity modes. The pty suite failed in all three modes as described above. The Bash source is unchanged, and the archetypes and nexus consumer wrappers still select it. No cutover or rebuild was performed.

The read-only review found two gaps to resolve before merging: the pty validation claim is not reproducible in this environment, and combined preflight errors differ from Bash. The branch remains unchanged. 1. **P2 — The pty witness fails before exercising either implementation.** `tests/flake/flake-update-cascade-nixconfig.sh:138` expects the raw update to block and exit 124, but Nix 2.34.8 returns 0 with `ignoring untrusted flake configuration setting 'extra-substituters'`. This reproduces in oracle, Go, and `CASCADE_PARITY=1` modes, including with a terminal attached. Consequently the body claim that this suite pins #143 for both programs is not established by this review. Provide the environment and evidence for the earlier successful run, or revise the validation claim and resolve the lost witness according to the suite's own control-arm guidance. 2. **P2 — Combined preflight errors break stdout parity.** `cmd/flake-update-cascade/main.go:381` joins errors with `", "`, while Bash's `IFS=', '; echo "${repo_errors[*]}"` joins with only the first IFS character, `","`. A repository on `feature` with unstaged changes prints `expected 'master'),dirty working tree` in Bash and `expected 'master'), dirty working tree` in Go. The existing preflight suite exercises one error at a time and misses this. Restore the Bash separator and add the combined-error case to the parity suite. Reproduction for finding 2, from the branch worktree with the Go package built: ```bash source tests/flake/flake-update-cascade/testlib.sh new_home review-combined-errors write_direct_lock "$HOME/work/app" export MOCK_SCENARIO=wrong-branch MOCK_FAIL_GIT='diff --quiet' export CASCADE_UNDER_TEST=/nix/store/974ag1nci38l545qm8gygc7sl51izg1i-flake-update-cascade-0.1.0/bin/flake-update-cascade export CASCADE_PARITY=1 run_cascade demo ``` This returns 99 with `PARITY MISMATCH` for stdout. Both implementations still refuse the operation. Validation rerun: `gofmt -l .` (empty), `go vet ./...`, `go test ./...`, `nix build .#flake-update-cascade`, and `nix flake check` passed; Nix reused existing successful check outputs. All nine mock-driven suites under `tests/flake` passed in oracle, Go, and parity modes. The pty suite failed in all three modes as described above. The Bash source is unchanged, and the archetypes and nexus consumer wrappers still select it. No cutover or rebuild was performed.
Author
Member

Owner request: implement the entire issue and stop only at human gates. I will resolve the review findings and complete agent-runnable implementation and validation before the implementation merge gate; consumer cutovers follow its merge, and Bash retirement follows the required human rebuild and live verification.

Owner request: implement the entire issue and stop only at human gates. I will resolve the review findings and complete agent-runnable implementation and validation before the implementation merge gate; consumer cutovers follow its merge, and Bash retirement follows the required human rebuild and live verification.
Author
Member

Both review findings are addressed in cf09348.

  • Combined preflight errors now use Bash's comma-only separator. The new combined-error case asserts exit status 1 as well as the diagnostic, so a parity mismatch (99) cannot pass merely because the diff contains the expected words. It passes against both implementations and rejects the original Go binary.
  • The pty fixture now sets TERM=xterm and unsets NO_COLOR and NOCOLOR for both arms. Nix 2.34.8 uses those variables to enable its interactive logger; the runner environment had disabled it. The raw control now prompts and times out as required, and both cascade implementations decline the foreign configuration and complete. The test retains its failing control-arm assertion.

Upstream condition: https://github.com/NixOS/nix/blob/2.34.8/src/libutil/terminal.cc#L77-L82

All ten cascade suites, including nixconfig, passed in oracle, Go, and parity modes (30 suite/mode pairs). The final strengthened preflight case was also rerun in all three modes. Formatting, vet, Go unit tests, the package build, and the final nix flake check passed, including cascade-parity, go-checks, and allod-parity. A focused read-only subagent review of cf09348 found no further issues in these fixes.

Ready for the human implementation merge gate. Running machines still use the unchanged Bash wrappers; consumer cutover PRs follow this merge, then human rebuild and live verification precede Bash retirement. The previously documented ordering/malformed-lock divergences, timeout/interrupt witness limitation, and carried origin-parsing bug remain explicit in the PR body.

Both review findings are addressed in cf09348. - Combined preflight errors now use Bash's comma-only separator. The new combined-error case asserts exit status 1 as well as the diagnostic, so a parity mismatch (99) cannot pass merely because the diff contains the expected words. It passes against both implementations and rejects the original Go binary. - The pty fixture now sets `TERM=xterm` and unsets `NO_COLOR` and `NOCOLOR` for both arms. Nix 2.34.8 uses those variables to enable its interactive logger; the runner environment had disabled it. The raw control now prompts and times out as required, and both cascade implementations decline the foreign configuration and complete. The test retains its failing control-arm assertion. Upstream condition: https://github.com/NixOS/nix/blob/2.34.8/src/libutil/terminal.cc#L77-L82 All ten cascade suites, including nixconfig, passed in oracle, Go, and parity modes (30 suite/mode pairs). The final strengthened preflight case was also rerun in all three modes. Formatting, vet, Go unit tests, the package build, and the final nix flake check passed, including cascade-parity, go-checks, and allod-parity. A focused read-only subagent review of cf09348 found no further issues in these fixes. Ready for the human implementation merge gate. Running machines still use the unchanged Bash wrappers; consumer cutover PRs follow this merge, then human rebuild and live verification precede Bash retirement. The previously documented ordering/malformed-lock divergences, timeout/interrupt witness limitation, and carried origin-parsing bug remain explicit in the PR body.
vnprc approved these changes 2026-09-08 18:20:26 +01:00
vnprc merged commit cf09348cf3 into master 2026-09-08 18:20:32 +01:00
vnprc deleted branch agent/go-flake-update-cascade 2026-09-08 18:20:32 +01:00
Sign in to join this conversation.
No description provided.