Skip to content

Remove dead generated Git facade - #3575

Merged
thomhurst merged 2 commits into
mainfrom
issue-3488-git-interface
Jul 31, 2026
Merged

Remove dead generated Git facade#3575
thomhurst merged 2 commits into
mainfrom
issue-3488-git-interface

Conversation

@thomhurst

@thomhurst thomhurst commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • remove the unregistered ModularPipelines.Git.Services.IGit / Services.Git command facade
  • keep the hand-written grouped ModularPipelines.Git.IGit facade as the only exported Git entry point
  • add GenerateCommandFacade metadata so scheduled Git option generation cannot recreate the dead facade or a conflicting DI extension
  • preserve all scraper metadata through orchestration and keep Git's dedicated grouped-facade documentation
  • add generator, scraper, and exported API regression coverage

Breaking change

This intentionally removes the public but unreachable ModularPipelines.Git.Services.IGit type for v4. Consumers should use ModularPipelines.Git.IGit and its Commands, Information, and Versioning groups.

Validation

  • OptionsGenerator tests: 691 passed
  • Git tests: 2 passed
  • OptionsGenerator Release build: 0 warnings, 0 errors
  • Git Release build: 0 errors (222 existing warnings)
  • scoped formatting verification passed
  • compiled Git assembly exports exactly one IGit: ModularPipelines.Git.IGit

Closes #3488

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 66170d11d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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

Code review

Reviewed the removal of the dead generated Git command facade and the new GenerateCommandFacade gating in the OptionsGenerator. Overall this is a clean, well-tested cleanup: the deletion of Git.Generated.cs/IGit.Generated.cs doesn't leave any dangling references, the hand-written grouped IGit remains the sole public surface (locked in by GitApiSurfaceTests), and the generator changes correctly avoid regenerating the dead facade going forward. CLAUDE.md compliance was also checked (generated-file handling, code-gen architecture, test project conventions) — no violations found.

One architectural gap surfaced in the new gating logic that's worth addressing before it bites a future tool integration:

GenerateCommandFacade isn't consistently respected across the generator set

