Skip to content

Generate CLI reference from the clap model + cli-doc drift gate (P5.3)#32

Merged
LayerDynamics merged 1 commit into
docs-pipeline-p52-clap-migrationfrom
docs-pipeline-p53-cli-generator
Jun 8, 2026
Merged

Generate CLI reference from the clap model + cli-doc drift gate (P5.3)#32
LayerDynamics merged 1 commit into
docs-pipeline-p52-clap-migrationfrom
docs-pipeline-p53-cli-generator

Conversation

@LayerDynamics

Copy link
Copy Markdown
Owner

Summary

Phase P5.3 (final phase) of the docs-autogeneration pipeline: generate the forge CLI reference directly from the clap command model and gate it against drift. The published docs and the binary's argument parser are now derived from the same model and cannot diverge.

Stacked on #31 (P5.2). This PR's base is the P5.2 branch, so the diff shows only the single P5.3 commit. Once #31 merges to main, retarget this PR to main (gh pr edit --base main).

What changed

forge_cli → lib + bin split

  • The clap model (Cli/Commands/IconCommand + AFTER_HELP) moves to src/lib.rs, which exposes pub fn cli() -> clap::Command. The binary imports it and dispatches as before; command handlers stay in the bin. Smallest lib surface that lets tooling introspect the real CLI.

forge-docs-check

  • New clidoc module: introspects forge_cli::cli() and renders an authoritative reference (synopsis, arguments, options, nested icon subcommands) into the <!-- forge:cli --> region of crates/forge.md. Authored prose in the narrative ## Commands section sits outside the markers and is never touched.
  • New cli-doc rule (wired into run_all_checks): fails when the region is stale. make docs-cli / --write-cli regenerates it. Mirrors the api-block/example-block marker-hybrid pattern (and inherits its CRLF-safe comparison).
  • cli-command rule refactored from regex source-parsing to clap introspection — deletes ~120 lines of brace/heck-mangling and the source-move fragility. The clap model is now the literal source of truth.

Docs/config

  • forge.md: new generated ## CLI reference section + a lib.rs entry in the file-structure tree.
  • DOCUMENTATION.md: documents the cli-doc (and previously-undocumented ext-index) rules and make docs-cli.
  • Makefile: adds the docs-cli target.

Test plan

  • Drift gate: in sync, 0 issues (new cli-doc rule passes)
  • forge-docs-check: 18 fixture rules + docs_sync ratchet + 3 clidoc unit tests + new cli-doc fixture (stale flagged / fresh passes / un-opted skipped)
  • forge_cli: 15 unit + 10 characterization
  • cargo fmt --all --check + clippy -D warnings clean
  • Windows CI green on the drift gate (the new surface) — watched on this PR

Notes

  • clidoc::render_block_body() prefixes the literal "forge" (not the binary name), so the .exe issue that bit P5.2's test cannot leak into the generated docs.
  • No dependency cycle: forge-docs-check → forge_cli → forge-etch/forge-smelt, nothing back.

🤖 Generated with Claude Code

Generate the `forge` CLI reference in crates/forge.md directly from the clap
command model, and gate it against drift — the published docs and the binary's
argument parser are now the same model and cannot diverge.

forge_cli: split into lib + bin. The clap model (`Cli`/`Commands`/`IconCommand`
+ `AFTER_HELP`) moves to `src/lib.rs`, which exposes `pub fn cli() -> clap::Command`.
The binary imports it and dispatches as before; the command handlers stay in the
bin. This is the smallest lib surface that lets tooling introspect the real CLI.

forge-docs-check:
- New `clidoc` module: introspects `forge_cli::cli()` and renders an authoritative
  reference (synopsis, arguments, options, nested subcommands) into the
  `<!-- forge:cli -->` region of crates/forge.md. Authored prose in the narrative
  `## Commands` section sits outside the markers and is never touched.
- New `cli-doc` rule (wired into run_all_checks) fails when that region is stale;
  `make docs-cli` / `--write-cli` regenerates it. Mirrors the api-block/example-block
  marker-hybrid pattern.
- `cli-command` rule refactored from regex source-parsing to clap introspection
  (`forge_cli::cli()`), deleting ~120 lines of brace/heck-mangling parsing and the
  source-move fragility — the clap model is now the literal source of truth.

Docs/config: forge.md gains the generated `## CLI reference` section and a `lib.rs`
entry in its file-structure tree; DOCUMENTATION.md documents the `cli-doc` (and
previously-undocumented `ext-index`) rules and `make docs-cli`; Makefile adds the
`docs-cli` target.

