Config-driven ward: the fs.FS-at-launch config-resolve seam (#650-1, foundational) #653

Closed
opened 2026-07-08 03:32:51 +00:00 by coilyco-ops · 4 comments
Member

Breakdown of the config-driven ward epic (#650). This is the foundational seam - issues #650-2..5 depend on it. Folds in the ward-side of #644, homebrew-tap#3, and the "keep the baked neutral default" half of #645.

Do

Make the KDL build sites take an fs.FS chosen at launch instead of the hard-wired embed.FS. The compiler (specverb.Build / execverb.Build / fleetconfig.Parse) is unchanged - only the source handle moves.

The three sites, grounded in the tree:

  • cmd/ward/ops.go - buildForgejoOps (:62) does opsAssets.ReadFile x2 (guardfile + spec lock), and graftForgejoAdminExec (:121) does opsAssets.ReadFile of the admin guardfile. opsAssets is //go:embed (:30).
  • cmd/ward/wardkdl_exec.go - mountWardKdlExec (:29) does fs.ReadDir(execAssets, execAssetsDir) then execAssets.ReadFile per file. execAssets is //go:embed (:22).
  • cmd/ward/fleet.go - loadFleetConfig (:17) does fleetconfig.Parse(fleetGeneratedKDL), where fleetGeneratedKDL is //go:embed'd as a single []byte (:12-13), not an embed.FS. So this site reads one file path off the selected source, a slightly different shape from the other two.

Selection contract

  • WARD_CONFIG_REF unset - the baked embed.FS (today's exact behavior, the neutral default). Byte-for-byte identical to now.
  • WARD_CONFIG_REF set - os.DirFS(<resolved subpath>). The resolver that turns the var into that local path is #650-2 - this issue only introduces the fs.FS seam and consumes whatever path it is handed. Tests can use file:// fixtures and the real aos bundle (see below).

Parse-failure behavior

Reuse the existing opsCommand degrade-to-error-leaf (ops.go:42): a build error mounts a leaf surfacing guardfile runtime failed to mount: %w on invocation, so a bad bundle cannot silently drop a whole verb surface.

Grounding update vs the epic's design comment

The design assumed aos#332 was still in flight and that issue-1 would test against file:// fixtures only. aos#332 landed 2026-07-08 - aos's .ward/ bundle now carries the real guardfiles (ward-kdl.forgejo/ollama/signoz, ward-kdl.fleet.kdl), the openapi/swagger locks, and ward.yaml. So this issue can point a real-bundle integration test at that landed bundle, not only synthetic fixtures.

Supersedes / folds in

  • #644 (release-overlay, closed) - the build-time overlay is gone. The neutral default is baked, real config is resolved live.
  • homebrew-tap#3 (from-source overlay) - superseded, closed alongside this breakdown.
  • #645 (neutralize tree) - its intent (ward tree carries zero coilyco config, safe neutral default) lands here as "keep the baked neutral default". Its mechanism (build overlay + tap from-source) is dropped.

Filed from the read-only director surface (she/her).

Breakdown of the [config-driven ward epic (#650)](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/650). This is the **foundational seam** - issues #650-2..5 depend on it. Folds in the ward-side of #644, homebrew-tap#3, and the "keep the baked neutral default" half of #645. ## Do Make the KDL build sites take an `fs.FS` **chosen at launch** instead of the hard-wired `embed.FS`. The compiler (`specverb.Build` / `execverb.Build` / `fleetconfig.Parse`) is unchanged - only the source handle moves. The three sites, grounded in the tree: * **`cmd/ward/ops.go`** - `buildForgejoOps` (`:62`) does `opsAssets.ReadFile` x2 (guardfile + spec lock), and `graftForgejoAdminExec` (`:121`) does `opsAssets.ReadFile` of the admin guardfile. `opsAssets` is `//go:embed` (`:30`). * **`cmd/ward/wardkdl_exec.go`** - `mountWardKdlExec` (`:29`) does `fs.ReadDir(execAssets, execAssetsDir)` then `execAssets.ReadFile` per file. `execAssets` is `//go:embed` (`:22`). * **`cmd/ward/fleet.go`** - `loadFleetConfig` (`:17`) does `fleetconfig.Parse(fleetGeneratedKDL)`, where `fleetGeneratedKDL` is `//go:embed`'d as a single `[]byte` (`:12-13`), **not** an `embed.FS`. So this site reads one file path off the selected source, a slightly different shape from the other two. ## Selection contract * **`WARD_CONFIG_REF` unset** - the baked `embed.FS` (today's exact behavior, the neutral default). Byte-for-byte identical to now. * **`WARD_CONFIG_REF` set** - `os.DirFS(<resolved subpath>)`. The resolver that turns the var into that local path is **#650-2** - this issue only introduces the `fs.FS` seam and consumes whatever path it is handed. Tests can use `file://` fixtures **and** the real aos bundle (see below). ## Parse-failure behavior Reuse the existing `opsCommand` degrade-to-error-leaf (`ops.go:42`): a build error mounts a leaf surfacing `guardfile runtime failed to mount: %w` on invocation, so a bad bundle cannot silently drop a whole verb surface. ## Grounding update vs the epic's design comment The design assumed aos#332 was still in flight and that issue-1 would test against `file://` fixtures only. **aos#332 landed 2026-07-08** - aos's `.ward/` bundle now carries the real guardfiles (`ward-kdl.forgejo/ollama/signoz`, `ward-kdl.fleet.kdl`), the openapi/swagger locks, and `ward.yaml`. So this issue can point a real-bundle integration test at that landed bundle, not only synthetic fixtures. ## Supersedes / folds in * **#644** (release-overlay, closed) - the build-time overlay is gone. The neutral default is baked, real config is resolved live. * **homebrew-tap#3** (from-source overlay) - superseded, closed alongside this breakdown. * **#645** (neutralize tree) - its **intent** (ward tree carries zero coilyco config, safe neutral default) lands here as "keep the baked neutral default". Its **mechanism** (build overlay + tap from-source) is dropped. Filed from the read-only director surface (she/her).
Author
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-653 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-08T03:35:45Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#653 · branch issue-653 · driver claude · workflow direct-main
  • Run: engineer-claude-ward-653 · ward v0.439.0 · dispatched 2026-07-08T03:35:45Z
  • Comment thread: 0 included in the pre-flight read, 0 stripped (ward's own automated comments).

Issue body as seeded:

Breakdown of the [config-driven ward epic (#650)](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/650). This is the **foundational seam** - issues #650-2..5 depend on it. Folds in the ward-side of #644, homebrew-tap#3, and the "keep the baked neutral default" half of #645.

## Do

Make the KDL build sites take an `fs.FS` **chosen at launch** instead of the hard-wired `embed.FS`. The compiler (`specverb.Build` / `execverb.Build` / `fleetconfig.Parse`) is unchanged - only the source handle moves.

The three sites, grounded in the tree:

* **`cmd/ward/ops.go`** - `buildForgejoOps` (`:62`) does `opsAssets.ReadFile` x2 (guardfile + spec lock), and `graftForgejoAdminExec` (`:121`) does `opsAssets.ReadFile` of the admin guardfile. `opsAssets` is `//go:embed` (`:30`).
* **`cmd/ward/wardkdl_exec.go`** - `mountWardKdlExec` (`:29`) does `fs.ReadDir(execAssets, execAssetsDir)` then `execAssets.ReadFile` per file. `execAssets` is `//go:embed` (`:22`).
* **`cmd/ward/fleet.go`** - `loadFleetConfig` (`:17`) does `fleetconfig.Parse(fleetGeneratedKDL)`, where `fleetGeneratedKDL` is `//go:embed`'d as a single `[]byte` (`:12-13`), **not** an `embed.FS`. So this site reads one file path off the selected source, a slightly different shape from the other two.

## Selection contract

* **`WARD_CONFIG_REF` unset** - the baked `embed.FS` (today's exact behavior, the neutral default). Byte-for-byte identical to now.
* **`WARD_CONFIG_REF` set** - `os.DirFS(<resolved subpath>)`. The resolver that turns the var into that local path is **#650-2** - this issue only introduces the `fs.FS` seam and consumes whatever path it is handed. Tests can use `file://` fixtures **and** the real aos bundle (see below).

## Parse-failure behavior

Reuse the existing `opsCommand` degrade-to-error-leaf (`ops.go:42`): a build error mounts a leaf surfacing `guardfile runtime failed to mount: %w` on invocation, so a bad bundle cannot silently drop a whole verb surface.

## Grounding update vs the epic's

… (truncated to 2000 chars; full body is on this issue)

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.439.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-653` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-08T03:35:45Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#653` · branch `issue-653` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-ward-653` · ward `v0.439.0` · dispatched `2026-07-08T03:35:45Z` - **Comment thread:** 0 included in the pre-flight read, 0 stripped (ward's own automated comments). **Issue body as seeded:** ``` Breakdown of the [config-driven ward epic (#650)](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/650). This is the **foundational seam** - issues #650-2..5 depend on it. Folds in the ward-side of #644, homebrew-tap#3, and the "keep the baked neutral default" half of #645. ## Do Make the KDL build sites take an `fs.FS` **chosen at launch** instead of the hard-wired `embed.FS`. The compiler (`specverb.Build` / `execverb.Build` / `fleetconfig.Parse`) is unchanged - only the source handle moves. The three sites, grounded in the tree: * **`cmd/ward/ops.go`** - `buildForgejoOps` (`:62`) does `opsAssets.ReadFile` x2 (guardfile + spec lock), and `graftForgejoAdminExec` (`:121`) does `opsAssets.ReadFile` of the admin guardfile. `opsAssets` is `//go:embed` (`:30`). * **`cmd/ward/wardkdl_exec.go`** - `mountWardKdlExec` (`:29`) does `fs.ReadDir(execAssets, execAssetsDir)` then `execAssets.ReadFile` per file. `execAssets` is `//go:embed` (`:22`). * **`cmd/ward/fleet.go`** - `loadFleetConfig` (`:17`) does `fleetconfig.Parse(fleetGeneratedKDL)`, where `fleetGeneratedKDL` is `//go:embed`'d as a single `[]byte` (`:12-13`), **not** an `embed.FS`. So this site reads one file path off the selected source, a slightly different shape from the other two. ## Selection contract * **`WARD_CONFIG_REF` unset** - the baked `embed.FS` (today's exact behavior, the neutral default). Byte-for-byte identical to now. * **`WARD_CONFIG_REF` set** - `os.DirFS(<resolved subpath>)`. The resolver that turns the var into that local path is **#650-2** - this issue only introduces the `fs.FS` seam and consumes whatever path it is handed. Tests can use `file://` fixtures **and** the real aos bundle (see below). ## Parse-failure behavior Reuse the existing `opsCommand` degrade-to-error-leaf (`ops.go:42`): a build error mounts a leaf surfacing `guardfile runtime failed to mount: %w` on invocation, so a bad bundle cannot silently drop a whole verb surface. ## Grounding update vs the epic's ``` … (truncated to 2000 chars; full body is on this issue) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.439.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Member

WARD-OUTCOME: done - the fs.FS-at-launch config-resolve seam landed on main (b4733ec): three KDL build sites compile from a WARD_CONFIG_REF-selected source, baked embeds stay the byte-identical default, and the release-CI overlay from ward#644 is removed.

PR-BODY-NOTE: ADVISORY-ONLY REVIEW: no heterogeneous reviewer family was available besides the worker (claude), so the adversarial panel could not run and did NOT gate this diff. Dropped: opencode (unavailable: opencode not on PATH); codex (unavailable: no codex auth at /home/ubuntu/.codex/auth.json). A human should review this change with the extra scrutiny an unrun panel would have applied.

Retrospective, from the engineer (she/her): this went smoother than a foundational seam had any right to. The lucky break was discovering that ward's own .ward/ward-kdl/ and aos's landed .ward/ share one flat layout, so a single bundleConfigSource covers both and the real-bundle integration tests run hermetically against the in-repo dir. What genuinely fought back: end-to-end driving the binary caught agentModes loading the fleet config at package init and panicking the whole binary on a bad ref - none of the unit tests saw it, only running ward version with a garbage WARD_CONFIG_REF did. That is now the one deliberate asymmetry (init reads the baked roster, every verb-time consumer fails loud) and it is documented. The pre-commit comment/doc-size caps ate a surprising share of the wall clock. Confidence is high: the selection contract, degrade paths, and dialect filter are all pinned by tests, lint findings are byte-identical to baseline, and the first post-removal release pipeline ran green. Rough edges worth knowing: the mixed-dialect exec scan sniffs the exec block with the same regex heuristic the Makefile uses (a broken exec guardfile still fails loud, but a hypothetical marker-free exec dialect would be skipped), and the bundle admin-guardfile name (forgejo-admin.guardfile.kdl) is a contract I defined here - aos does not carry one yet, so the admin slice is withheld under a live bundle until someone homes it there. #654 has a clean seam to feed.

WARD-OUTCOME: done - the fs.FS-at-launch config-resolve seam landed on main (b4733ec): three KDL build sites compile from a WARD_CONFIG_REF-selected source, baked embeds stay the byte-identical default, and the release-CI overlay from ward#644 is removed. PR-BODY-NOTE: ADVISORY-ONLY REVIEW: no heterogeneous reviewer family was available besides the worker (claude), so the adversarial panel could not run and did NOT gate this diff. Dropped: opencode (unavailable: opencode not on PATH); codex (unavailable: no codex auth at /home/ubuntu/.codex/auth.json). A human should review this change with the extra scrutiny an unrun panel would have applied. Retrospective, from the engineer (she/her): this went smoother than a foundational seam had any right to. The lucky break was discovering that ward's own `.ward/ward-kdl/` and aos's landed `.ward/` share one flat layout, so a single bundleConfigSource covers both and the real-bundle integration tests run hermetically against the in-repo dir. What genuinely fought back: end-to-end driving the binary caught `agentModes` loading the fleet config at package init and panicking the whole binary on a bad ref - none of the unit tests saw it, only running `ward version` with a garbage WARD_CONFIG_REF did. That is now the one deliberate asymmetry (init reads the baked roster, every verb-time consumer fails loud) and it is documented. The pre-commit comment/doc-size caps ate a surprising share of the wall clock. Confidence is high: the selection contract, degrade paths, and dialect filter are all pinned by tests, lint findings are byte-identical to baseline, and the first post-removal release pipeline ran green. Rough edges worth knowing: the mixed-dialect exec scan sniffs the `exec` block with the same regex heuristic the Makefile uses (a broken exec guardfile still fails loud, but a hypothetical marker-free exec dialect would be skipped), and the bundle admin-guardfile name (`forgejo-admin.guardfile.kdl`) is a contract I defined here - aos does not carry one yet, so the admin slice is withheld under a live bundle until someone homes it there. #654 has a clean seam to feed.
Author
Member

⚠️ Reopened: this run's work did not land on main

An ephemeral ward container (claude mode) dispatched for this issue finished but its work was not merged to main, so the reaper preserved it on a branch before teardown and reopened the issue (its closes #653 never reached main). Recover from the salvage branch below.

  • Repo: coilyco-flight-deck/ward
  • Salvage branch: ward-salvage/ward-d9e51395
  • Reason: missing same-repo closing reference
  • Container uptime at reap: 31m (age of the baked Forgejo PAT snapshot; a long-lived container is likelier to carry a rotated token)

Reap diagnostics

--- reap diagnostics ---
ward version:      v0.439.0
version source:    pinned via WARD_VERSION/--ward-version (v0.439.0)
HEAD:              35676284d846
origin/main:       8428c38db29f
ancestry:          HEAD is NOT yet on origin/main - residual work remains to land
decision gate:     missing same-repo closing reference
reason:            missing same-repo closing reference
provenance:        present
run-owned landed:  yes
working tree:      1 dirty path(s)
container uptime:  31m (baked Forgejo PAT age proxy)
--- end reap diagnostics ---

Recover

git fetch https://forgejo.coilysiren.me/coilyco-flight-deck/ward.git ward-salvage/ward-d9e51395
git checkout -b ward-salvage/ward-d9e51395 FETCH_HEAD

Working tree at reap time

?? .ward-run-provenance.json

— Claude (she/her), via ward agent

## ⚠️ Reopened: this run's work did not land on `main` An ephemeral `ward container` (claude mode) dispatched for this issue finished but its work was **not merged to `main`**, so the reaper preserved it on a branch before teardown and reopened the issue (its `closes #653` never reached `main`). Recover from the salvage branch below. - **Repo:** `coilyco-flight-deck/ward` - **Salvage branch:** `ward-salvage/ward-d9e51395` - **Reason:** missing same-repo closing reference - **Container uptime at reap:** 31m (age of the baked Forgejo PAT snapshot; a long-lived container is likelier to carry a rotated token) ## Reap diagnostics ``` --- reap diagnostics --- ward version: v0.439.0 version source: pinned via WARD_VERSION/--ward-version (v0.439.0) HEAD: 35676284d846 origin/main: 8428c38db29f ancestry: HEAD is NOT yet on origin/main - residual work remains to land decision gate: missing same-repo closing reference reason: missing same-repo closing reference provenance: present run-owned landed: yes working tree: 1 dirty path(s) container uptime: 31m (baked Forgejo PAT age proxy) --- end reap diagnostics --- ``` ## Recover ```bash git fetch https://forgejo.coilysiren.me/coilyco-flight-deck/ward.git ward-salvage/ward-d9e51395 git checkout -b ward-salvage/ward-d9e51395 FETCH_HEAD ``` ## Working tree at reap time ``` ?? .ward-run-provenance.json ``` <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Member

Closing: the fs.FS config-resolve seam landed on main (closes #653 trailer + the merged docs/config-source.md). Forgejo's native keyword-close did not fire because this run's landing raced/interleaved with #660 (both touch config-source.md), so the closes #653 commit reached main as an already-seen object via integration - the auto-close gap is captured as its own issue. Closing by hand. Filed from the read-only director surface (she/her).

Closing: the fs.FS config-resolve seam landed on `main` (`closes #653` trailer + the merged `docs/config-source.md`). Forgejo's native keyword-close did not fire because this run's landing raced/interleaved with #660 (both touch config-source.md), so the `closes #653` commit reached `main` as an already-seen object via integration - the auto-close gap is captured as its own issue. Closing by hand. Filed from the read-only director surface (she/her).
Sign in to join this conversation.
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
coilyco-flight-deck/ward#653
No description provided.