public Task<IReadOnlyList<GeneratedFile>> GenerateAsync(CliToolDefinition tool, CancellationToken cancellationToken = default)
{
if (!tool.GenerateCommandFacade)
{
return Task.FromResult<IReadOnlyList<GeneratedFile>>([]);
}

DependencyRegistrationGenerator now short-circuits entirely when GenerateCommandFacade is false. That's correct for the facade registration itself, but the same method is also the sole place that emits sub-domain service DI registrations and the [ModularPipelinesIntegration] Register{Tool}Context entry point (lines 79–104 of that file). SubDomainClassGenerator, meanwhile, is not gated by GenerateCommandFacade at all and will still emit sub-domain classes unconditionally.

Git is unaffected today only because it happens to set SubDomainGroup = null for all its commands and hand-provides RegisterGitContext in GitExtensions.cs. But for any future tool that sets GenerateCommandFacade = false while also having SubDomainGroups (a combination the new GeneratorHardeningTests.Command_Facade_Generation_Can_Be_Disabled_Independently_Of_Options test doesn't exercise), the generator would emit sub-domain service classes with no DI registration and no context entry point — generated-but-unreachable/broken code, discovered only when someone tries to wire that tool up.

Suggested fix: rather than gating the whole DependencyRegistrationGenerator output on one flag, split "facade registration" from "sub-domain + entry-point registration" so the latter is emitted whenever SubDomainGroups is non-empty, independent of GenerateCommandFacade. That keeps the flag scoped to what it's meant to control (the flat command facade) and removes the latent trap for the next tool that combines both settings — it also makes SubDomainClassGenerator's unconditional behavior consistent with what actually gets registered, instead of two generators quietly relying on the same tool never using both options together.

(A second candidate issue — Git's new DocumentationOutputDirectory = null silently skipping markdown doc generation — was investigated but is not a real problem: no docs/docs/mp-packages/cli/git.md file exists or ever has, so there's nothing for the cleanup logic to strand.)

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

Code review (update)

Re-reviewed at the latest commit (ecde8a8b8/merge of 4ca8550c → head). The overall change is still solid: the dead ModularPipelines.Git.Services.IGit facade is fully removed with no dangling references, GitApiSurfaceTests locks the exported surface down to the single grouped IGit, and the new GitCliScraperTests / CodeGeneratorOrchestratorTests coverage correctly pins GenerateCommandFacade = false and DocumentationOutputDirectory = null for Git going forward. CodeGeneratorOrchestrator's switch to toolDefinition with { ... } for completeToolDefinition is also a nice simplification that removes the risk of a future property being silently dropped from that copy.

The gap from my previous review is still present and unaddressed — the diff for DependencyRegistrationGenerator.cs, ServiceInterfaceGenerator.cs, and ServiceImplementationGenerator.cs is byte-for-byte identical to the version reviewed earlier:

// DependencyRegistrationGenerator.GenerateAsync
if (!tool.GenerateCommandFacade)
{
    return Task.FromResult<IReadOnlyList<GeneratedFile>>([]);
}

This still means DependencyRegistrationGenerator — the only place that emits sub-domain DI registrations (services.TryAddScoped<I{Tool}{SubDomain}, ...>()) and the [ModularPipelinesIntegration] Register{Tool}Context entry point — short-circuits entirely when GenerateCommandFacade is false. SubDomainClassGenerator, however, is not gated by that flag at all (confirmed: no GenerateCommandFacade check anywhere in that file) and is invoked unconditionally by the orchestrator's foreach (var generator in _generators) loop. Git dodges this today only because it happens to have zero SubDomainGroups and Git hand-writes its own RegisterGitContext in GitExtensions.cs.

The new Command_Facade_Generation_Can_Be_Disabled_Independently_Of_Options test in GeneratorHardeningTests.cs still only exercises a tool with a single root-level command and no sub-domain groups, so it doesn't cover this combination and wouldn't catch it.

Net effect unchanged: the next tool integration that sets GenerateCommandFacade = false while also using SubDomainGroups (a very plausible combination — e.g. a tool that wants grouped sub-commands but a hand-written top-level facade, similar in shape to Git but without Git's null SubDomainGroups) will silently get SubDomainClassGenerator-emitted sub-domain classes with no DI registration and no context entry point. That's a broken build or a DI-resolution failure discovered by whoever tries to wire that tool up next, not by this generator's own test suite.

Suggested fix (unchanged from before): split DependencyRegistrationGenerator's output into two independently-gated pieces — "root facade registration" (gated on GenerateCommandFacade, since it depends on the generated I{Tool}/{Tool} types) and "sub-domain services + entry point registration" (gated on tool.SubDomainGroups.Count > 0, independent of the facade flag). That keeps GenerateCommandFacade scoped to what its doc comment now says it controls, and removes the latent trap instead of relying on every future tool integration avoiding the combination by accident.

This is worth fixing before merge (or as an immediate fast-follow) since it's a correctness trap for the next consumer of GenerateCommandFacade, not a Git-specific concern — but it doesn't block the core goal of this PR (removing the dead Git facade), which is otherwise clean and well-tested.

@thomhurst

Copy link
Copy Markdown
Owner Author

Addressed the generator review at ce05b7e: when the root command facade is disabled, sub-domain services and the compile-time integration entry point are still generated; root registration/context access remain omitted. Added a regression and verified GeneratorHardeningTests (41/41).

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: ce05b7ed2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Code review

Reviewed the diff (Git facade removal, GenerateCommandFacade generator flag, and the associated generator/scraper tests) against the repo's CLAUDE.md and for bugs/logic errors.

No issues found.

A few things worth calling out as good design choices:

  • Swapping the manual field-by-field CliToolDefinition reconstruction in CodeGeneratorOrchestrator.GenerateFromCliAsync for a toolDefinition with { ... } expression (tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Generators/CodeGeneratorOrchestrator.cs) is a solid simplification — it removes an entire class of "forgot to copy a new property" bugs (which is effectively what this PR needed to guard GenerateCommandFacade/DocumentationOutputDirectory against), instead of just patching the symptom.
  • GenerateCommandFacade is threaded through ServiceInterfaceGenerator, ServiceImplementationGenerator, and DependencyRegistrationGenerator consistently, and the SubDomainGroups.Count == 0 short-circuit in DependencyRegistrationGenerator correctly distinguishes "no facade, no sub-domains → generate nothing" from "no facade, but sub-domains exist → still register sub-domain services," matching the follow-up fix described in the PR thread.
  • Verified there are no remaining references to the deleted ModularPipelines.Git.Services.Git/IGit types anywhere in the repo, and the new GitApiSurfaceTests, GitCliScraperTests, and GeneratorHardeningTests cases directly cover the behavior this PR is protecting (single exported IGit, facade generation opt-out, and scraper metadata for Git).

No CLAUDE.md compliance concerns for the touched files (core build/testing guidance doesn't apply to source changes directly, and no generated-file editing rules were violated — the deleted files were the generated ones, not hand edits to them).

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Code review

No new changes to review — the diff for all 12 changed files is byte-for-byte identical to the version already reviewed in the previous comment (commit ce05b7ed2a), which found no issues after the GenerateCommandFacade/DependencyRegistrationGenerator gap was addressed. The two commits added since then are merge commits from main that don't touch any file in this PR's diff.

Standing verdict: no issues found. The Git dead-facade removal is clean, the new GenerateCommandFacade gating correctly separates root-facade registration from sub-domain service registration, and the added GitApiSurfaceTests/GeneratorHardeningTests/GitCliScraperTests/CodeGeneratorOrchestratorTests coverage locks in the intended behavior.

@thomhurst
thomhurst force-pushed the issue-3488-git-interface branch from 169013e to 209f583 Compare July 31, 2026 00:04
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Code review

Re-reviewed after the branch was rebased onto main (head now 209f583be6, two commits: fix(git): remove dead generated facade and fix(generator): register facade-free groups). The diff content is unchanged from what was already reviewed in the two prior comments on this PR — same 12 files, same +152/-854 shape.

No issues found. Confirmed independently:

  • src/ModularPipelines.Git/Services/ (the dead generated Git/IGit facade) is fully removed from the tree at the PR head commit; the hand-written grouped ModularPipelines.Git.IGit facade (src/ModularPipelines.Git/IGit.cs, Git.cs, etc.) is untouched and remains the sole public entry point.
  • GenerateCommandFacade is threaded consistently through ServiceInterfaceGenerator, ServiceImplementationGenerator, and DependencyRegistrationGenerator. In DependencyRegistrationGenerator, the early-return (!GenerateCommandFacade && SubDomainGroups.Count == 0 → emit nothing) is correctly distinct from the "facade disabled but sub-domains exist" case, where sub-domain registrations and using ModularPipelines.Context;/RegisterXContext are still emitted only where actually needed — no dangling using directive or dead code path when the facade is off.
  • The toolDefinition with { Commands = ..., ToolVersion = ..., Errors = [] } refactor in CodeGeneratorOrchestrator.GenerateFromCliAsync (replacing manual field-by-field reconstruction) is a good simplification: it's exactly the kind of change that prevents a newly added record property (like GenerateCommandFacade here) from silently being dropped during orchestration in the future.
  • New coverage (GitApiSurfaceTests, GitCliScraperTests, GeneratorHardeningTests additions, CodeGeneratorOrchestratorTests additions) directly locks in the behaviors this PR relies on: exactly one exported IGit, the Git scraper opting out of facade generation, and the generator's facade-disabled/sub-domain-registration split.

No CLAUDE.md concerns — no full-solution or full-build-pipeline commands were needed to review this (source/test diff only), and none of the touched files are hand-edits to *.Generated.cs outputs (those were only deleted, per the intended breaking change).

@thomhurst
thomhurst force-pushed the issue-3488-git-interface branch from 209f583 to 63f793a Compare July 31, 2026 01:09
@thomhurst

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 63f793a745

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Code review

No new changes to review — the diff is byte-for-byte identical to the version already reviewed in the three prior comments on this PR (same 12 files, same +152/-854 shape). The branch has been rebased again (head now 63f793a7452c701077af4621938ffaca795e8b67), but the two commits (fix(git): remove dead generated facade, fix(generator): register facade-free groups) carry the same content as before.

Standing verdict: no issues found. The dead ModularPipelines.Git.Services.IGit facade removal is clean, the GenerateCommandFacade gating correctly separates root-facade registration from sub-domain service registration in DependencyRegistrationGenerator, and the added GitApiSurfaceTests/GeneratorHardeningTests/GitCliScraperTests/CodeGeneratorOrchestratorTests coverage locks in the intended behavior. No CLAUDE.md compliance concerns for the touched files.

@thomhurst
thomhurst merged commit dcb8b80 into main Jul 31, 2026
15 checks passed
@thomhurst
thomhurst deleted the issue-3488-git-interface branch July 31, 2026 02:04
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.

v4: Git package ships a dead public generated Services.IGit colliding with the real IGit facade

1 participant