Validate cross-generator consistency for oneof discriminator and flatten annotations across all 4 generators.

Purpose: Ensure the same proto definitions produce semantically identical JSON across go-http, go-client, ts-client, and OpenAPI. The server must serialize what clients expect, and OpenAPI must document what both produce. This is the final validation for Phase 7.

Output: Cross-generator consistency tests that verify generated code matches between generators, TypeScript types match Go serialization behavior, and OpenAPI schemas accurately reflect the wire format for all oneof and flatten combinations.

<execution_context> @/Users/sebastienmelki/.claude/get-shit-done/workflows/execute-plan.md @/Users/sebastienmelki/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/07-json-structural-transforms/07-RESEARCH.md @.planning/phases/07-json-structural-transforms/07-02-SUMMARY.md @.planning/phases/07-json-structural-transforms/07-03-SUMMARY.md

Existing consistency test patterns from Phase 4-6 to follow exactly

@internal/httpgen/encoding_consistency_test.go @internal/httpgen/nullable_consistency_test.go @internal/httpgen/timestamp_format_consistency_test.go @internal/httpgen/bytes_encoding_consistency_test.go

Task 1: Create oneof_discriminator cross-generator consistency tests internal/httpgen/oneof_discriminator_consistency_test.go Create internal/httpgen/oneof_discriminator_consistency_test.go following the established pattern from encoding_consistency_test.go and timestamp_format_consistency_test.go.

The test file should contain these test functions (following the split-test pattern from D-04-05-01):

  1. TestGoGeneratorsProduceIdenticalOneofDiscriminator -- Compare golden files:

    • Read httpgen golden file for oneof_discriminator
    • Read clientgen golden file for oneof_discriminator
    • Normalize generator comments using normalizeGeneratorComment
    • Assert byte-level equality after normalization
  2. TestOneofDiscriminatorTypeScriptTypes -- Verify TS type mapping:

    • Read tsclientgen golden file for oneof_discriminator
    • Verify FlattenedEvent generates a discriminated union type with "type" discriminator
    • Verify discriminator values match: "text" and "img" (custom oneof_value)
    • Verify NestedEvent generates a union with "kind" discriminator and nested variants
    • Verify PlainEvent generates standard interface (no discriminated union)
  3. TestOneofDiscriminatorOpenAPISchemas -- Verify OpenAPI schemas:

    • Read openapiv3 golden file for oneof_discriminator
    • Verify FlattenedEvent schema uses oneOf with discriminator keyword
    • Verify discriminator propertyName matches annotation
    • Verify mapping includes custom oneof_value ("img" maps to ImageContent)
    • Verify NestedEvent has discriminator with nested variant references
    • Verify PlainEvent has no discriminator (standard object schema)
  4. TestOneofDiscriminatorCrossGeneratorAgreement -- Cross-check all generators agree:

    • Table-driven test with test cases for each message:
      testCases := []struct {
          message            string
          hasDiscriminator   bool
          discriminatorField string  // "type", "kind", or ""
          flatten            bool
          variants           []struct {
              name  string  // field name
              value string  // discriminator value
          }
      }{
          {
              "FlattenedEvent", true, "type", true,
              []struct{name, value string}{{"text", "text"}, {"image", "img"}},
          },
          {
              "NestedEvent", true, "kind", false,
              []struct{name, value string}{{"text", "text"}, {"image", "image"}, {"video", "vid"}},
          },
          {"PlainEvent", false, "", false, nil},
      }
    • For each test case, verify:
      • Go golden file contains MarshalJSON for discriminated messages, not for PlainEvent
      • TypeScript golden has discriminated union types for discriminated messages
      • OpenAPI golden has discriminator keyword for discriminated messages
      • All agree on discriminator field name and variant values

Implementation approach: Read golden files from disk (same as existing consistency tests). Parse Go golden for type-switch patterns and discriminator strings, TS golden for union types, OpenAPI golden for discriminator keyword and mapping. Use string matching and regex patterns.

