Skip to content

[GEN-E3] ref chains, pure policies and schema-name resolvers get one owner each; an error-union refusal no longer cascades (PR 3/3, #436) - #456

Merged
Blind-Striker merged 1 commit into
masterfrom
refactor/gen-e3-statics
Oct 17, 2026
Merged

Blind-Striker merged 1 commit into
masterfrom
refactor/gen-e3-statics

Conversation

@Blind-Striker

@Blind-Striker Blind-Striker commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Binding's ref-chain walks, pure policies and schema-name resolvers each get one owner. A refused error union no longer cascades into misleading errors (#436). Generated output is byte-identical.

Closes: P44, P38, U7-30, U10-09, P47, P48, P50, #436. This is GEN-E3 PR 3 of 3, following the approved plan (faz4/GEN-E3/plan.md; amendments A.1–A.4, 14 and 15 bind this PR). PR 1 is #425 and PR 2 is #437. It was developed in parallel with GEN-E6 PR 2b (#455), with disjoint write sets. It was rebased onto 7bb2faef, past RT-E2 C, #451 and the preview.12 cut #454. This PR touches no CHANGELOG, and the full local check was rerun on that base.

What changes

  • RefChain (P44) replaces the four ref-chain walks with one walk. Its result names the last successful key, including what QueryEnumShapePolicy needs. The document-based OperationFacetContext.Resolve is gone, and its two UpstreamClientQueryTypeTests callers use RefChain (amendment 14).
  • The pure policies become static (P38, U7-30, U10-09):
    • comparer-only Binding types go from 10 to 0, and Binding _comparer fields from 11 to 0 (S4);
    • SchemaPlanBinder keeps its instance shape;
    • SchemaAliasApplier and PathTemplateValidator become static, along with the four Ingestion/Projection pure types (GraphKeyBuilder, ErrorStyleClassifier, EnumSchemaProjector, LiteralClassifier);
    • their constructor threading, DI registrations (ToolApp) and BindingTestHost's hand-wiring go too;
    • SpecIngestion.cs changes only in its GraphKeyBuilder construction and call lines (S1).
  • SchemaNameResolver's four facet-root resolvers become their own static collaborators: RequestBodyRootNameResolver, QueryEnumRootNameResolver, EnvelopePayloadRootNameResolver and EffectStreamRootNameResolver (decision 9, S2). SchemaNameResolver goes from 410 to 205 lines.
  • A duplicate-tag error union refusal cascades into misleading 'absent union' membership errors #436: a refused error union drops its membership claims before the graph is published, the same mechanism [GEN-E3] union binding returns its decisions; nested-union and object-or-array defects refuse or bind correctly (PR 1/3) #425 used for P36. A duplicate-tag refusal now reports one error instead of three. Amendment 15 makes Bind_Should_Refuse_A_Tag_Owned_By_Two_Closure_Schemas a deliberate, red-first change: three errors → the duplicate-tag error alone, as the full ordered list. Its response-collision row keeps its Naming error after it.
  • Hygiene:
    • P47: the "in M1" milestone text is gone from the WebSocket and PUT refusals.
    • P48: the session-event wrapper is removed (plan §8.1). Removing it pushed SpecBinder.Bind past MA0051's 80 lines, so its orchestration got one private-method extraction inside SpecBinder.cs, keeping the pass order, arguments and results. That is mechanical fallout of a planned move, approved by the lead; there is no new type and no suppression.
    • P50: comment fixes.
  • .editorconfig: the four CA1822/S2325 exemption sections go (4 → 0). The SuppressionLedger snapshot loses 8 deletions plus their aggregate count (editorconfig: 158 → 150), edited by hand with no auto-accept (S3, and the lead's OK on the count).

Proof (spec)

Metric Result
Ref-chain walks 4 → 1
Comparer-only Binding types / _comparer fields 10 → 0 / 11 → 0
CA1822/S2325 exemptions 4 → 0
SchemaPlanBinder 154 lines (≤ 250)
OperationPlanBinder 252 (≤ 400)
SchemaNameResolver 205 (≤ 400)
New Binding files over 400 none
Methods with ≥ 6 parameters (decision 8, methods only) 0
generate --verify byte-identical

Tests

  • Red first:
  • Mutation: three compiling mutants, all killed, each file restored with cp/cmp:
    • RefChain stops one hop early;
    • RefChain ignores the visited set;
    • a refused error union keeps its claims.
  • Existing-test edits, each under the plan or an amendment:
    • CSharpNamePolicyTests' summary (comment only);
    • the two ToolAppTests marker strings and two PendingOperationBindabilityProbeTests messages, which lose only " in M1";
    • A duplicate-tag error union refusal cascades into misleading 'absent union' membership errors #436's two rows (amendment 15);
    • the S3 snapshot lines;
    • QueryRequestFacetBinderTests' two enum-refusal tests, folded into one data-driven test with every assertion unchanged;
    • otherwise only compile-forced arrange lines (new X().Bind → X.Bind, and constructor arguments removed), in BindingTestHost, SpecBinder/OperationPlanBinder/StabilizeDuplicatePolicy/ModelReachCollector/UnionMembershipValidator/UnionMemberHoistBinder tests, UnionHoistPlanData, EmitterPlanFixture, GraphKeyBuilder/LiteralClassifier/SchemaProjector tests and the two projection test hosts.
  • E0 guards: full ordered error lists, and both TST-E1 site lists stay empty.

Clone baseline, every entry accounted for

Tools (1.98%): −5 stale, +2.

  • The 5 removals: 3 are pairs freed by this PR's deduplications, and 2 are pairs earlier PR 3 work removed.
  • Duplicates removed: the static conversions made five latent duplicates textually identical (they had differed only by instance access, e.g. _keys.Append against GraphKeyBuilder.Append). Each now has one owner, a same-file static helper:
    • LiteralClassifier's two marker prologues;
    • the media projection shared by RequestBody/ResponseProjector;
    • the array-node projection;
    • and two more.
  • The 2 additions: ObjectSchemaProjector.cs:10-23 ↔ PrefixItemsAdapter.cs:12-25 and ObjectSchemaProjector.cs:12-23 ↔ UnionNormalizer.cs:23-34.

Tests (5.38%): −2, +1.

  • b4bc3f07 is freed by the QueryRequestFacetBinderTests fold.
  • The fixture pair EmitterPlanFixture.cs:1041-1067 ↔ UnionHoistPlanData.cs:149-175 is a fingerprint swap, 94b8d278 → e59cfed5. PR 3's compile-forced new X().Bind → X.Bind edits shifted it. With master's two fixture files restored at this head, the gate shows 0 new clones.

Budget (against the merge-base)

  • Hand-written tools: +605 / −711, −106 net, against a target of ≤ −40. The run reached −123, and the review fix-up spent +17, mostly restored rationale comments.
  • Tests: 169 added (−94), against a cap of 200.

Local check and review

  • Local check at this head, on dev-01, after a root Release build: slopwatch 0 issues; the Release build with 0 warnings; both format passes; verify-clones passing; --help; generate --verify current; the whole Tools.Tests project (guards included) at 2070/0. This is the rerun after the review fix-up and the rebase, on a quiet host. The first check, on the pre-review head, ran beside a dispatch. CI is the full gate.
    • Bun 1.4.3, off the 1.4.2 pin.
  • verify.log: each run's harness verify covered the guards, 28 touched classes, the whole project, verify-clones and generate --verify (manual §5.3).
  • Process: the Sol guide pilot template (task 2).
    • The stops: run 1 stopped on MA0051, the in-file extraction above (over-triggered). Run 2 stopped on the S3 aggregate count, the lead's wording (justified). The clone fix-up's run 1 stopped on a restore-authority ambiguity in its prompt (over-triggered).
    • Review: the coordinator's Claude review and a fresh cross-family review, comparing git show 695c5557:<file> with the new code behaviour by behaviour. Neither found a behaviour change for any document they constructed: RefChain's four walks, the static conversions, resolver precedence and order, the SpecBinder pass order, and A duplicate-tag error union refusal cascades into misleading 'absent union' membership errors #436's scope. Fixed here:
      • the nullable characterization, which couldn't fail for the regression it names. It gains a direct ResolveModelKey(Ref(State), graph) row and a full-pipeline refusal, both red against an "unwrap Nullable inside the walk" mutant;
      • two helpers moved to their owners: the media-entry projection to MediaTypeProjector, and ProjectArrayItems to an instance method taking items explicitly, with no Debug.Assert;
      • the rationale comments the moves had dropped, restored;
      • one SchemaBindingContext factory, replacing two tests' hand copies.
    • A review ask that the analyzer overruled: reverting ReferencesMarkedUnion to IsMarkedUnionReference triggers S3218, because the static method shadows ProjectedBranch.IsMarkedUnionReference. So that rename stays: it was forced, not unrequested.

Provenance: Generator lane / opencode dispatch harness (openai/gpt-6.1-sol-1m), coordinated, reviewed and finished in Claude Code.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…ers each get one owner, and an error-union refusal no longer cascades

RefChain replaces the four ref-chain walks, with one walk and a result naming the last key. The
pure Binding and Ingestion/Projection policies become static: comparer-only Binding types go
from 10 to 0, the `_comparer` fields from 11 to 0, and their constructor threading and DI
registrations go with them. SchemaNameResolver's four facet-root resolvers become their own
static collaborators (SchemaNameResolver 410 -> 205 lines). SchemaPlanBinder is 154 lines.

The four CA1822/S2325 exemptions in .editorconfig go, with their eight suppression-ledger rows.
Latent duplicates that the static conversions made textually identical each get one owner.

#436: a refused error union drops its membership claims before the graph is published, so a
duplicate-tag refusal reports one error instead of two misleading absent-union errors.

Generated output is byte-identical.

Closes #436
@Blind-Striker Blind-Striker added this to the 1.0 milestone Oct 17, 2026
@Blind-Striker
Blind-Striker added this pull request to the merge queue Oct 17, 2026
Merged via the queue into master with commit f101089 Oct 17, 2026
12 checks passed
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