fix: resolve all open issues — CLI strict parsing, EF record gaps, EffectiveTable dedup, PR #161 debt roundup (#162–#165) - #167
Merged
Conversation
The effective-table rule (owned type shares its owner's table unless it declares its own) was implemented verbatim in both FluentOwnedTypeWalker and MermaidErdRenderer, so a future change to EF table-sharing shapes could silently drift the capture and render paths apart. Hoist it onto EfEntity.EffectiveTable alongside EffectiveKey and point both call sites at it. Pure refactor - no golden moves. Closes #164 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Document ResolveSourceEntity's unbounded ancestor walk precondition: it is safe only while relationship chains are never captured inside owned builders; bounding is required if that ever changes. - Consolidate the nested-builder boundary rule into one FluentSyntax.IsNestedBuilderFence predicate shared by IsInsideNestedBuilderScope and ResolveOwningEntity's ancestor search. - Keep (not remove) ForeignKeyPropertyNames' lambda branch: contrary to the issue premise, OwnershipBuilder<TEntity,TDependentEntity> HAS an Expression HasForeignKey overload (EF Core 3.0+), so the branch is live; documented and pinned with a regression test instead. - Reword the OwnsMany because-string to drop the doubled-brace CA2241 workaround that rendered literally in failure messages. - Close test-coverage boundaries: assert the Address box exists before splitting in Classic_OwnerDoesNotGainNavigationColumn; new test that an inlined undrawn same-named sibling does not force a qualified label onto the sole rendered box; new OwnedFkConfigContext fixture exercising StripShadowKeys against a config-class owned type with explicit WithOwner().HasForeignKey/HasKey. Closes #165 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n, config-class scan Three ClassDeclarationSyntax/PropertyDeclarationSyntax-only sites kept record-declared types invisible to EF analysis: - Owned-nav pre-pass: a positional record's navigation is a primary-constructor parameter, not a property member, so its owned type's file was never discovered and the owned entity degraded to an empty box. The nav lookup now also reads RecordDeclarationSyntax.ParameterList. - EntityAnalyzer.ExtractPrimaryKeysFromSyntax: the [PrimaryKey]/[Key] syntax fallback skipped RecordDeclarationSyntax; widened to TypeDeclarationSyntax. - EntityConfigurationWalker.FindConfigClassesInRoot: a record-declared IEntityTypeConfiguration<T> was silently skipped; widened to TypeDeclarationSyntax. Each gap is pinned by a regression test verified to fail pre-fix. Closes #163 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…silently ignored Spectre.Console.Cli's default parser drops unrecognized long options on every command: 'projgraph erd <path> --owned-mod classic' (typo, missing 'e') exited 0 and silently rendered in mirror mode. Enable StrictParsing in Program.cs so any unknown option produces 'Error: Unknown option ...' and a non-zero exit code; mirror the setting in the CLI test harness app. The Program.Main-based regression test must remain the only test that reaches Spectre's default error rendering: the library freezes AnsiConsole.Console in a process-lifetime Lazy on first render, so a second such test would write to the first test's disposed capture writer (documented on the test). Verified against the real binary: the typo'd option now exits 255 with a pointed error; valid invocations still exit 0. Closes #162 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tionship-walk comment Multi-angle review of the branch surfaced four follow-ups: - EntityFileDiscovery.ProcessConfigurationFileAsync was still ClassDeclarationSyntax-only, so a record config class in a SEPARATE file never reached the (widened) EntityConfigurationWalker - the cross-file case is the one that matters. Widened, interfaces excluded. - EntityFileDiscovery.ExtractBaseClassNamesFromSyntax was class-only, so a record entity's record base type in another file was never pulled into the compilation and its inherited columns vanished. - The TypeDeclarationSyntax widening in EntityConfigurationWalker admitted interfaces with default-implemented Configure bodies, which EF's ApplyConfigurationsFromAssembly never applies; now excluded. - ExtractPrimaryKeysFromPropertySyntax also scans a positional record's [property: Key] primary-constructor parameters. - The FluentRelationshipWalker precondition comment claimed relationships inside owned builders are never captured; they ARE (FindRelationshipRoots fences only UsingEntity) and get attributed to the enclosing entity - reworded to state the real behavior. All behavioral fixes pinned by regression tests verified to fail pre-fix. Refs #163 #165 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR closes four open issues by tightening CLI option parsing, extending EF model/file-discovery to cover record-specific syntax gaps, and refactoring duplicated owned-entity table/inline logic into a single shared source of truth—backed by new regression tests.
Changes:
- Enable Spectre.Console.Cli strict parsing in the production CLI and test harness, and add integration coverage for unknown options failing fast.
- Fix multiple EF record-related discovery/analysis gaps (positional-record owned nav discovery, config/base-type cross-file discovery,
[property: Key]on record parameters) and pin them with regressions. - Centralize the “effective table” rule on
EfEntity.EffectiveTableand update capture + render paths to consume it.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/ProjGraph.Tests.Unit.EntityFramework/Rendering/OwnedTypeRenderingTests.cs | Adds regression coverage for owned-type label qualification and strengthens an existing classic-mode assertion. |
| tests/ProjGraph.Tests.Unit.EntityFramework/RecordTypeDiscoveryRegressionTests.cs | New end-to-end tests pin cross-file record config discovery and record base-type discovery. |
| tests/ProjGraph.Tests.Unit.EntityFramework/OwnedRecordOwnerRegressionTests.cs | Adds positional-record-owner + cross-file owned record regression to ensure owned columns are discovered/rendered. |
| tests/ProjGraph.Tests.Unit.EntityFramework/Infrastructure/FluentOwnedTypeWalkerTests.cs | Adds fixtures/tests for StripShadowKeys behavior and proves the lambda FK branch is reachable. |
| tests/ProjGraph.Tests.Unit.EntityFramework/Golden/fixtures/OwnedFkConfigContext.cs | New golden fixture exercising config-class-owned-type shapes similar to snapshot output. |
| tests/ProjGraph.Tests.Unit.EntityFramework/EntityConfigurationWalkerTests.cs | Adds coverage for record-declared config classes and excludes interfaces with default Configure. |
| tests/ProjGraph.Tests.Unit.EntityFramework/EntityAnalyzerTests.cs | Adds regressions for record-declared [PrimaryKey] and positional-record [property: Key]. |
| tests/ProjGraph.Tests.Integration.Cli/StrictParsingTests.cs | New integration tests asserting unknown long options fail (production path + harness path). |
| tests/ProjGraph.Tests.Integration.Cli/Helpers/CliTestHelpers.cs | Mirrors production strict parsing in the CLI test harness app configuration. |
| src/ProjGraph.Lib.EntityFramework/Rendering/MermaidErdRenderer.cs | Switches inline-vs-box decision to use EfEntity.EffectiveTable single-source rule. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/FluentSyntax.cs | Consolidates nested-builder boundary logic into a single predicate used by multiple walkers. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/FluentRelationshipWalker.cs | Documents the unbounded ancestor walk behavior/limitations for owned-builder relationship attribution. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/FluentOwnedTypeWalker.cs | Uses EffectiveTable single-source rule and documents/relies on live FK-extraction branches. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityFileDiscovery.cs | Extends cross-file config discovery and base-type extraction to TypeDeclarationSyntax (excluding interfaces). |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityConfigurationWalker.cs | Widens config-class discovery to TypeDeclarationSyntax and excludes interfaces to match EF behavior. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityAnalyzer.cs | Widens syntax fallback for PK discovery to records and adds record-parameter [property: Key] handling. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/EfModelAnalyzer.cs | Adds positional-record navigation type discovery via record primary-constructor parameter scanning. |
| src/ProjGraph.Core/Models/EfModel.cs | Introduces EfEntity.EffectiveTable as the canonical effective-table computation. |
| src/ProjGraph.Cli/Program.cs | Enables strict parsing to prevent silent acceptance of unknown long options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses all four open issues in one branch, one commit per issue plus a review-fix commit.
#162 — CLI: strict parsing
config.Settings.StrictParsing = trueinProgram.cs(mirrored in the CLI test-harness app). An unknown long option now printsError: Unknown option '...'with a caret pointer and exits non-zero; verified against the real binary (--owned-mod classic→ exit 255, valid invocations still exit 0). Integration tests run the realProgram.Mainconfiguration; the test file documents a Spectre.Console.Cli quirk (the default error renderer freezesAnsiConsole.Consolein a process-lifetimeLazy) that constrains how many tests may exercise the default render path.#163 — EF: record-type gaps
RecordDeclarationSyntax.ParameterList, sorecord Product(int Id, Money Price)discoversMoney.cscross-file instead of degrading to an empty box.EntityAnalyzer.ExtractPrimaryKeysFromSyntaxandEntityConfigurationWalker.FindConfigClassesInRootwidened fromClassDeclarationSyntaxtoTypeDeclarationSyntax.EntityFileDiscoverywere still class-only, and the[Key]syntax fallback missed[property: Key]on positional-record parameters. The widening also newly admitted interfaces with defaultConfigurebodies (which EF never applies) — now excluded.Every behavioral fix is pinned by a regression test verified to fail on the pre-fix sources.
#164 — EffectiveTable single source
The rule now lives once, as
EfEntity.EffectiveTablebesideEffectiveKey;FluentOwnedTypeWalkerandMermaidErdRendererboth consume it. Pure refactor — no golden moved.#165 — minor debt roundup
ResolveSourceEntity's unbounded ancestor walk is now documented — and the review found the issue's own premise ("relationships aren't captured inside owned builders") is false:FindRelationshipRootsfences onlyUsingEntity, so owned-builderHasOne/HasManychains are captured and attributed to the enclosing entity. The comment states the real behavior and the bounding required if owned relationships are ever modelled properly.OwnershipBuilder<TEntity,TDependentEntity>has had anExpressionHasForeignKeyoverload since EF Core 3.0 (docs), soForeignKeyPropertyNames' lambda branch is live; it is now documented and pinned by a regression test.FluentSyntax.IsNestedBuilderFencepredicate.becausestring reworded (no more literal{{OwnerTable}}_{{Nav}}in failure messages).Classic_OwnerDoesNotGainNavigationColumn; new inlined-undrawn-sibling label test; newOwnedFkConfigContextfixture exercisingStripShadowKeysagainst a config-class owned type with explicitWithOwner().HasForeignKey/HasKey.Verification
dtk dotnet format --verify-no-changesclean.Closes #162
Closes #163
Closes #164
Closes #165
🤖 Generated with Claude Code