go test -v -run TestOneofDiscriminator ./internal/httpgen/...
make lint-fix
- TestGoGeneratorsProduceIdenticalOneofDiscriminator passes (byte-level code match) - TestOneofDiscriminatorTypeScriptTypes passes (correct discriminated union types) - TestOneofDiscriminatorOpenAPISchemas passes (discriminator keyword with mapping) - TestOneofDiscriminatorCrossGeneratorAgreement passes (all 4 generators agree)
Task 2: Create flatten cross-generator consistency tests internal/httpgen/flatten_consistency_test.go Create internal/httpgen/flatten_consistency_test.go following the same pattern:
  1. TestGoGeneratorsProduceIdenticalFlatten -- Compare golden files:

    • Read httpgen golden file for flatten
    • Read clientgen golden file for flatten
    • Normalize generator comments
    • Assert byte-level equality after normalization
  2. TestFlattenTypeScriptTypes -- Verify TS type mapping:

    • Read tsclientgen golden file for flatten
    • Verify SimpleFlatten interface contains street, city, zip at top level (no nested address)
    • Verify DualFlatten interface contains billing_street, billing_city, shipping_street, shipping_city
    • Verify MixedFlatten has flattened fields AND a nested contact property
    • Verify PlainNested has standard nested address property
  3. TestFlattenOpenAPISchemas -- Verify OpenAPI schemas:

    • Read openapiv3 golden file for flatten
    • Verify SimpleFlatten schema has street, city, zip as direct properties
    • Verify DualFlatten schema has billing_street, billing_city, etc. as direct properties
    • Verify MixedFlatten has both flattened and nested properties
    • Verify PlainNested has $ref to Address schema
  4. TestFlattenCrossGeneratorAgreement -- Cross-check all generators agree:

    • Table-driven test with test cases:
      testCases := []struct {
          message          string
          hasFlatten       bool
          flattenedFields  []string  // Expected top-level JSON field names from flattening
          nestedFields     []string  // Expected nested (non-flattened) fields
      }{
          {
              "SimpleFlatten", true,
              []string{"street", "city", "zip"},
              nil,
          },
          {
              "DualFlatten", true,
              []string{"billing_street", "billing_city", "billing_zip",
                        "shipping_street", "shipping_city", "shipping_zip"},
              nil,
          },
          {
              "MixedFlatten", true,
              []string{"street", "city", "zip"},
              []string{"contact"},
          },
          {"PlainNested", false, nil, []string{"address"}},
      }
    • For each test case, verify:
      • Go golden contains MarshalJSON for flatten messages, not for PlainNested
      • Go golden references the expected flattened field names (with prefixes)
      • TypeScript golden has flattened fields at interface level
      • OpenAPI golden has flattened fields as direct properties

Implementation approach: Same as oneof_discriminator -- read golden files, parse patterns, compare.

go test -v -run TestFlatten ./internal/httpgen/...
make lint-fix
- TestGoGeneratorsProduceIdenticalFlatten passes (byte-level code match) - TestFlattenTypeScriptTypes passes (correct inlined fields with prefixes) - TestFlattenOpenAPISchemas passes (correct property names and types) - TestFlattenCrossGeneratorAgreement passes (all 4 generators agree)
Task 3: Run full test suite and verify all golden files (verification only - no new files) Run the complete test suite to verify:
  1. All existing tests still pass (zero regression)
  2. All new consistency tests pass
  3. All golden files are up to date
  4. Lint is clean
# Full test suite
go test ./...
 
# Specific generator tests
go test -v ./internal/httpgen/... ./internal/clientgen/... ./internal/tsclientgen/... ./internal/openapiv3/...
 
# Golden file tests
go test -v -run TestExhaustiveGoldenFiles ./internal/...
 
# All consistency tests (Phase 4, 5, 6, and 7)
go test -v -run "Consistency|Identical" ./internal/httpgen/...
 
# Lint check
make lint-fix

Verify that:

  • oneof_discriminator and flatten golden files exist for all 4 generators
  • Generated code matches expected golden output
  • Cross-generator consistency verified for all structural transform combinations
  • No regressions in any Phase 4, 5, or 6 tests
  • Phase 7 success criteria 6 is met: cross-generator consistency test confirms agreement

If any test fails, debug and fix. The consistency tests are the final gate ensuring Phase 7 success.

go test ./...
make lint-fix

All tests pass, lint clean, no regressions.

  • All 4 generators produce consistent output for oneof discriminator annotations
  • All 4 generators produce consistent output for flatten annotations
  • go-http and go-client produce byte-identical encoding code for both features
  • TypeScript types match Go serialization for all structural transform combinations
  • OpenAPI schemas match Go serialization for all structural transform combinations
  • All Phase 4, 5, and 6 consistency tests still pass (zero regression)
  • Full test suite passes
  • Lint clean
1. Consistency tests pass: `go test -v -run "OneofDiscriminator|Flatten" ./internal/httpgen/...` 2. Full test suite passes: `go test ./...` 3. Lint clean: `make lint-fix` 4. Golden files up to date: `go test -run TestExhaustiveGoldenFiles ./internal/...` 5. Cross-generator verification for Phase 7: - go-http JSON == go-client JSON for all oneof discriminator variants (byte-level after normalization) - go-http JSON == go-client JSON for all flatten variants (byte-level after normalization) - TypeScript discriminated unions match Go oneof serialization - TypeScript inlined fields match Go flatten serialization - OpenAPI discriminator keyword matches Go oneof serialization - OpenAPI property names match Go flatten serialization

<success_criteria>

  • go-http and go-client produce byte-identical code for oneof_discriminator and flatten
  • All oneof discriminator combinations (flattened/non-flattened, custom values, unset) produce correct types in all 4 generators
  • All flatten combinations (simple, prefix, dual, mixed) produce correct field names in all 4 generators
  • OpenAPI discriminator and flatten schemas accurately reflect the wire format
  • Zero regressions in existing tests (Phase 1-6)
  • Phase 7 success criterion 6 met: cross-generator consistency test confirms agreement </success_criteria>
After completion, create `.planning/phases/07-json-structural-transforms/07-04-SUMMARY.md`