KDL specs: add a first-class top-level description field; stop leaning on the code-comments header exemption #179

Closed
opened 2026-07-01 15:33:47 +00:00 by coilysiren · 4 comments
Owner

Problem

The code-comments catalog hook (authored in agentic-os, agentic_os/pre_commit/check_code_comments.py) caps comments at 2 contiguous lines / 90 chars after content begins, but exempts the top-of-file header block (every comment line above the first content line, check_code_comments.py:222-224). That carve-out exists for license and teaching headers.

.kdl is folded into that rule. In ward cmd/ward-kdl/ward-kdl.fleet.kdl the first content line is fleet { at line 23, so lines 1-22 are all "header" and fully exempt. Each line is under 90 chars, so the file is technically green - but the license-header exemption is carrying three paragraphs of provenance, sync mechanics, and contract-pin archaeology. That is exactly the durable detail the 2-line rule is meant to push into docs/ behind a short pointer. The rule isn't violated; the exemption is doing something it wasn't designed for, and it recurs across every .kdl in the ward-kdl surface.

Decision (Kai)

If a KDL spec needs that much standing context, the context should be first-class data, not a comment header. Full three-layer fix approved.

Design to produce (advisor commission)

1. cli-guard (this repo) - the enabling grammar change. Add a first-class top-level description node to the KDL grammar. Neither dialect has one today: fleetconfig (dialect 2, pkg/fleetconfig/fleetconfig.go) nor guardfile (dialect 1, http/guardfile/guardfile.go). The proposal should cover: the node shape (single string vs multi-line block), whether it lands on both dialects or fleetconfig first, how it surfaces (e.g. ward-kdl describe / reference-doc generation), and grammar-doc updates (docs/kdl-specs.md, docs/fleetconfig.md). It becomes queryable contract data instead of dead comment text.

2. ward (cmd/ward-kdl/) - migration. Move the "what this spec is and does" prose from the comment header into the new description. Do not dump the whole header in: the changelog/provenance archaeology (which test pins it, which make target syncs it, ward#419/ward#415 history) is not runtime description - it moves to a docs/*.md walkthrough behind a short pointer, per the durable-detail-to-docs convention. description carries the durable "what/why", docs carry the mechanics.

3. agentic-os (check_code_comments.py) - close the loophole. Tighten the .kdl top-of-file header exemption so a first-class description is the sanctioned home for standing context and a fat comment header can't silently recur. Sequencing matters: this lands after the grammar field exists and the ward-kdl files migrate, or it breaks currently-green files.

Sequencing

cli-guard grammar -> ward-kdl migration -> agentic-os rule tightening. This repo leads because nothing downstream can proceed until the description node exists.

Authored from the aos director surface after Kai flagged the fleet.kdl header. Config-placement doctrine: the grammar is authored at the engine layer (cli-guard) and flows down.

## Problem The `code-comments` catalog hook (authored in agentic-os, `agentic_os/pre_commit/check_code_comments.py`) caps comments at 2 contiguous lines / 90 chars after content begins, but exempts the top-of-file header block (every comment line above the first content line, `check_code_comments.py:222-224`). That carve-out exists for license and teaching headers. `.kdl` is folded into that rule. In `ward` `cmd/ward-kdl/ward-kdl.fleet.kdl` the first content line is `fleet {` at line 23, so lines 1-22 are all "header" and fully exempt. Each line is under 90 chars, so the file is technically green - but the license-header exemption is carrying three paragraphs of provenance, sync mechanics, and contract-pin archaeology. That is exactly the durable detail the 2-line rule is meant to push into `docs/` behind a short pointer. The rule isn't violated; the exemption is doing something it wasn't designed for, and it recurs across every `.kdl` in the ward-kdl surface. ## Decision (Kai) If a KDL spec needs that much standing context, the context should be **first-class data, not a comment header**. Full three-layer fix approved. ## Design to produce (advisor commission) **1. cli-guard (this repo) - the enabling grammar change.** Add a first-class top-level `description` node to the KDL grammar. Neither dialect has one today: fleetconfig (dialect 2, `pkg/fleetconfig/fleetconfig.go`) nor guardfile (dialect 1, `http/guardfile/guardfile.go`). The proposal should cover: the node shape (single string vs multi-line block), whether it lands on both dialects or fleetconfig first, how it surfaces (e.g. `ward-kdl describe` / reference-doc generation), and grammar-doc updates (`docs/kdl-specs.md`, `docs/fleetconfig.md`). It becomes queryable contract data instead of dead comment text. **2. ward (`cmd/ward-kdl/`) - migration.** Move the "what this spec is and does" prose from the comment header into the new `description`. **Do not** dump the whole header in: the changelog/provenance archaeology (which test pins it, which `make` target syncs it, `ward#419`/`ward#415` history) is not runtime description - it moves to a `docs/*.md` walkthrough behind a short pointer, per the durable-detail-to-docs convention. `description` carries the durable "what/why", docs carry the mechanics. **3. agentic-os (`check_code_comments.py`) - close the loophole.** Tighten the `.kdl` top-of-file header exemption so a first-class `description` is the sanctioned home for standing context and a fat comment header can't silently recur. Sequencing matters: this lands **after** the grammar field exists and the ward-kdl files migrate, or it breaks currently-green files. ## Sequencing cli-guard grammar -> ward-kdl migration -> agentic-os rule tightening. This repo leads because nothing downstream can proceed until the `description` node exists. Authored from the aos director surface after Kai flagged the fleet.kdl header. Config-placement doctrine: the grammar is authored at the engine layer (cli-guard) and flows down.
Member

🔎 ward agent advisor

ward agent advisor --driver claude ran a one-shot standard research pass on this question:

Produce the concrete three-layer design proposal this issue commissions, and post it as a comment. Cover: (1) cli-guard grammar - the exact shape of the new top-level 'description' node for the fleetconfig dialect (pkg/fleetconfig/fleetconfig.go) and whether/how it also lands on the guardfile dialect (http/guardfile/guardfile.go); single string vs multi-line block scalar; how it surfaces (ward-kdl describe / reference-doc gen); doc updates. (2) ward cmd/ward-kdl migration - what prose moves into 'description' vs what changelog/sync archaeology moves to a docs/ walkthrough behind a pointer, using ward-kdl.fleet.kdl as the worked example. (3) agentic-os check_code_comments.py - how to tighten the .kdl top-of-file header exemption so a fat comment header can't recur, and confirm the sequencing (grammar -> migration -> rule) so no currently-green file breaks. Recommend, don't just enumerate.


Now I have enough grounding in both dialects, the describe/Surface model, the reference-doc generation, and the kdl-go v0.14.1 multi-line string support. Here is the proposal.


Three-layer design: first-class description, retire the header exemption

Grounded in a fresh clone of cli-guard. Both dialects parse KDL and never evaluate it, kdl-go is v0.14.1 and its tokenizer already supports KDL v2 triple-quoted, auto-dedented multi-line strings (the same """...""" form the specverb until blocks use, docs/specverb-actions.md). That means the enabling primitive already exists in the parser. What is missing is a node that mounts it.

Layer 1 - cli-guard grammar (this repo, leads)

Node shape: one scalar node, not a block

Add a single top-level description node carrying one string argument, authored as either a bare string or a triple-quoted multi-line block scalar:

description "one-line what/why"
description """
    What this spec is and what it does. Two or three short
    paragraphs of durable what/why. Auto-dedented by kdl-go,
    so it reads as prose, not a blob.
    """

Recommend a scalar node, not a description { ... } block with child paragraph nodes. A block invites re-inventing prose structure in KDL and re-opens the exact "how much standing context" question the header carried. One string field stays trivially queryable (Fleet.Description, Guardfile.Description), renders straight into help and docs, and the multi-line form already covers the "three paragraphs" case without new grammar.

Name it description, deliberately distinct from the existing describe

The guardfile dialect already has a describe node in two places: grant-body (guardfile.go:638, Grant.Describe) and action-body (:720, Action.Describe), both per-leaf notes that enrich the thin upstream spec and flow into per-verb help. Reusing that word at the top level would overload it. Keep the split legible:

  • describe - a per-leaf note (existing, unchanged).
  • description - the whole-spec lede (new, top-level).

Land on both dialects in this one change, not fleetconfig-first

The issue floats "fleetconfig first." Recommend against it. Layer 3 (the code-comments rule) applies to every .kdl file regardless of dialect - the hook does not know fleetconfig from guardfile. If only fleetconfig gets the sanctioned home, Layer 3 can only safely tighten for fleetconfig files, and guardfile .kdl headers either keep the loophole or break with nowhere to migrate to. The grammar delta is tiny and symmetric, so do both together and let Layer 3 close cleanly across the whole surface.

fleetconfig (pkg/fleetconfig/fleetconfig.go) - document-level node (sibling of fleet/director), matching where the comment header sits today (above fleet {):

  • Add Description string to Fleet (near SchemaVersion).
  • In ParseSource's top-level switch n.Name() (:140), add case "description": singleStringArg, reject a duplicate (mirror the seenFleet pattern with a seenDescription flag).
  • Accept it in both Embedded and OperatorLocal sources. It is pure prose, carries no grant, and is not embed-only, so it must not trip the OperatorLocal rejection that fleet gets. It sits outside the fleet block, so it does not disturb the schema-version/agent-required invariants.
  • No entry in permissionTokens - it is descriptive by construction.

guardfile (http/guardfile/guardfile.go) - child of the top-level wrap node (guardfile puts everything under wrap, :221):

  • Add Description string to Guardfile (:180).
  • In applyNode's switch (:245), add case "description": singleArg, reject a duplicate. It is a wrap-body sibling of spec/base-url/auth.

Surfacing: make it the lede, so it is queryable contract data, not dead text

This is the payoff line from the issue - "queryable contract data instead of dead comment text." Wire it into the model each dialect already exposes:

  • guardfile - specverb.Describe(Config) builds the Surface (docs/specverb-describe.md). Add a top-level Description field to Surface, populated from Guardfile.Description, and render it as the opening paragraph of Surface.Markdown(). That single addition flows into all three consumer surfaces at once: the reference doc gen/lock writes (<name>.md), the top-level --help, and the describe verb. The spec's what/why becomes the doc's first paragraph instead of a comment nobody parses.
  • fleetconfig - ward-kdl describe (ward repo) prints Fleet.Description as the header of its output. Same idea, thinner surface.

Doc updates (this repo)

  • docs/fleetconfig.md - add description to the Schema bullet list and to the fenced example, above fleet {.
  • docs/kdl-specs.md - note description as the shared top-of-spec node in the grammar overview.
  • docs/specverb-describe.md - document the new Surface.Description and that it seeds the reference doc's lede.
  • docs/specverb.md - if it carries a wrap-body node table, add the row.

Layer 2 - ward-kdl migration (cmd/ward-kdl/, second)

Worked against ward-kdl.fleet.kdl (the 22-line header, fleet { at line 23). Split the header by the durable-what/why vs mechanics line:

Into description (the runtime what/why): what this fleet spec is and does - it names the agent roster and each agent's launch shape, it is embedded into the ward binary, it is config not a guarded surface. The standing "what am I looking at" that a reader or ward-kdl describe should always see.

To a docs/*.md walkthrough behind a one-line pointer (the archaeology): the provenance and sync mechanics - which test pins the file, which make target regenerates/syncs it, and the ward#419/ward#415 contract-pin history. None of that is runtime description, it is changelog. Put it in a new docs/ward-kdl-fleet.md (ward repo) and leave a single top-of-file comment pointer:

// spec: see docs/ward-kdl-fleet.md for sync + provenance
description """
    ... what/why ...
    """
fleet {
    schema-version 2
    ...
}

That one pointer line is the whole surviving header, well inside any tightened cap. Apply the same split to every other .kdl in the ward-kdl surface in the same migration, since the issue notes the fat-header pattern recurs across all of them.

Layer 3 - agentic-os check_code_comments.py (last)

The exemption today (per the issue, check_code_comments.py:222-224) waives every comment line above the first content line. For .kdl, the sanctioned home now exists, so the header no longer needs to carry standing context.

Recommend: drop the top-of-file header exemption for .kdl entirely - apply the same body rule (2 contiguous comment lines / 90 chars) to the whole file including the top. KDL specs are config, not source, so they carry no SPDX/license block that would need a multi-line header, and a one-line docs pointer like the migration leaves fits comfortably in the 2-line cap. Dropping the carve-out (rather than shrinking it to a fixed N-line budget) is the strongest anti-recurrence: there is no residual "header allowance" for prose to silently regrow into. A shrunk-but-nonzero budget would just invite the next fat header up to the new limit.

Mechanically, gate the exemption on extension: keep the header carve-out for the license/teaching-header file types it was built for, and make .kdl fall through to the standard contiguous-comment check. If a license line is ever genuinely needed on a .kdl, it still passes under the 2-line cap.

Sequencing - confirmed, and why nothing green breaks

The issue's order is correct and is a hard dependency chain, not a preference:

  1. cli-guard grammar - ship description on both dialects. Until this lands, migrated files have nowhere to put the prose.
  2. ward-kdl migration - move what/why into description, archaeology into docs. After this, no .kdl in the ward-kdl surface relies on a fat header.
  3. agentic-os rule tightening - only now drop the .kdl header exemption.

The break risk is real and one-directional: the instant Layer 3 lands, ward-kdl.fleet.kdl's 22-line header stops being exempt and the file goes red. So Layer 3 must not merge before Layer 2 has landed on canonical main. Concretely, gate the Layer 3 PR on: pre-commit run check-code-comments --all-files clean across the ward-kdl surface with the migrated files in place. Run that check against a tree that has both the new rule and the migrated .kdl files before merging Layer 3 - if it is green there, no currently-green file regresses in production.

One cross-repo caveat worth stating in the Layer 3 PR: the rule ships from agentic-os and rolls to the fleet via ansible, not at install time. Any .kdl in a repo that has not yet migrated its headers will go red when the tightened hook reaches it. Layer 2 covers the ward-kdl surface, which is the known population, but a quick grep -rl for fat-header .kdl files elsewhere before Layer 3 rolls out avoids a surprise red in a repo nobody was thinking about.


Researched and posted automatically by ward agent advisor --driver claude (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Claude (she/her), via ward agent

### 🔎 ward agent advisor `ward agent advisor --driver claude` ran a one-shot **standard** research pass on this question: > Produce the concrete three-layer design proposal this issue commissions, and post it as a comment. Cover: (1) cli-guard grammar - the exact shape of the new top-level 'description' node for the fleetconfig dialect (pkg/fleetconfig/fleetconfig.go) and whether/how it also lands on the guardfile dialect (http/guardfile/guardfile.go); single string vs multi-line block scalar; how it surfaces (ward-kdl describe / reference-doc gen); doc updates. (2) ward cmd/ward-kdl migration - what prose moves into 'description' vs what changelog/sync archaeology moves to a docs/ walkthrough behind a pointer, using ward-kdl.fleet.kdl as the worked example. (3) agentic-os check_code_comments.py - how to tighten the .kdl top-of-file header exemption so a fat comment header can't recur, and confirm the sequencing (grammar -> migration -> rule) so no currently-green file breaks. Recommend, don't just enumerate. --- Now I have enough grounding in both dialects, the describe/Surface model, the reference-doc generation, and the kdl-go v0.14.1 multi-line string support. Here is the proposal. --- # Three-layer design: first-class `description`, retire the header exemption Grounded in a fresh clone of cli-guard. Both dialects parse KDL and never evaluate it, `kdl-go` is `v0.14.1` and its tokenizer already supports KDL v2 triple-quoted, auto-dedented multi-line strings (the same `"""..."""` form the specverb `until` blocks use, `docs/specverb-actions.md`). That means the enabling primitive already exists in the parser. What is missing is a node that mounts it. ## Layer 1 - cli-guard grammar (this repo, leads) ### Node shape: one scalar node, not a block Add a single top-level `description` node carrying **one string argument**, authored as either a bare string or a triple-quoted multi-line block scalar: ```kdl description "one-line what/why" ``` ```kdl description """ What this spec is and what it does. Two or three short paragraphs of durable what/why. Auto-dedented by kdl-go, so it reads as prose, not a blob. """ ``` **Recommend a scalar node, not a `description { ... }` block with child paragraph nodes.** A block invites re-inventing prose structure in KDL and re-opens the exact "how much standing context" question the header carried. One string field stays trivially queryable (`Fleet.Description`, `Guardfile.Description`), renders straight into help and docs, and the multi-line form already covers the "three paragraphs" case without new grammar. ### Name it `description`, deliberately distinct from the existing `describe` The guardfile dialect **already has a `describe` node** in two places: grant-body (`guardfile.go:638`, `Grant.Describe`) and action-body (`:720`, `Action.Describe`), both per-leaf notes that enrich the thin upstream spec and flow into per-verb help. Reusing that word at the top level would overload it. Keep the split legible: - `describe` - a per-leaf note (existing, unchanged). - `description` - the whole-spec lede (new, top-level). ### Land on **both** dialects in this one change, not fleetconfig-first The issue floats "fleetconfig first." Recommend against it. Layer 3 (the code-comments rule) applies to **every `.kdl` file regardless of dialect** - the hook does not know fleetconfig from guardfile. If only fleetconfig gets the sanctioned home, Layer 3 can only safely tighten for fleetconfig files, and guardfile `.kdl` headers either keep the loophole or break with nowhere to migrate to. The grammar delta is tiny and symmetric, so do both together and let Layer 3 close cleanly across the whole surface. **fleetconfig (`pkg/fleetconfig/fleetconfig.go`)** - document-level node (sibling of `fleet`/`director`), matching where the comment header sits today (above `fleet {`): - Add `Description string` to `Fleet` (near `SchemaVersion`). - In `ParseSource`'s top-level `switch n.Name()` (`:140`), add `case "description"`: `singleStringArg`, reject a duplicate (mirror the `seenFleet` pattern with a `seenDescription` flag). - **Accept it in both `Embedded` and `OperatorLocal` sources.** It is pure prose, carries no grant, and is not embed-only, so it must not trip the `OperatorLocal` rejection that `fleet` gets. It sits outside the `fleet` block, so it does not disturb the `schema-version`/`agent`-required invariants. - No entry in `permissionTokens` - it is descriptive by construction. **guardfile (`http/guardfile/guardfile.go`)** - child of the top-level `wrap` node (guardfile puts everything under `wrap`, `:221`): - Add `Description string` to `Guardfile` (`:180`). - In `applyNode`'s `switch` (`:245`), add `case "description"`: `singleArg`, reject a duplicate. It is a wrap-body sibling of `spec`/`base-url`/`auth`. ### Surfacing: make it the lede, so it is queryable contract data, not dead text This is the payoff line from the issue - "queryable contract data instead of dead comment text." Wire it into the model each dialect already exposes: - **guardfile** - `specverb.Describe(Config)` builds the `Surface` (`docs/specverb-describe.md`). Add a top-level `Description` field to `Surface`, populated from `Guardfile.Description`, and render it as the opening paragraph of `Surface.Markdown()`. That single addition flows into all three consumer surfaces at once: the reference doc `gen`/`lock` writes (`<name>.md`), the top-level `--help`, and the describe verb. The spec's what/why becomes the doc's first paragraph instead of a comment nobody parses. - **fleetconfig** - `ward-kdl describe` (ward repo) prints `Fleet.Description` as the header of its output. Same idea, thinner surface. ### Doc updates (this repo) - `docs/fleetconfig.md` - add `description` to the Schema bullet list and to the fenced example, above `fleet {`. - `docs/kdl-specs.md` - note `description` as the shared top-of-spec node in the grammar overview. - `docs/specverb-describe.md` - document the new `Surface.Description` and that it seeds the reference doc's lede. - `docs/specverb.md` - if it carries a wrap-body node table, add the row. ## Layer 2 - ward-kdl migration (`cmd/ward-kdl/`, second) Worked against `ward-kdl.fleet.kdl` (the 22-line header, `fleet {` at line 23). Split the header by the durable-what/why vs mechanics line: **Into `description` (the runtime what/why):** what this fleet spec is and does - it names the agent roster and each agent's launch shape, it is embedded into the ward binary, it is config not a guarded surface. The standing "what am I looking at" that a reader or `ward-kdl describe` should always see. **To a `docs/*.md` walkthrough behind a one-line pointer (the archaeology):** the provenance and sync mechanics - which test pins the file, which `make` target regenerates/syncs it, and the `ward#419`/`ward#415` contract-pin history. None of that is runtime description, it is changelog. Put it in a new `docs/ward-kdl-fleet.md` (ward repo) and leave a single top-of-file comment pointer: ```kdl // spec: see docs/ward-kdl-fleet.md for sync + provenance description """ ... what/why ... """ fleet { schema-version 2 ... } ``` That one pointer line is the whole surviving header, well inside any tightened cap. Apply the same split to every other `.kdl` in the ward-kdl surface in the same migration, since the issue notes the fat-header pattern recurs across all of them. ## Layer 3 - agentic-os `check_code_comments.py` (last) The exemption today (per the issue, `check_code_comments.py:222-224`) waives **every** comment line above the first content line. For `.kdl`, the sanctioned home now exists, so the header no longer needs to carry standing context. **Recommend: drop the top-of-file header exemption for `.kdl` entirely** - apply the same body rule (2 contiguous comment lines / 90 chars) to the whole file including the top. KDL specs are config, not source, so they carry no SPDX/license block that would need a multi-line header, and a one-line docs pointer like the migration leaves fits comfortably in the 2-line cap. Dropping the carve-out (rather than shrinking it to a fixed N-line budget) is the strongest anti-recurrence: there is no residual "header allowance" for prose to silently regrow into. A shrunk-but-nonzero budget would just invite the next fat header up to the new limit. Mechanically, gate the exemption on extension: keep the header carve-out for the license/teaching-header file types it was built for, and make `.kdl` fall through to the standard contiguous-comment check. If a license line is ever genuinely needed on a `.kdl`, it still passes under the 2-line cap. ## Sequencing - confirmed, and why nothing green breaks The issue's order is correct and is a hard dependency chain, not a preference: 1. **cli-guard grammar** - ship `description` on both dialects. Until this lands, migrated files have nowhere to put the prose. 2. **ward-kdl migration** - move what/why into `description`, archaeology into docs. After this, no `.kdl` in the ward-kdl surface relies on a fat header. 3. **agentic-os rule tightening** - only now drop the `.kdl` header exemption. The break risk is real and one-directional: the instant Layer 3 lands, `ward-kdl.fleet.kdl`'s 22-line header stops being exempt and the file goes red. So Layer 3 **must not merge before Layer 2 has landed on canonical main**. Concretely, gate the Layer 3 PR on: `pre-commit run check-code-comments --all-files` clean across the ward-kdl surface with the migrated files in place. Run that check against a tree that has both the new rule and the migrated `.kdl` files before merging Layer 3 - if it is green there, no currently-green file regresses in production. One cross-repo caveat worth stating in the Layer 3 PR: the rule ships from agentic-os and rolls to the fleet via ansible, not at install time. Any `.kdl` in a repo that has **not** yet migrated its headers will go red when the tightened hook reaches it. Layer 2 covers the ward-kdl surface, which is the known population, but a quick `grep -rl` for fat-header `.kdl` files elsewhere before Layer 3 rolls out avoids a surprise red in a repo nobody was thinking about. --- Researched and posted automatically by `ward agent advisor --driver claude` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Member

Cleared consult→headless (Kai, director session 2026-07-07). No open decision: the fix was approved on filing and the advisor already posted a complete three-layer design. Dispatching Layer 1 only (cli-guard grammar): the scalar top-level description node on BOTH dialects (fleetconfig + guardfile) per the advisor's recommendation, surfaced via Surface/describe + reference-doc gen, plus the grammar-doc updates. Layers 2 (ward-kdl migration) and 3 (aos code-comments rule) are sequenced follow-ups dispatched AFTER Layer 1 lands on main - they cannot land before it (grammar→migration→rule is a hard chain, Layer 3 reddens green files until Layer 2 is on main).

Cleared consult→headless (Kai, director session 2026-07-07). No open decision: the fix was approved on filing and the advisor already posted a complete three-layer design. **Dispatching Layer 1 only (cli-guard grammar):** the scalar top-level `description` node on BOTH dialects (fleetconfig + guardfile) per the advisor's recommendation, surfaced via Surface/describe + reference-doc gen, plus the grammar-doc updates. Layers 2 (ward-kdl migration) and 3 (aos code-comments rule) are sequenced follow-ups dispatched AFTER Layer 1 lands on main - they cannot land before it (grammar→migration→rule is a hard chain, Layer 3 reddens green files until Layer 2 is on main).
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-cli-guard-179 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-07T22:46:14Z). 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/cli-guard#179 · branch issue-179 · driver claude · workflow direct-main
  • Run: engineer-claude-cli-guard-179 · ward v0.422.0 · dispatched 2026-07-07T22:46:14Z
  • Comment thread: 2 included in the pre-flight read, 0 stripped (ward's own automated comments).

Issue body as seeded:

## Problem

The `code-comments` catalog hook (authored in agentic-os, `agentic_os/pre_commit/check_code_comments.py`) caps comments at 2 contiguous lines / 90 chars after content begins, but exempts the top-of-file header block (every comment line above the first content line, `check_code_comments.py:222-224`). That carve-out exists for license and teaching headers.

`.kdl` is folded into that rule. In `ward` `cmd/ward-kdl/ward-kdl.fleet.kdl` the first content line is `fleet {` at line 23, so lines 1-22 are all "header" and fully exempt. Each line is under 90 chars, so the file is technically green - but the license-header exemption is carrying three paragraphs of provenance, sync mechanics, and contract-pin archaeology. That is exactly the durable detail the 2-line rule is meant to push into `docs/` behind a short pointer. The rule isn't violated; the exemption is doing something it wasn't designed for, and it recurs across every `.kdl` in the ward-kdl surface.

## Decision (Kai)

If a KDL spec needs that much standing context, the context should be **first-class data, not a comment header**. Full three-layer fix approved.

## Design to produce (advisor commission)

**1. cli-guard (this repo) - the enabling grammar change.** Add a first-class top-level `description` node to the KDL grammar. Neither dialect has one today: fleetconfig (dialect 2, `pkg/fleetconfig/fleetconfig.go`) nor guardfile (dialect 1, `http/guardfile/guardfile.go`). The proposal should cover: the node shape (single string vs multi-line block), whether it lands on both dialects or fleetconfig first, how it surfaces (e.g. `ward-kdl describe` / reference-doc generation), and grammar-doc updates (`docs/kdl-specs.md`, `docs/fleetconfig.md`). It becomes queryable contract data instead of dead comment text.

**2. ward (`cmd/ward-kdl/`) - migration.** Move the "what this spec is and does" prose from the comment header into the new `description`. **Do not** dump the whole header in: the changelog/provenan

… (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.422.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-cli-guard-179` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-07T22:46:14Z). 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/cli-guard#179` · branch `issue-179` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-cli-guard-179` · ward `v0.422.0` · dispatched `2026-07-07T22:46:14Z` - **Comment thread:** 2 included in the pre-flight read, 0 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-01T15:36:54Z), @coilyco-ops (2026-07-07T22:46:03Z) **Issue body as seeded:** ``` ## Problem The `code-comments` catalog hook (authored in agentic-os, `agentic_os/pre_commit/check_code_comments.py`) caps comments at 2 contiguous lines / 90 chars after content begins, but exempts the top-of-file header block (every comment line above the first content line, `check_code_comments.py:222-224`). That carve-out exists for license and teaching headers. `.kdl` is folded into that rule. In `ward` `cmd/ward-kdl/ward-kdl.fleet.kdl` the first content line is `fleet {` at line 23, so lines 1-22 are all "header" and fully exempt. Each line is under 90 chars, so the file is technically green - but the license-header exemption is carrying three paragraphs of provenance, sync mechanics, and contract-pin archaeology. That is exactly the durable detail the 2-line rule is meant to push into `docs/` behind a short pointer. The rule isn't violated; the exemption is doing something it wasn't designed for, and it recurs across every `.kdl` in the ward-kdl surface. ## Decision (Kai) If a KDL spec needs that much standing context, the context should be **first-class data, not a comment header**. Full three-layer fix approved. ## Design to produce (advisor commission) **1. cli-guard (this repo) - the enabling grammar change.** Add a first-class top-level `description` node to the KDL grammar. Neither dialect has one today: fleetconfig (dialect 2, `pkg/fleetconfig/fleetconfig.go`) nor guardfile (dialect 1, `http/guardfile/guardfile.go`). The proposal should cover: the node shape (single string vs multi-line block), whether it lands on both dialects or fleetconfig first, how it surfaces (e.g. `ward-kdl describe` / reference-doc generation), and grammar-doc updates (`docs/kdl-specs.md`, `docs/fleetconfig.md`). It becomes queryable contract data instead of dead comment text. **2. ward (`cmd/ward-kdl/`) - migration.** Move the "what this spec is and does" prose from the comment header into the new `description`. **Do not** dump the whole header in: the changelog/provenan ``` … (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.422.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Member

WARD-OUTCOME: done - top-level description node added to all three .kdl dialects (fleetconfig, http/guardfile, exec), surfaced in describe + reference docs, landed on main.

The grammar change itself was small and clean - a single string arg parsed into a typed field, fail-closed on empty. The interesting decision was scope: the issue named two dialects, but the repo actually has three .kdl-producing grammars (fleetconfig, the http/guardfile wrap, and the exec-dialect wrap), and all three carry fat headers in ward-kdl. Since Layer 3's whole point is to drop the header exemption, leaving the exec dialect out would just move the loophole rather than close it, so I did all three. That felt like the right read of the intent even though it's slightly beyond the letter.

What fought back was not the code - it was the docs. FEATURES.md, fleetconfig.md, and kdl-specs.md were each sitting at ~3990/4000 chars against a hard documentation-layout cap, so adding even a one-line pointer overflowed all three. I moved the real grammar walkthrough into its own docs/kdl-description.md and then spent more effort than I'd like trimming existing prose word-by-word to fit the pointers back under the cap. It works and reads fine, but it's a smell that those three docs are permanently one sentence away from the ceiling.

Confidence is high: full pre-commit suite green (including the very code-comments hook this issue is about), godoc pinned, tests cover parse + surface + fail-closed for each dialect. Follow-ups are the already-sequenced Layers 2 (ward-kdl migration) and 3 (agentic-os rule tightening) - both blocked on this being on main, which it now is. One minor thing worth watching: the opcore inline source (ParseInline, ward-mcp) shares guardfile pieces but is embedded in a larger doc, so it deliberately got no top-level description; if that grammar ever grows standalone .kdl files, it'll want the same node.

WARD-OUTCOME: done - top-level `description` node added to all three .kdl dialects (fleetconfig, http/guardfile, exec), surfaced in describe + reference docs, landed on main. The grammar change itself was small and clean - a single string arg parsed into a typed field, fail-closed on empty. The interesting decision was scope: the issue named two dialects, but the repo actually has three .kdl-producing grammars (fleetconfig, the http/guardfile `wrap`, and the exec-dialect `wrap`), and all three carry fat headers in ward-kdl. Since Layer 3's whole point is to drop the header exemption, leaving the exec dialect out would just move the loophole rather than close it, so I did all three. That felt like the right read of the intent even though it's slightly beyond the letter. What fought back was not the code - it was the docs. FEATURES.md, fleetconfig.md, and kdl-specs.md were each sitting at ~3990/4000 chars against a hard documentation-layout cap, so adding even a one-line pointer overflowed all three. I moved the real grammar walkthrough into its own docs/kdl-description.md and then spent more effort than I'd like trimming existing prose word-by-word to fit the pointers back under the cap. It works and reads fine, but it's a smell that those three docs are permanently one sentence away from the ceiling. Confidence is high: full pre-commit suite green (including the very code-comments hook this issue is about), godoc pinned, tests cover parse + surface + fail-closed for each dialect. Follow-ups are the already-sequenced Layers 2 (ward-kdl migration) and 3 (agentic-os rule tightening) - both blocked on this being on main, which it now is. One minor thing worth watching: the opcore inline source (ParseInline, ward-mcp) shares guardfile pieces but is embedded in a larger doc, so it deliberately got no top-level description; if that grammar ever grows standalone .kdl files, it'll want the same node.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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/cli-guard#179
No description provided.