Tests: clidoc unit tests (every subcommand, flags/positionals, nested icon, help
filtered out) + a fixture drift test (stale region flagged, fresh region passes,
un-opted page skipped). The cli-command fixture test updated for introspection.

Verification: drift gate in sync; forge-docs-check 18 rules + docs_sync + clidoc;
forge_cli 15 + 10; fmt --all + clippy -D warnings clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @LayerDynamics, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the forge CLI command model by moving it from the binary entry point to a library crate (forge_cli), allowing external tooling to introspect the CLI surface. It introduces a new cli-doc rule in forge-docs-check to automatically generate and verify the CLI reference in crates/forge.md directly from the live clap model, eliminating manual source-text parsing. Feedback on these changes suggests normalizing CRLF line endings to LF when reading and writing the CLI reference to prevent cross-platform test failures on Windows, as well as simplifying a string join operation in the value_name helper to avoid redundant allocations.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +55 to +56
match markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) {
Some((_, _, body)) if body == expected => Vec::new(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent test failures on Windows platforms where files might be checked out with CRLF line endings, normalize the line endings of the extracted region to LF (\n) before comparing it with the expected generated body.

Suggested change
match markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) {
Some((_, _, body)) if body == expected => Vec::new(),
match markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) {
Some((_, _, body)) if body.replace("\r\n", "\n") == expected => Vec::new(),

Comment on lines +80 to +83
if let Some((_, _, body)) = markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) {
if body == expected {
return Ok(Vec::new());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similarly, normalize CRLF line endings to LF in write_all to avoid rewriting the file unnecessarily when the only difference is the line endings (CRLF vs LF).

Suggested change
if let Some((_, _, body)) = markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) {
if body == expected {
return Ok(Vec::new());
}
if let Some((_, _, body)) = markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) {
if body.replace("\r\n", "\n") == expected {
return Ok(Vec::new());
}

Comment on lines +181 to +189
if let Some(names) = arg.get_value_names() {
if !names.is_empty() {
return names
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(" ");
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since names is a slice of String (or &str), you can directly call names.join(" ") instead of mapping and collecting into a temporary Vec. This is more concise, readable, and avoids unnecessary allocations.

    if let Some(names) = arg.get_value_names() {
        if !names.is_empty() {
            return names.join(" ");
        }
    }

@LayerDynamics LayerDynamics merged commit b76e3d4 into docs-pipeline-p52-clap-migration Jun 8, 2026
4 checks passed
LayerDynamics added a commit that referenced this pull request Jun 8, 2026
#33)

## Summary

Phase **P5.3** (final phase) of the docs-autogeneration pipeline:
generate the `forge` CLI reference **directly from the clap command
model** and gate it against drift.

> **Re-opened against `main`.** The original #32 was stacked on the P5.2
branch and got merged into *that* branch instead of `main`, so P5.3
never landed on `main` (`clidoc.rs` / `forge_cli/src/lib.rs` were
missing). This PR targets `main` directly. `main` already contains the
P5.2 fork point, so the diff is just the single P5.3 commit (plus the
merge of current `main`).

## What changed

**`forge_cli` → lib + bin split**
- The clap model (`Cli`/`Commands`/`IconCommand` + `AFTER_HELP`) moves
to `src/lib.rs`, exposing `pub fn cli() -> clap::Command`. The binary
imports it and dispatches as before; handlers stay in the bin.

**`forge-docs-check`**
- New `clidoc` module: introspects `forge_cli::cli()` and renders an
authoritative reference (synopsis, arguments, options, nested `icon`
subcommands) into the `<!-- forge:cli -->` region of `crates/forge.md`.
Authored prose stays outside the markers.
- New **`cli-doc`** rule fails when that region is stale; `make
docs-cli` / `--write-cli` regenerates it.
- **`cli-command`** rule refactored from regex source-parsing to clap
introspection — deletes ~120 lines of brace/heck-mangling.

**Docs/config:** `forge.md` generated `## CLI reference` + `lib.rs` in
the file tree; `DOCUMENTATION.md` documents the `cli-doc`/`ext-index`
rules and `make docs-cli`; `Makefile` adds `docs-cli`.

## Test plan
- [x] Merged with current `main` (branding + rusty_v8 CI cache fix);
zero conflicts
- [x] Drift gate **in sync**; forge-docs-check 18 rules + docs_sync +
clidoc + cli-doc fixture
- [x] `forge_cli` 15 + 10; build, `fmt --all --check`, `clippy -D
warnings` all clean
- [x] CI now inherits the `~/.cargo/.rusty_v8` cache from `main`, so the
V8-download 504s can't gate it

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant