Skip to content

perf: keep rarely declared command metadata out of every command's table - #1472

Merged
jdx merged 3 commits into
mainfrom
claude/command-meta-extra
Sep 23, 2026
Merged

jdx merged 3 commits into
mainfrom
claude/command-meta-extra

Conversation

@jdx

@jdx jdx commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

https://entire.io/gh/jdx/usage/trails/24

oxc is moving oxlint and oxfmt from bpaf to usage-rs (oxc-project/oxc#26027), and reviewers weigh how much binary size usage-rs adds. Much of that is static metadata. Each CommandMeta was about 600 bytes. A derived CLI emits one for every subcommand and every flattened Args type, so oxlint has about 11. Nearly every field in them was empty.

This applies the #1470 FlagMeta → FlagExtra split to commands. CommandMeta keeps only what nearly every command sets: cmd, about, long_about, hide, args_override_self, flags, args, subcommands, groups, flatten_groups. Everything else is now in a CommandExtra behind CommandMeta::extra. That covers deprecation, hidden aliases, heading and display order, surface/available_if, effect, mount, restart token, clause, subcommand settings, next_line_help/flatten_help, term widths, before/after help, examples, headings, outputs, select, and exit codes.

Derived tables build each command's extras as a constant. CommandExtra::shared then replaces an empty one with the single NO_COMMAND_EXTRA static, so a command that declares none of these costs one pointer. shared destructures every field without .., so a field added later and missed in the emptiness check is a compile error, not a silently dropped declaration. A test pins the sharing.

Size

binary vs #1470 overhead vs bpaf
oxlint −3,608 B +134,764 B (was +138,372)
oxfmt −1,488 B +112,796 B (was +114,284)

How this was measured: oxc's PR branch was patched to use this usage branch and built with oxc's release profile (opt-level 3, fat LTO, 1 codegen unit) on linux x86_64. The numbers are the summed allocated sections of the stripped binaries. Most of the saving is in .data.rel.ro (−4,128 B oxlint, −1,824 B oxfmt). Help, error, spec and completion output from both binaries was compared byte-for-byte against the #1470 build, and all 30 cases were identical.

ArgMeta was left alone. oxlint and oxfmt each have one positional, so splitting it would save a few hundred bytes at most.

Migration

Code that reads or builds CommandMeta directly has to go through extra for the moved fields. Derived CLIs need no changes.

// before
let text = meta.after_long_help;
static ROOT: CommandMeta = CommandMeta {
    cmd: &CMD,
    about: Some("…"),
    deprecated: Some("use `new`"),
    ..CommandMeta::EMPTY
};

// after
let text = meta.extra.after_long_help;
static ROOT: CommandMeta = CommandMeta {
    cmd: &CMD,
    about: Some("…"),
    extra: &CommandExtra {
        deprecated: Some("use `new`"),
        ..CommandExtra::EMPTY
    },
    ..CommandMeta::EMPTY
};

Validation

cargo test --workspace --all-features passes (2,801 tests, no snapshot changes), and cargo clippy --workspace --all-targets --all-features -- -D warnings is clean.

🤖 Generated with Claude Code


Note

Low Risk
Layout/API refactor across help, completion, and spec emission with broad test coverage; runtime CLI behavior should be unchanged if all extra paths were migrated consistently.

Overview
Introduces CommandExtra and moves rarely set command metadata (deprecation, hidden aliases, clause/restart tokens, help layout, examples, outputs, effects, etc.) off CommandMeta behind meta.extra, mirroring the existing FlagExtra pattern. CommandMeta keeps the common parse/help surface (flags, args, subcommands, hide, …) so each static table is smaller; derived CLIs call CommandExtra::shared to point empty extras at a single NO_COMMAND_EXTRA static.

All readers/writers are updated to use extra.*: shell completion, help rendering and usage lines, Spec::to_kdl, conformance KDL→tables building, usage-dynamic collision checks, and derive codegen (root/subcommand/enum variant metadata). Hand-built CommandMeta in tests and fixtures nest moved fields under CommandExtra { .. }.

Breaking for direct CommandMeta users only: field access becomes meta.extra.after_help (etc.); behavior and emitted help/spec/completions are intended to stay the same.

Reviewed by Cursor Bugbot for commit a2f8dbb. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Refactor
    • Updated how command settings are organized across command definitions and related tools. Help text, examples, completion behavior, deprecation details, and other settings retain their existing behavior.
    • Generated commands and specifications continue to preserve their existing metadata and help-page output.
  • User Impact
    • No changes to command-line behavior or help-page content are indicated.

CommandMeta was about 600 bytes per command, and a derived CLI emits one
for every subcommand and every flattened `Args` type, nearly all of it
empty. Deprecation, hidden aliases, help prose and examples, layout
settings, outputs, exit codes and the other spec-only fields now live in
a CommandExtra behind `CommandMeta::extra`. Derived tables build each
command's extras as a constant and `CommandExtra::shared` swaps an empty
one for the single NO_COMMAND_EXTRA static, so a command that declares
none of these costs a pointer.

Code that reads a moved field goes through `extra`
(`meta.extra.after_long_help`), and hand-written tables set them inside
`extra: &CommandExtra { .. }`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: 7f92acf7-5a83-418b-8090-e3f111e7758b

📥 Commits

Reviewing files that changed from the base of the PR and between 74f3ac6 and a2f8dbb.

📒 Files selected for processing (1)
  • argv/src/help.rs

Included review availability: Your plan provides up to 4 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The pull request moves extended command metadata from CommandMeta into a nested CommandExtra structure. Metadata generation, runtime readers, spec serialization, conformance builders, and tests now use the nested fields.

Changes

Command metadata restructuring

Layer / File(s) Summary
Metadata contract and generation
argv/src/spec.rs, derive/src/codegen.rs
CommandMeta now holds extended metadata through extra: &CommandExtra. Code generation places and inherits those values through CommandExtra; empty extras can share NO_COMMAND_EXTRA.
Runtime metadata consumers
argv/src/complete.rs, argv/src/help.rs, usage-dynamic/src/lib.rs, cli/src/command_effects.rs, benches/gate/tests/help.rs, usage-rs/tests/*
Completion, help rendering, dynamic lookup, and effect checks read extended metadata through extra. Associated fixtures and assertions use the nested structure.
Portable spec serialization
argv/src/spec.rs, conformance/src/tables.rs, conformance/tests/*
Spec serialization and conformance builders read and construct extended metadata through extra. Round-trip and metadata tests use the revised access paths.

Priority: ⚪ Not assessed

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Suggested reviewers: lu-zero

Merge Risk: ⚪ Minimal · up to a2f8d

No actionable merge-blocking issue is established; the PR is ready to merge after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.94% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 77 functions across 12 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: moving rarely declared command metadata out of each command's table for performance and size improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with no outstanding correctness, security, or repository-rule issues identified.

Summary

This PR reduces generated command metadata size by moving rarely declared fields into a shared CommandExtra, updating generated and hand-written metadata consumers accordingly. Changes since the previous review also simplify colored-help rendering by collecting styling structure while constructing the page.

  • Adds CommandExtra and a shared empty instance for commands without uncommon metadata.
  • Updates derive output, specification emission, help, completion, dynamic command resolution, effects, and conformance builders to use CommandMeta::extra.
  • Adds coverage for empty-extra sharing and colored help across templates, flattened commands, global flags, and next-line layouts.

Reviews (3) · Last reviewed commit: "Merge main into claude/command-meta-extr..."

jdx and others added 2 commits September 23, 2026 12:03
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdx
jdx merged commit b145daa into main Sep 23, 2026
13 checks passed
@jdx
jdx deleted the claude/command-meta-extra branch September 23, 2026 13:25
jdx pushed a commit that referenced this pull request Sep 23, 2026
<!-- entire-trail-link-start -->
https://entire.io/gh/jdx/usage/trails/16
<!-- entire-trail-link-end -->

### 🐛 Bug Fixes

- **(help)** colour only the structure the renderer writes by
[@jdx](https://github.com/jdx) in
[#1480](#1480)

### ⚡ Performance

- shrink the help and completion code usage-rs adds to a binary by
[@jdx](https://github.com/jdx) in
[#1469](#1469)
- keep rarely declared flag metadata out of every flag's table by
[@jdx](https://github.com/jdx) in
[#1470](#1470)
- write help rows through one renderer for both help pages by
[@jdx](https://github.com/jdx) in
[#1471](#1471)
- collect coloured-help structure while the page is written by
[@jdx](https://github.com/jdx) in
[#1476](#1476)
- shrink the per-CLI code the derive generates for binding by
[@jdx](https://github.com/jdx) in
[#1477](#1477)
- shrink the parse-error renderer usage-rs adds to a binary by
[@jdx](https://github.com/jdx) in
[#1478](#1478)
- shrink the shell-completion code a derived CLI carries by
[@jdx](https://github.com/jdx) in
[#1475](#1475)
- keep rarely declared command metadata out of every command's table by
[@jdx](https://github.com/jdx) in
[#1472](#1472)
- shrink the command and metadata tables a derive emits by
[@jdx](https://github.com/jdx) in
[#1474](#1474)

### 🔍 Other Changes

- **(ci)** comment on a discussion when the PR implementing it merges by
[@jdx](https://github.com/jdx) in
[#1460](#1460)

### 📦️ Dependency Updates

- update jdx/packslip action to v1.2.0 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1466](#1466)
- update zizmorcore/zizmor-action action to v0.6.4 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1465](#1465)
- update module github.com/urfave/cli/v3 to v3.12.0 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1467](#1467)
- update aube to v2.3.0 by [@jdx](https://github.com/jdx) in
[#1473](#1473)
@github-actions

Copy link
Copy Markdown
Contributor

Instruction counts

The comparison never ran — an earlier step failed.

a2f8dbbcd750 vs `` · measured on the runner, not pushed to the history.

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