feat(docs): auto-generate a flat KDL-keyword index (names-only UL) linking each keyword to its doc page + command entrypoint, freshness-validated #175

Closed
opened 2026-06-26 16:32:23 +00:00 by coilysiren · 1 comment
Owner

Goal

Replace the hand-maintained KDL-keyword reference with an auto-generated, fully-flat keyword index, cross-link every keyword to its entrypoint, and keep it honest with the knowledge-base freshness program (a derived render that cannot drift past its source). Three coupled deliverables.

1. Enumerate every KDL keyword from ONE source of truth

The grammar keywords are parsed across several files as scattered case "<kw>": switches: http/guardfile/guardfile.go, cli/execverb/guardfile.go, http/specverb/*.go (wrap, exec, passthrough, can, cannot, never, deny-when, restrict, argv, seal, allow, gate, op, action, poll, collect, call, env, auth, only/never pass, ...). Introduce a single keyword registry the parsers consult (or that they are generated from) so there is exactly one list - never a second hand-maintained list that can drift. The index, the per-page links, and the freshness check all read from this one registry.

2. Render a fully-flat, names-only UL (replacing a doc page)

Generate a markdown page (under docs/, replacing the current keyword-reference page) that is a single flat list of keyword NAMES ONLY - no descriptions (the page must stay under the markdown size limit, which is the whole reason descriptions are excluded). Each list item links to both:

  • its true documentation page - the docs/<keyword>.md (or section) for that keyword, and
  • its command entrypoint - the source symbol/handler that implements the keyword.

Hook the generator into scripts/gen-webdocs/main.go - it already renders the CLI reference and builds a flat nav list via layout.BuildNavList (see writeIndex) - or a sibling generator in the kdl-specs package. Reuse layout.BuildNavList so the flat-UL shape is consistent with the existing index.

One decision to confirm (the single human-judgment point): "command entrypoint" = the Go parse/handler symbol that implements the keyword (a godoc / source-reference target) by default. If the maintainer means the rendered /cli/ CLI-reference page instead, wire there. Default to the source entrypoint unless told otherwise.

Add to each docs/<keyword>.md a link to that keyword's command entrypoint. Generate or lint this so a keyword page missing the link fails CI. Drive it from the same registry (#1).

Freshness validation (the real ask: the hand-rolled aos program, not godoc-current)

The generated index and the per-page entrypoint links are derived facts: rendered from the keyword registry (ground truth), diffable, cannot drift past their source. Validate on two layers:

  • Provenance marker (the knowledge-base freshness program, agentic_os/freshness.py / ward freshness). Stamp the generated page with <!-- freshness: as-of=<date> decay-class=derived half-life=fast source="<the generator command>" --> so the scheduled freshness probe grades and tracks it. (See docs/knowledge-base-freshness.md; rollout of the probe into a repo is via infrastructure/ansible. If cli-guard does not yet carry the aos catalog/freshness suite, note that as a prerequisite/follow-up rather than hand-porting the probe.)
  • Hard drift gate (the godoc-current style you also approved). Mirror scripts/check-godoc-current.sh: the generator runs with --update to write the committed page + links; a CI step runs it WITHOUT --update and git diff --exit-code fails the build if the index or any entrypoint link drifts from the registry. Add it next to the existing godoc-current.txt up to date step in .forgejo/workflows/ci.yml (and the GitHub-mirror twin). This is the teeth: a keyword added/removed/renamed without regenerating -> red CI.

derived + a regenerate-and-diff gate is the canonical pairing - the marker classifies it, the diff enforces it.

Acceptance

  • A generated, fully-flat, names-only keyword index page exists; every keyword links to both its doc page and its command entrypoint.
  • Every keyword doc page links to its command entrypoint.
  • Keywords come from ONE registry - no hand-maintained duplicate anywhere.
  • CI fails if a keyword is added/removed/renamed without regenerating; freshness marker present and graded derived.
  • The page stays within the markdown size limit (names only).

Files

http/guardfile/guardfile.go + cli/execverb/guardfile.go + http/specverb/*.go (keyword registry / source of truth), scripts/gen-webdocs/main.go (writeIndex + layout.BuildNavList - index generation), docs/ (generated index page + per-keyword entrypoint links), .forgejo/workflows/ci.yml + .github/workflows/ci.yml (hard drift gate), scripts/check-godoc-current.sh (the freshness-gate template to mirror), and the aos freshness program for reference (agentic-os/agentic_os/freshness.py, docs/knowledge-base-freshness.md).

Dependency

Rebase on / land after the specgen -> kdl-specs rename (#174) so the generator lands on the renamed surface.

When done

Run the gate, commit, push to canonical main, update docs/FEATURES.md.

## Goal Replace the hand-maintained KDL-keyword reference with an **auto-generated, fully-flat keyword index**, cross-link every keyword to its entrypoint, and keep it honest with the **knowledge-base freshness program** (a `derived` render that cannot drift past its source). Three coupled deliverables. ## 1. Enumerate every KDL keyword from ONE source of truth The grammar keywords are parsed across several files as scattered `case "<kw>":` switches: `http/guardfile/guardfile.go`, `cli/execverb/guardfile.go`, `http/specverb/*.go` (`wrap, exec, passthrough, can, cannot, never, deny-when, restrict, argv, seal, allow, gate, op, action, poll, collect, call, env, auth, only/never pass, ...`). Introduce a **single keyword registry** the parsers consult (or that they are generated from) so there is exactly one list - never a second hand-maintained list that can drift. The index, the per-page links, and the freshness check all read from this one registry. ## 2. Render a fully-flat, names-only UL (replacing a doc page) Generate a markdown page (under `docs/`, replacing the current keyword-reference page) that is a **single flat list of keyword NAMES ONLY - no descriptions** (the page must stay under the markdown size limit, which is the whole reason descriptions are excluded). Each list item links to **both**: - its **true documentation page** - the `docs/<keyword>.md` (or section) for that keyword, and - its **command entrypoint** - the source symbol/handler that implements the keyword. Hook the generator into `scripts/gen-webdocs/main.go` - it already renders the CLI reference and builds a flat nav list via `layout.BuildNavList` (see `writeIndex`) - or a sibling generator in the kdl-specs package. Reuse `layout.BuildNavList` so the flat-UL shape is consistent with the existing index. **One decision to confirm (the single human-judgment point):** "command entrypoint" = the Go parse/handler symbol that implements the keyword (a godoc / source-reference target) by default. If the maintainer means the rendered `/cli/` CLI-reference page instead, wire there. **Default to the source entrypoint unless told otherwise.** ## 3. Every keyword doc page links to its command entrypoint Add to each `docs/<keyword>.md` a link to that keyword's command entrypoint. Generate or lint this so a keyword page missing the link fails CI. Drive it from the same registry (#1). ## Freshness validation (the real ask: the hand-rolled aos program, not godoc-current) The generated index and the per-page entrypoint links are **`derived`** facts: rendered from the keyword registry (ground truth), diffable, cannot drift past their source. Validate on two layers: - **Provenance marker (the knowledge-base freshness program, `agentic_os/freshness.py` / `ward freshness`).** Stamp the generated page with `<!-- freshness: as-of=<date> decay-class=derived half-life=fast source="<the generator command>" -->` so the scheduled freshness probe grades and tracks it. (See `docs/knowledge-base-freshness.md`; rollout of the probe into a repo is via infrastructure/ansible. If cli-guard does not yet carry the aos catalog/freshness suite, note that as a prerequisite/follow-up rather than hand-porting the probe.) - **Hard drift gate (the godoc-current style you also approved).** Mirror `scripts/check-godoc-current.sh`: the generator runs with `--update` to write the committed page + links; a CI step runs it WITHOUT `--update` and `git diff --exit-code` **fails the build** if the index or any entrypoint link drifts from the registry. Add it next to the existing `godoc-current.txt up to date` step in `.forgejo/workflows/ci.yml` (and the GitHub-mirror twin). This is the teeth: a keyword added/removed/renamed without regenerating -> red CI. `derived` + a regenerate-and-diff gate is the canonical pairing - the marker classifies it, the diff enforces it. ## Acceptance - A generated, fully-flat, **names-only** keyword index page exists; every keyword links to both its doc page and its command entrypoint. - Every keyword doc page links to its command entrypoint. - Keywords come from ONE registry - no hand-maintained duplicate anywhere. - CI fails if a keyword is added/removed/renamed without regenerating; freshness marker present and graded `derived`. - The page stays within the markdown size limit (names only). ## Files `http/guardfile/guardfile.go` + `cli/execverb/guardfile.go` + `http/specverb/*.go` (keyword registry / source of truth), `scripts/gen-webdocs/main.go` (`writeIndex` + `layout.BuildNavList` - index generation), `docs/` (generated index page + per-keyword entrypoint links), `.forgejo/workflows/ci.yml` + `.github/workflows/ci.yml` (hard drift gate), `scripts/check-godoc-current.sh` (the freshness-gate template to mirror), and the aos freshness program for reference (`agentic-os/agentic_os/freshness.py`, `docs/knowledge-base-freshness.md`). ## Dependency Rebase on / land after the `specgen` -> kdl-specs rename (#174) so the generator lands on the renamed surface. ## When done Run the gate, commit, push to canonical `main`, update `docs/FEATURES.md`.
Author
Owner

The `specgen` -> kdl-specs rename (#174) has landed on `main` (commit `0359c55`): Go package `kdlspecs`, command/binary `kdl-specs`, docs at `docs/kdl-specs.md`. No `specgen` literal remains in code or docs. Build this feature on the renamed surface, or rebase onto it.

The \`specgen\` -> **kdl-specs** rename (#174) has landed on \`main\` (commit \`0359c55\`): Go package \`kdlspecs\`, command/binary \`kdl-specs\`, docs at \`docs/kdl-specs.md\`. No \`specgen\` literal remains in code or docs. Build this feature on the renamed surface, or rebase onto it.
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/cli-guard#175
No description provided.