Skip to content

fix: resolve all open issues — CLI strict parsing, EF record gaps, EffectiveTable dedup, PR #161 debt roundup (#162–#165) - #167

Merged
HandyS11 merged 5 commits into
developfrom
fix/issue-roundup-162-165
Jul 16, 2026
Merged

fix: resolve all open issues — CLI strict parsing, EF record gaps, EffectiveTable dedup, PR #161 debt roundup (#162–#165)#167
HandyS11 merged 5 commits into
developfrom
fix/issue-roundup-162-165

Conversation

@HandyS11

Copy link
Copy Markdown
Owner

Addresses all four open issues in one branch, one commit per issue plus a review-fix commit.

#162 — CLI: strict parsing

config.Settings.StrictParsing = true in Program.cs (mirrored in the CLI test-harness app). An unknown long option now prints Error: 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 real Program.Main configuration; the test file documents a Spectre.Console.Cli quirk (the default error renderer freezes AnsiConsole.Console in a process-lifetime Lazy) that constrains how many tests may exercise the default render path.

#163 — EF: record-type gaps

  • Positional-record owner: the owned-nav pre-pass now also reads RecordDeclarationSyntax.ParameterList, so record Product(int Id, Money Price) discovers Money.cs cross-file instead of degrading to an empty box.
  • EntityAnalyzer.ExtractPrimaryKeysFromSyntax and EntityConfigurationWalker.FindConfigClassesInRoot widened from ClassDeclarationSyntax to TypeDeclarationSyntax.
  • Review of the branch surfaced and fixed three more gaps in the same family: cross-file config discovery and base-type extraction in EntityFileDiscovery were still class-only, and the [Key] syntax fallback missed [property: Key] on positional-record parameters. The widening also newly admitted interfaces with default Configure bodies (which EF never applies) — now excluded.
  • The snapshot-path cross-file case remains unpinned (no fixture reaches it today), as the issue allowed.

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.EffectiveTable beside EffectiveKey; FluentOwnedTypeWalker and MermaidErdRenderer both 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: FindRelationshipRoots fences only UsingEntity, so owned-builder HasOne/HasMany chains 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.
  • Lambda branch kept, not removed: contrary to the issue, OwnershipBuilder<TEntity,TDependentEntity> has had an Expression HasForeignKey overload since EF Core 3.0 (docs), so ForeignKeyPropertyNames' lambda branch is live; it is now documented and pinned by a regression test.
  • Nested-builder boundary rule consolidated into one FluentSyntax.IsNestedBuilderFence predicate.
  • Doubled-brace because string reworded (no more literal {{OwnerTable}}_{{Nav}} in failure messages).
  • Test-coverage boundaries closed: box-existence assertion before splitting in Classic_OwnerDoesNotGainNavigationColumn; new inlined-undrawn-sibling label test; new OwnedFkConfigContext fixture exercising StripShadowKeys against a config-class owned type with explicit WithOwner().HasForeignKey/HasKey.
  • Mermaid-validation provenance stays recorded in the issue per its own text; no docs note added.

Verification

Closes #162
Closes #163
Closes #164
Closes #165

🤖 Generated with Claude Code

HandyS11 and others added 5 commits July 16, 2026 21:17
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>
Copilot AI review requested due to automatic review settings July 16, 2026 19:48

Copilot AI 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.

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.EffectiveTable and 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.

@HandyS11
HandyS11 merged commit 71e9333 into develop Jul 16, 2026
7 checks passed
@HandyS11
HandyS11 deleted the fix/issue-roundup-162-165 branch July 16, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment