Port and deploy flake-update-cascade as a Go binary #159

Open
opened 2026-08-26 03:04:00 +01:00 by vnprc-agent · 0 comments
Contributor

Replace flake-update-cascade with a standard-library-only Go binary while preserving its fail-safe multi-repository update workflow, then deploy it and retire the Bash implementation.

Primary goals:

  • One tool, one finish line — this issue is done only when the Go implementation is deployed and verified on its real consumers and the Bash source is removed; it owns the remaining flake-update-cascade work split from issue allod/tools#98.
  • Preserve the safety boundary — all repositories pass preflight before any mutation, failures remain loud, and every failed update path restores the affected checkout and lock state it can prove it changed.
  • Preserve all three modes — direct, --pr, and --dry-run keep their current flags, output, exit behavior, multi-input semantics, branch handling, and one-combined-update-per-repository contract.
  • Close on retirement — implementation and consumer cutover PRs reference this issue; the final allod/tools PR that removes the verified Bash implementation carries the closing keyword.

Current state and contracts

flake/flake-update-cascade is a 513-line Bash state machine. It sources lib/workspace.sh, walks reachable direct pins through flake.lock, checks every repository before mutation, serializes per-repository runs with flock, and then drives nix, git, and optionally forge. It owns direct commits, dry-run temporary locks, PR branch creation and lease-protected updates, evaluation checks, and rollback paths.

The executable contract is spread across tests/flake/flake-update-cascade/*.sh, tests/flake/flake-update-cascade-multiple-inputs.sh, and tests/flake/flake-update-cascade-follows.sh; flake/README.md and flake/flake-update-cascade --help are the human-facing contract. All of those suites are in scope: the directory-only glob in the old migration plan misses the multiple-input and follows-collapse witnesses.

The deployed command is still assembled with writeShellApplication: allod/archetypes modules/dev-home-shared.nix:61-65 for development machines and allod/nexus nix/home.nix:54-58 for the host. Both wrappers prepend the shared workspace library. The Go implementation must preserve workspace_collect_repos discovery and ordering semantics while keeping nix, git, and forge as explicit subprocess boundaries.

Freeze current behavior at implementation start. Preserve multiple requested input names, reachable-pin and follows resolution, active-PR and protected-branch skips, dirty/branch/unpushed preflight failures, per-repository exclusion locking, combined updates, evaluation gates, deterministic output order, force-with-lease PR updates, and checkout restoration. The noninteractive foreign-nixConfig and interruptibility behavior repaired by issue allod/tools#143 is part of the contract. A correctness bug still open when implementation starts may diverge only with a regression test and an explicit PR-body note.

Validation and deployment

Parameterize every cascade suite with CASCADE_UNDER_TEST and run the same scenarios against Bash and Go. Compare stdout, stderr, exit status, command traces, resulting locks, branches, commits, and working-tree state. Add explicit witnesses for direct mode; dry-run immutability; preflight atomicity; multiple inputs; a pin collapsing to follows; Nix update failure; evaluation failure; push failure; timeout/interruption; lock contention; and restoration after every partially entered mutation path.

Before cutover, gofmt -l ., go vet ./..., go test ./..., all Bash oracle suites, all Go-target parity suites, nix build .#flake-update-cascade, and affected flake checks must pass. Consumer PRs switch only this command to buildGoModule and retain the Bash file for rollback. After the downstream lock updates and human rebuilds, verify that command -v flake-update-cascade resolves to the Go package, run a no-change dry run on a development machine and nexus, and exercise the mutating path in a disposable fixture repository. Remove the Bash source only after those witnesses pass.

Scope

In scope: the Go command and supporting internal packages in allod/tools; parity, state-transition, and rollback tests; the allod/tools package output; the allod/archetypes and allod/nexus package switches; downstream lock updates and rebuild verification; documentation adjustments required by packaging; and final Bash retirement.

Out of scope: flake-status, new update modes or flags, changing branch-protection policy, changing forge, unrelated workspace-tool migrations, and the report-validator work in issue allod/tools#142.

Replace `flake-update-cascade` with a standard-library-only Go binary while preserving its fail-safe multi-repository update workflow, then deploy it and retire the Bash implementation. Primary goals: - **One tool, one finish line** — this issue is done only when the Go implementation is deployed and verified on its real consumers and the Bash source is removed; it owns the remaining `flake-update-cascade` work split from issue allod/tools#98. - **Preserve the safety boundary** — all repositories pass preflight before any mutation, failures remain loud, and every failed update path restores the affected checkout and lock state it can prove it changed. - **Preserve all three modes** — direct, `--pr`, and `--dry-run` keep their current flags, output, exit behavior, multi-input semantics, branch handling, and one-combined-update-per-repository contract. - **Close on retirement** — implementation and consumer cutover PRs reference this issue; the final `allod/tools` PR that removes the verified Bash implementation carries the closing keyword. ### Current state and contracts `flake/flake-update-cascade` is a 513-line Bash state machine. It sources `lib/workspace.sh`, walks reachable direct pins through `flake.lock`, checks every repository before mutation, serializes per-repository runs with `flock`, and then drives `nix`, `git`, and optionally `forge`. It owns direct commits, dry-run temporary locks, PR branch creation and lease-protected updates, evaluation checks, and rollback paths. The executable contract is spread across `tests/flake/flake-update-cascade/*.sh`, `tests/flake/flake-update-cascade-multiple-inputs.sh`, and `tests/flake/flake-update-cascade-follows.sh`; `flake/README.md` and `flake/flake-update-cascade --help` are the human-facing contract. All of those suites are in scope: the directory-only glob in the old migration plan misses the multiple-input and follows-collapse witnesses. The deployed command is still assembled with `writeShellApplication`: `allod/archetypes` `modules/dev-home-shared.nix:61-65` for development machines and `allod/nexus` `nix/home.nix:54-58` for the host. Both wrappers prepend the shared workspace library. The Go implementation must preserve `workspace_collect_repos` discovery and ordering semantics while keeping `nix`, `git`, and `forge` as explicit subprocess boundaries. Freeze current behavior at implementation start. Preserve multiple requested input names, reachable-pin and `follows` resolution, active-PR and protected-branch skips, dirty/branch/unpushed preflight failures, per-repository exclusion locking, combined updates, evaluation gates, deterministic output order, force-with-lease PR updates, and checkout restoration. The noninteractive foreign-`nixConfig` and interruptibility behavior repaired by issue allod/tools#143 is part of the contract. A correctness bug still open when implementation starts may diverge only with a regression test and an explicit PR-body note. ### Validation and deployment Parameterize every cascade suite with `CASCADE_UNDER_TEST` and run the same scenarios against Bash and Go. Compare stdout, stderr, exit status, command traces, resulting locks, branches, commits, and working-tree state. Add explicit witnesses for direct mode; dry-run immutability; preflight atomicity; multiple inputs; a pin collapsing to `follows`; Nix update failure; evaluation failure; push failure; timeout/interruption; lock contention; and restoration after every partially entered mutation path. Before cutover, `gofmt -l .`, `go vet ./...`, `go test ./...`, all Bash oracle suites, all Go-target parity suites, `nix build .#flake-update-cascade`, and affected flake checks must pass. Consumer PRs switch only this command to `buildGoModule` and retain the Bash file for rollback. After the downstream lock updates and human rebuilds, verify that `command -v flake-update-cascade` resolves to the Go package, run a no-change dry run on a development machine and nexus, and exercise the mutating path in a disposable fixture repository. Remove the Bash source only after those witnesses pass. ### Scope In scope: the Go command and supporting internal packages in `allod/tools`; parity, state-transition, and rollback tests; the `allod/tools` package output; the `allod/archetypes` and `allod/nexus` package switches; downstream lock updates and rebuild verification; documentation adjustments required by packaging; and final Bash retirement. Out of scope: `flake-status`, new update modes or flags, changing branch-protection policy, changing `forge`, unrelated workspace-tool migrations, and the report-validator work in issue allod/tools#142.
Sign in to join this conversation.
No description provided.