Port the PM board tool to Go as allod pm (first bash-to-Go carve-off) #103

Open
opened 2026-07-16 21:17:09 +01:00 by vnprc-agent · 3 comments
Contributor

User story: So that I can refresh and reorganize my project board with one first-class command instead of a pile of coupled bash scripts, I want the PM board tooling reimplemented in Go as allod pm — the pilot for moving the workspace CLI off bash.

Part of allod/tools#98.

Context

There is a working PM board toolkit: a JSON overlay that references issues by owner/repo#num, a deterministic renderer that joins it with live forge state into a self-contained HTML board, an integrity checker (schema + ref resolution + overlay purity), and a wrapper that refreshes and grooms it. It works, but it is a set of coupled bash/POSIX scripts. This issue carves it off as the first concrete step of the bash→Go migration — reimplement it as a first-class allod pm subcommand, establishing the Go patterns the rest of the migration reuses.

Scope

Reimplement the toolkit as allod pm:

  • allod pm — usage.
  • allod pm refresh — validate the overlay, re-render the HTML board from live forge state, commit. Deterministic; no agent.
  • allod pm groom [--dry-run] — run the grooming agent to rebuild the overlay from current forge state (add new issues, drop closed, re-prioritize), then refresh. --dry-run previews without committing.
  • Fold the current renderer and integrity-checker logic into the Go tool: JSON-schema validation, forge issue-ref resolution, and self-contained rendering (no JS, no external refs, forge text HTML-escaped). This is the bulk of the work — it is not just the dispatcher.
  • Config-driven, not hardcoded: the board location and the managed repo list come from config, so this public tool assumes no particular repo layout.

Boundaries

  • This is the tool only; the board data (the overlay and the triage policy) stays as the operator's own config/data, not in this repo.
  • refresh and groom are read-only on the forge; groom writes only the operator's overlay file.
  • Land it after the bash version's ergonomics have stabilized in use, so the Go port is not chasing a moving design.

Validation

  • allod pm refresh reproduces the existing renderer's output byte-for-byte on a fixture overlay + issue snapshot.
  • Schema and purity checks match the existing integrity checker (a cached issue-fact field must still fail validation).
  • The rendered HTML asserts self-contained: no <script>, no external URLs, forge-sourced text escaped.

Part of allod/tools#98.

**User story:** So that I can refresh and reorganize my project board with one first-class command instead of a pile of coupled bash scripts, I want the PM board tooling reimplemented in Go as `allod pm` — the pilot for moving the workspace CLI off bash. _Part of allod/tools#98._ ## Context There is a working PM board toolkit: a JSON overlay that references issues by `owner/repo#num`, a deterministic renderer that joins it with live forge state into a self-contained HTML board, an integrity checker (schema + ref resolution + overlay purity), and a wrapper that refreshes and grooms it. It works, but it is a set of coupled bash/POSIX scripts. This issue carves it off as the first concrete step of the bash→Go migration — reimplement it as a first-class `allod pm` subcommand, establishing the Go patterns the rest of the migration reuses. ## Scope Reimplement the toolkit as `allod pm`: - `allod pm` — usage. - `allod pm refresh` — validate the overlay, re-render the HTML board from live forge state, commit. Deterministic; no agent. - `allod pm groom [--dry-run]` — run the grooming agent to rebuild the overlay from current forge state (add new issues, drop closed, re-prioritize), then refresh. `--dry-run` previews without committing. - Fold the current renderer and integrity-checker logic into the Go tool: JSON-schema validation, forge issue-ref resolution, and self-contained rendering (no JS, no external refs, forge text HTML-escaped). This is the bulk of the work — it is not just the dispatcher. - Config-driven, not hardcoded: the board location and the managed repo list come from config, so this public tool assumes no particular repo layout. ## Boundaries - This is the tool only; the board data (the overlay and the triage policy) stays as the operator's own config/data, not in this repo. - `refresh` and `groom` are read-only on the forge; `groom` writes only the operator's overlay file. - Land it after the bash version's ergonomics have stabilized in use, so the Go port is not chasing a moving design. ## Validation - `allod pm refresh` reproduces the existing renderer's output byte-for-byte on a fixture overlay + issue snapshot. - Schema and purity checks match the existing integrity checker (a cached issue-fact field must still fail validation). - The rendered HTML asserts self-contained: no `<script>`, no external URLs, forge-sourced text escaped. _Part of allod/tools#98._
Author
Contributor

Design note for the Go port — keep groom --dry-run as a review-without-re-running mode.

The groom agent writes the rebuilt overlay to the working tree; --dry-run only withholds the git commit. So after reviewing the diff you finalize the already-produced overlay without re-invoking the agent — a second LLM run is never needed to approve. The bash version pairs this with commit (re-render + commit the pending overlay) and discard (revert it).

Preserve in Go: the agent's output persists to the working tree, --dry-run withholds only the commit, and commit/discard finalize it — so review is free.

Design note for the Go port — keep `groom --dry-run` as a **review-without-re-running** mode. The groom agent writes the rebuilt overlay to the working tree; `--dry-run` only withholds the `git commit`. So after reviewing the diff you finalize the *already-produced* overlay without re-invoking the agent — a second LLM run is never needed to approve. The bash version pairs this with `commit` (re-render + commit the pending overlay) and `discard` (revert it). Preserve in Go: the agent's output persists to the working tree, `--dry-run` withholds only the commit, and `commit`/`discard` finalize it — so review is free.
Author
Contributor

Status (2026-07-17): the bash version has landed as allod pm (the pm/ tree in this repo) and is packaged into the store, so the "land after the bash ergonomics stabilize" boundary is essentially met — the Go port can start against a stable reference.

Two points for the dev-plan / review pass that the body doesn't yet cover:

1. Build structure — the main open decision. This repo is pure shell today with no build step (the tools are read into writeShellApplication by the consuming home config). A Go binary adds go.mod + buildGoModule (vendorHash, a real compile). Decide up front: either this repo grows its own flake exposing a pm package (the build lives with the code and nix run works), or the consuming config carries the buildGoModule. The own-flake option is the cleaner default, and since this is the pilot carve-off of the bash→Go migration (#98), whatever we pick sets the pattern for the rest.

2. Oracle first. No tests or golden output exist for the tool yet. Step 0 of the port: freeze a golden HTML from the bash renderer on the committed fixtures (deterministic via --issue-snapshot), then hold the Go renderer byte-identical to it — that operationalizes the "reproduces byte-for-byte" criterion already in the body.

Dispatcher note: allod currently hardcodes exec bash "$pm_script"; for a compiled binary that becomes exec "$pm_script" — the only dispatcher coupling, and a one-line change.

**Status (2026-07-17):** the bash version has landed as `allod pm` (the `pm/` tree in this repo) and is packaged into the store, so the "land after the bash ergonomics stabilize" boundary is essentially met — the Go port can start against a stable reference. Two points for the dev-plan / review pass that the body doesn't yet cover: **1. Build structure — the main open decision.** This repo is pure shell today with no build step (the tools are read into `writeShellApplication` by the consuming home config). A Go binary adds `go.mod` + `buildGoModule` (vendorHash, a real compile). Decide up front: either this repo grows its own flake exposing a `pm` package (the build lives with the code and `nix run` works), or the consuming config carries the `buildGoModule`. The own-flake option is the cleaner default, and since this is the pilot carve-off of the bash→Go migration (#98), whatever we pick sets the pattern for the rest. **2. Oracle first.** No tests or golden output exist for the tool yet. Step 0 of the port: freeze a golden HTML from the bash renderer on the committed fixtures (deterministic via `--issue-snapshot`), then hold the Go renderer byte-identical to it — that operationalizes the "reproduces byte-for-byte" criterion already in the body. Dispatcher note: `allod` currently hardcodes `exec bash "$pm_script"`; for a compiled binary that becomes `exec "$pm_script"` — the only dispatcher coupling, and a one-line change.
Author
Contributor

Decision (2026-07-17): the tools flake owns the build.

allod/tools grows its own flake exposing packages.<system>.pm via buildGoModule, so go.mod + vendorHash, the compile, and nix run .#pm all live with the code. The consuming home config stops readFile-ing the pm scripts into writeShellApplication and instead pulls the built pm package from the allod-tools flake input it already has.

This is the pattern for the rest of #98: program-shaped tools become Go packages exposed by this flake, while the shell workspace helpers keep the current readFile path until they migrate too. The flake can also host the render golden + integrity-check fixtures as flake checks, so the "reproduces byte-for-byte" criterion runs in CI rather than by hand.

**Decision (2026-07-17): the tools flake owns the build.** `allod/tools` grows its own flake exposing `packages.<system>.pm` via `buildGoModule`, so `go.mod` + vendorHash, the compile, and `nix run .#pm` all live with the code. The consuming home config stops `readFile`-ing the `pm` scripts into `writeShellApplication` and instead pulls the built `pm` package from the `allod-tools` flake input it already has. This is the pattern for the rest of #98: program-shaped tools become Go packages exposed by this flake, while the shell workspace helpers keep the current readFile path until they migrate too. The flake can also host the render golden + integrity-check fixtures as flake checks, so the "reproduces byte-for-byte" criterion runs in CI rather than by hand.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
allod/tools#103
No description provided.