Skip to content

Preserve handwritten Git API during coverage validation - #4037

Open
thomhurst wants to merge 6 commits into
mainfrom
issue-3996-git-help-streams-20260823
Open

Preserve handwritten Git API during coverage validation#4037
thomhurst wants to merge 6 commits into
mainfrom
issue-3996-git-help-streams-20260823

Conversation

@thomhurst

Copy link
Copy Markdown
Owner

Part of #3996

Summary

  • consume Git short help from both stdout and stderr even when usage exits nonzero
  • retain established plumbing-command coverage and discover remote/worktree children from usage rows
  • validate command coverage without generating or cleaning code for the intentionally handwritten Git integration

Validation

  • OptionsGenerator Release build: 0 warnings, 0 errors
  • OptionsGenerator tests: 827/827 passed
  • focused Git scraper tests: 3/3 passed
  • real installed Git scrape: 140 commands, 0 errors
  • ModularPipelines.Git Release solution build after scrape: 0 warnings, 0 errors

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 13 minutes.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cd0aa52f-aada-4d97-b529-79e61c04c870

📥 Commits

Reviewing files that changed from the base of the PR and between d3eda6c and 1bd819e.

⛔ Files ignored due to path filters (1)
  • src/ModularPipelines.Git/Generated/Git.CommandCoverage.json is excluded by !**/generated/**
📒 Files selected for processing (6)
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Generators/CodeGeneratorOrchestratorTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/GitCliScraperTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Generators/CodeGeneratorOrchestrator.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Models/CliToolDefinition.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/CliScraperBase.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GitCliScraper.cs

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.

@thomhurst

Copy link
Copy Markdown
Owner Author

Addressed the CodeFactor finding in 3ba9972 by extracting code-generation, cleanup, and assembly-info branches from GenerateForToolAsync. Behavior is unchanged; GenerateCode=false still performs coverage validation while preserving handwritten Git API files.\n\nValidated locally:\n- GitCliScraperTests: 3/3 passed\n- CodeGeneratorOrchestratorTests: 16/16 passed\n- OptionsGenerator Release build: 0 warnings, 0 errors\n- ModularPipelines.Git Release build: 0 warnings, 0 errors\n- git diff --check: clean

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Greptile Summary

The PR preserves the handwritten Git API while continuing to scrape and validate command coverage.

  • Adds an explicit generation gate that skips code generation, cleanup, and assembly metadata for handwritten integrations.
  • Moves Git scraping onto the shared CLI scraper infrastructure and discovers usage-derived child commands.
  • Reads Git short help from both output streams and updates the committed command-coverage manifest.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Generators/CodeGeneratorOrchestrator.cs Gates generated files, cleanup, and assembly metadata behind GenerateCode while preserving containment and coverage validation.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GitCliScraper.cs Integrates Git with the shared scraper, combines short-help streams, and derives nested commands from usage rows.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/CliScraperBase.cs Adds path-aware subcommand extraction and propagates command-facade configuration into tool definitions.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Models/CliToolDefinition.cs Introduces the GenerateCode switch for integrations whose APIs are intentionally maintained by hand.
src/ModularPipelines.Git/Generated/Git.CommandCoverage.json Refreshes Git command coverage with usage-derived command groups and the installed Git version.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Run Git scraper] --> B[Read help from stdout and stderr]
    B --> C[Discover top-level and usage-derived commands]
    C --> D[Build command definitions]
    D --> E[Validate command coverage]
    E --> F[Write coverage manifest]
    D --> G{GenerateCode?}
    G -->|Yes| H[Generate and reconcile artifacts]
    G -->|No| I[Preserve handwritten Git API]
Loading

Reviews (5): Last reviewed commit: "fix(generator): preserve facade ownershi..." | Re-trigger Greptile

@claude claude 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.

Review: PR #4037 — Git CLI scraper subcommand discovery + GenerateCode gate

Nice change overall — generalizing help-text parsing to discover remote/worktree children via usage lines (instead of hardcoding them) is a good direction, and gating generation/cleanup/assembly-info behind a single GenerateCode flag is a cleaner design than the ad-hoc GenerateCommandFacade checks scattered across three generators. Good test coverage added for both the scraper and orchestrator paths.

A few things worth addressing:

1. CommandGroups is still hardcoded, undermining the PR's own goal

GitCliScraper.cs:20-21

private static readonly HashSet<string> CommandGroups =
    ["remote", "worktree"];

The new ExtractSubcommands/usage-line parser is fully generic — it works off any usage: git X ... / or: git X <subcmd> ... block. But it's only ever invoked for commands in this hardcoded set. Verified against real git 2.55.0 help output: git stash -h, git submodule -h, git bundle -h, and git notes -h all emit the same usage-line shape the parser targets, so stash pop, submodule add, bundle create, etc. are silently never scraped or fed into CommandCoverageGuard.

Suggestion: drop CommandGroups and instead attempt subcommand discovery for every top-level command, treating "usage-line parser found no children" as the natural no-op case for genuinely flat commands (e.g. git status). That's a better architecture here because it makes the scraper self-describing from git's own help output rather than requiring a maintainer to remember to update a second list every time a new grouped command needs coverage — which is exactly the kind of manual-sync problem this PR is trying to move away from.

2. Duplicate git <command> -h invocation per group

GitCliScraper.cs:339 and GitCliScraper.cs:431

ScrapeAsync calls ParseCommandAsync([command]), which runs git remote -h. Immediately after, for anything in CommandGroups, DiscoverSubcommandsAsync(command) runs git remote -h again to get the same help text. This doubles process spawns for every grouped command with no behavioral difference. Since ParseCommandAsync already has the combined help text in scope, consider having it return (CliCommandDefinition?, string helpText) (or exposing a lower-level helper both callers can share) so the second invocation is unnecessary.

3. Child commands can reference a parent that was never yielded

GitCliScraper.cs:84-93

var definition = await ParseCommandAsync([command], cancellationToken);
if (definition is not null) { ... yield return definition; }

if (!CommandGroups.Contains(command)) { continue; }

foreach (var subcommand in await DiscoverSubcommandsAsync(command, cancellationToken))
{
    var childDefinition = await ParseCommandAsync([command, subcommand], cancellationToken);
    ...
    yield return childDefinition;
}

The subcommand-discovery branch doesn't check whether definition was non-null before proceeding. If git remote -h ever returns empty output on both streams (unusual, but ParseCommandAsync explicitly handles that case by returning null), no GitRemoteOptions definition gets yielded, yet children still get yielded with ParentClassName = "GitRemoteOptions" — a class that won't exist in the generated output. This is a low-probability edge case for real git, but worth an explicit guard (if (definition is null) { continue; } before the subcommand loop) so a downstream consumer never gets a dangling parent reference.

4. GenerateCommandFacade = false is now dead configuration for Git

GitCliScraper.cs:49-50, and the three generators listed above that read it

GenerateCommandFacade is only consulted inside DependencyRegistrationGenerator, ServiceImplementationGenerator, and ServiceInterfaceGenerator. Since CodeGeneratorOrchestrator.GenerateFilesAsync now returns [] immediately whenever !toolDefinition.GenerateCode, none of those generators ever run for the Git tool definition anymore — so GenerateCommandFacade on Git's CliToolDefinition is unreachable dead config. Recommend removing it from GitCliScraper.CreateToolDefinition() (or adding a comment noting it's now superseded by GenerateCode) so a future reader doesn't spend time tuning a flag that has no effect.


None of these are blockers — #1 is the one I'd push back on before merging since it directly limits the stated purpose of this PR (broader coverage validation for hand-maintained Git commands); #2#4 are efficiency/robustness/cleanup nits.

@thomhurst
thomhurst force-pushed the issue-3996-git-help-streams-20260823 branch from 3ba9972 to fb37300 Compare August 23, 2026 22:45
@thomhurst

Copy link
Copy Markdown
Owner Author

Rebased two commits onto current main after Kevlar 0.9 (#4032) and force-pushed with an exact lease. Validation: GitCliScraperTests 3/3; CodeGeneratorOrchestratorTests 16/16; OptionsGenerator and ModularPipelines.Git Release builds both 0 warnings/errors; diff check clean. Verified remote head: fb37300.

@thomhurst

Copy link
Copy Markdown
Owner Author

Addressed all four review items at exact head b7577983c5:

  • every top-level Git command now derives children from its own usage text; no hardcoded group allowlist;
  • parent help is executed once and reused for child discovery;
  • a missing parent definition now stops child emission, preventing dangling ParentClassName references;
  • removed Git's superseded GenerateCommandFacade setting;
  • added a generic stash group regression that also proves parent help executes once.

Validation: GitCliScraperTests 4/4; CodeGeneratorOrchestratorTests 16/16; diff check clean.

@claude Please review exact head b7577983c55914de9e69aa0556306d4be6e49a70.

thomhurst added a commit that referenced this pull request Aug 24, 2026
Reuse shared traversal and reject operand-shaped usage groups.\n\nRefs #4037
@thomhurst
thomhurst force-pushed the issue-3996-git-help-streams-20260823 branch from b757798 to 82ad98a Compare August 24, 2026 05:20
@thomhurst

Copy link
Copy Markdown
Owner Author

@claude Please re-review exact head 82ad98a. All seven prior threads are addressed and resolved. Local validation: OptionsGenerator build passed with 0 warnings/errors; 917 tests passed; Git 2.55.0.windows.5 regeneration is stable at 178 commands without shrinkage approval.

@thomhurst
thomhurst force-pushed the issue-3996-git-help-streams-20260823 branch from 96d4178 to 7920d8a Compare August 24, 2026 05:32
@thomhurst
thomhurst force-pushed the issue-3996-git-help-streams-20260823 branch from 7920d8a to 1bd819e Compare August 24, 2026 05:40
@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review: re-review at head 1bd819eebc6413670154bafabf80da7731edc5c4

Verified the four items from my previous review against the current diff (base d3eda6c8 → head 1bd819ee), and independently built + ran the affected tests.

Previous findings — status

  1. Hardcoded CommandGroups — ✅ Fixed. GitCliScraper now derives subcommands for every top-level command generically from usage:/or: lines (GitCliScraper.cs), no allowlist. Verified against the real installed git 2.55.0 help text for stash, remote, worktree, and submodule — the bracket/alternation parser (TryExtractLeadingSubcommand/TryReadBracketGroup) correctly recovers subcommands like stash pop/stash apply/stash export even though they sit after multi-token option groups, and correctly rejects bracketed operand labels like [options] (via the "no optional candidates unless a required one was also found" rule) — matches the new ExtractSubcommands_* unit tests.
  2. Duplicate git <command> -h invocation per group — ✅ Fixed. The rewritten shared traversal in CliScraperBase fetches help text once per path and passes it to both subcommand-extraction and parsing; GitCliScraper's own IHelpTextCache is a second line of defense. Covered by ScrapeAsync_Discovers_Generic_Groups_Without_Repeating_Parent_Help, which asserts exactly 1 invocation of stash -h.
  3. Child yielded with a parent that was never emitted — ✅ N/A for Git now. GitCliScraper no longer owns its own scrape loop; it delegates entirely to CliScraperBase's shared traversal (pre-existing code, unchanged by this PR), so the git-specific version of this bug can no longer occur. (The shared traversal has its own, unrelated, pre-existing behavior here — out of scope for this PR.)
  4. GenerateCommandFacade = false on Git being dead config — Understood differently than my original comment suggested. It's still set on GitCliScraper, but I confirmed it isn't actually dead: ToolCatalog/ToolCatalogTests reads GenerateCommandFacade straight off the scraper instance independent of CreateToolDefinition()/GenerateCode, so removing it would break ToolCatalogTests.RegisteredCatalog_ContainsEveryPlatformAndValidMetadata. No change needed here — I was wrong to suggest removing it.

New checks

  • The GenerateCode gate (CodeGeneratorOrchestrator.cs) is well covered by the new Handwritten_Tool_Validates_Coverage_Without_Generation_Or_Cleanup test — confirms generators are skipped, AssemblyInfo.Generated.cs isn't written, cleanup doesn't run, and pre-existing files survive, while coverage validation still runs.
  • Confirmed no handwritten files under src/ModularPipelines.Git/ changed — only Git.CommandCoverage.json — matching the PR's stated goal of preserving the handwritten facade.
  • Built tools/ModularPipelines.OptionsGenerator/ModularPipelines.OptionsGenerator.slnx (Release): 0 warnings, 0 errors.
  • Ran ModularPipelines.OptionsGenerator.Tests: 923/923 passed.

No new issues found. This addresses all prior feedback; good to merge from my end.

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