Restructure ward KDL layer: per-verb read/write/admin trifecta (drop flat bucket) + convert .ward config to KDL - the two upstream-blocked parts of #435 #614

Open
opened 2026-07-05 23:13:52 +00:00 by coilysiren · 0 comments
Owner

Follow-up carrying the two parts of #435 that could not land in that run because each is hard-blocked on an external repo outside a ward container's grant, and forcing either one would break the commit gate or the config loader for every consumer.

#435 landed Part 2: the guardfile tree moved from cmd/ward-kdl/ into .ward/ward-kdl/, with the source -> embed -> drift-test contract kept intact (make sync-*-assets, the execassets/opsassets/fleetassets drift tests, ward exec/doctor/the hook all green from the new home). This issue tracks Parts 1 and 3, which should land together once their upstream gates clear (they rewrite the same tree, so keep them one job - not two racing branches).

Part 1 - per-verb read/write/admin trifecta for every tool; drop the flat bucket

Today .ward/ward-kdl/ still holds ~15 flat guardfiles (aider, aws, brew, claude, codex, docker, eco-native, eco-observe, eco-server, forgejo-key, git, goose, kubectl, ollama, opencode) plus the two already-tiered areas (forgejo via ward#278, signoz via ward#338) and five placeholder scaffolds (glama, glitchtip, skillsmp, tailscale, trello via ward#344). The target is: no guardfile at the top layer, every tool split into ward-kdl-read//ward-kdl-write//ward-kdl-admin/.

Blocker: the per-verb tiering fan-out is ward#339, which the ward#344 placeholder comments record as gated on cli-guard#169 (the override grammar needed to narrow an inherited wildcard at a lower tier). forgejo/signoz were tiered without it only by hand-authoring explicit per-tier leaves plus never-inheritance - doing that for 15 more tools is a large, security-critical, per-tool judgement call that upstream deliberately staged.

Coupled structural work Part 1 also needs (not just the guardfiles): the flat bucket is the embed source for the run-time product. cmd/ward/wardkdl_exec.go + make sync-exec-assets discover exec-dialect guardfiles by the flat-layer glob and auto-mount them into ward at their wrap path (ward docker, ward ops aws, ward agents ...); cmd/ward/ops.go embeds the flat forgejo guardfile for ward ops forgejo. Dropping the flat bucket therefore also means reworking those embed/mount sources to read from a tier (the -read set for a read-only surface), or ward docker/ward ops/ward agents go dark. This is why the removal cannot be a plain git rm.

Part 3 - convert .ward/ward.yaml to KDL

Goal: .ward/ward.yaml (the commands + security + catalog YAML) becomes KDL so the whole .ward/ layer speaks one grammar, with the repocfg loader reading the KDL form and the catalog: semantics preserved.

Two upstream blockers, both external repos:

  1. cli-guard repocfg is YAML-only at the pinned REF (v0.72.0). cli/repocfg/repocfg.go discovers config.BaseName()+".yaml" (ward.yaml) and parses with yaml.Unmarshal; security.go likewise. ward's cmd/ward/repocfg.go calls repocfg.Load directly. Reading KDL needs either an upstream cli-guard change (KDL discovery + parse) followed by the cross-module release dance (tag -> go get bump -> Makefile REF bump), or a ward-local KDL loader that builds a repocfg.Config itself (all its types are exported, so this is feasible but bypasses upstream validation).

  2. The agentic-os catalog hooks hard-code .ward/ward.yaml as YAML (v0.167.0, pinned in .pre-commit-config.yaml). check_catalog_block.py sets CONFIG_PATH = Path(".ward/ward.yaml") and calls yaml.safe_load; check_catalog_trifecta.py sets CATALOG_YAMLS = (Path(".ward/ward.yaml"),) and fails if it is absent. Both run on every commit, and ward must never --no-verify. Renaming/removing ward.yaml reds the commit gate until agentic-os teaches those hooks a KDL config file. That repo is not in a ward container's grant.

So Part 3 needs coordinated changes in cli-guard (repocfg) and agentic-os (catalog hooks) before the YAML can go away.

Suggested sequencing

  • Land cli-guard#169 (override) -> do the Part 1 per-verb fan-out (ward#339) for the remaining tools, and rework the exec-mount/ops-embed sources off the flat bucket, then delete the flat bucket.
  • Land the cli-guard repocfg-KDL + agentic-os catalog-hook-KDL support -> do Part 3.
  • Both parts rewrite .ward/ward-kdl/ + .ward/, so land them as one coherent job when unblocked.

Origin: ward agent run on #435, 2026-07-05. #435 closed with Part 2 (relocation) landed; this issue carries the blocked remainder so it is not lost.

Follow-up carrying the two parts of #435 that could not land in that run because each is hard-blocked on an **external repo outside a ward container's grant**, and forcing either one would break the commit gate or the config loader for every consumer. #435 **landed Part 2**: the guardfile tree moved from `cmd/ward-kdl/` into `.ward/ward-kdl/`, with the source -> embed -> drift-test contract kept intact (`make sync-*-assets`, the `execassets`/`opsassets`/`fleetassets` drift tests, `ward exec`/`doctor`/the hook all green from the new home). This issue tracks Parts 1 and 3, which should land together once their upstream gates clear (they rewrite the same tree, so keep them one job - not two racing branches). ## Part 1 - per-verb read/write/admin trifecta for every tool; drop the flat bucket Today `.ward/ward-kdl/` still holds ~15 flat guardfiles (aider, aws, brew, claude, codex, docker, eco-native, eco-observe, eco-server, forgejo-key, git, goose, kubectl, ollama, opencode) plus the two already-tiered areas (forgejo via ward#278, signoz via ward#338) and five placeholder scaffolds (glama, glitchtip, skillsmp, tailscale, trello via ward#344). The target is: no guardfile at the top layer, every tool split into `ward-kdl-read/`/`ward-kdl-write/`/`ward-kdl-admin/`. **Blocker:** the per-verb tiering fan-out is ward#339, which the ward#344 placeholder comments record as **gated on cli-guard#169** (the `override` grammar needed to narrow an inherited wildcard at a lower tier). forgejo/signoz were tiered without it only by hand-authoring explicit per-tier leaves plus `never`-inheritance - doing that for 15 more tools is a large, security-critical, per-tool judgement call that upstream deliberately staged. **Coupled structural work Part 1 also needs (not just the guardfiles):** the flat bucket is the *embed source* for the run-time product. `cmd/ward/wardkdl_exec.go` + `make sync-exec-assets` discover exec-dialect guardfiles by the flat-layer glob and auto-mount them into `ward` at their `wrap` path (`ward docker`, `ward ops aws`, `ward agents ...`); `cmd/ward/ops.go` embeds the flat forgejo guardfile for `ward ops forgejo`. Dropping the flat bucket therefore also means reworking those embed/mount sources to read from a tier (the `-read` set for a read-only surface), or `ward docker`/`ward ops`/`ward agents` go dark. This is why the removal cannot be a plain `git rm`. ## Part 3 - convert `.ward/ward.yaml` to KDL Goal: `.ward/ward.yaml` (the `commands` + `security` + `catalog` YAML) becomes KDL so the whole `.ward/` layer speaks one grammar, with the `repocfg` loader reading the KDL form and the `catalog:` semantics preserved. **Two upstream blockers, both external repos:** 1. **cli-guard `repocfg` is YAML-only at the pinned REF (v0.72.0).** `cli/repocfg/repocfg.go` discovers `config.BaseName()+".yaml"` (`ward.yaml`) and parses with `yaml.Unmarshal`; `security.go` likewise. ward's `cmd/ward/repocfg.go` calls `repocfg.Load` directly. Reading KDL needs either an upstream cli-guard change (KDL discovery + parse) followed by the cross-module release dance (tag -> `go get` bump -> Makefile `REF` bump), or a ward-local KDL loader that builds a `repocfg.Config` itself (all its types are exported, so this is feasible but bypasses upstream validation). 2. **The agentic-os catalog hooks hard-code `.ward/ward.yaml` as YAML (v0.167.0, pinned in `.pre-commit-config.yaml`).** `check_catalog_block.py` sets `CONFIG_PATH = Path(".ward/ward.yaml")` and calls `yaml.safe_load`; `check_catalog_trifecta.py` sets `CATALOG_YAMLS = (Path(".ward/ward.yaml"),)` and fails if it is absent. Both run on **every commit**, and ward must never `--no-verify`. Renaming/removing `ward.yaml` reds the commit gate until agentic-os teaches those hooks a KDL config file. That repo is not in a ward container's grant. So Part 3 needs coordinated changes in cli-guard (repocfg) **and** agentic-os (catalog hooks) before the YAML can go away. ## Suggested sequencing - Land cli-guard#169 (`override`) -> do the Part 1 per-verb fan-out (ward#339) for the remaining tools, and rework the exec-mount/ops-embed sources off the flat bucket, then delete the flat bucket. - Land the cli-guard `repocfg`-KDL + agentic-os catalog-hook-KDL support -> do Part 3. - Both parts rewrite `.ward/ward-kdl/` + `.ward/`, so land them as one coherent job when unblocked. Origin: `ward agent` run on #435, 2026-07-05. #435 closed with Part 2 (relocation) landed; this issue carries the blocked remainder so it is not lost.
coilyco-ops added
P4
and removed
P2
labels 2026-07-14 06:50:31 +00:00
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#614
No description provided.