Create comprehensive cross-generator consistency tests that verify int64_encoding and enum_encoding produce identical JSON semantics across go-http, go-client, ts-client, and openapiv3.

Purpose: Ensure the "two capital sins" (breaking backward compat, cross-generator inconsistency) have not been committed. Programmatically verify Phase 4 success criteria.

Output: Expanded exhaustive test proto with all encoding combinations. Cross-generator consistency test that fails if any generator diverges.

<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/phases/04-json-primitive-encoding/04-CONTEXT.md @.planning/phases/04-json-primitive-encoding/04-02-SUMMARY.md @.planning/phases/04-json-primitive-encoding/04-03-SUMMARY.md @.planning/phases/04-json-primitive-encoding/04-04-SUMMARY.md

Test infrastructure

@internal/httpgen/exhaustive_golden_test.go @internal/tsclientgen/golden_test.go @internal/openapiv3/exhaustive_golden_test.go @internal/httpgen/testdata/exhaustive.proto

Task 1: Expand exhaustive.proto with encoding test cases internal/httpgen/testdata/exhaustive.proto Add comprehensive encoding test cases to internal/httpgen/testdata/exhaustive.proto:
  1. Add int64 encoding test message:
// Int64 encoding test cases
message Int64EncodingExhaustive {
  // Default (STRING per proto3 JSON spec)
  int64 default_int64 = 1;
  sint64 default_sint64 = 2;
  sfixed64 default_sfixed64 = 3;
  uint64 default_uint64 = 4;
  fixed64 default_fixed64 = 5;
 
  // Explicit STRING encoding
  int64 string_int64 = 6 [(sebuf.http.int64_encoding) = INT64_ENCODING_STRING];
  uint64 string_uint64 = 7 [(sebuf.http.int64_encoding) = INT64_ENCODING_STRING];
 
  // NUMBER encoding (precision warning)
  int64 number_int64 = 8 [(sebuf.http.int64_encoding) = INT64_ENCODING_NUMBER];
  uint64 number_uint64 = 9 [(sebuf.http.int64_encoding) = INT64_ENCODING_NUMBER];
 
  // Repeated with encoding
  repeated int64 repeated_string_int64 = 10;
  repeated int64 repeated_number_int64 = 11 [(sebuf.http.int64_encoding) = INT64_ENCODING_NUMBER];
 
  // Map with int64 values
  map<string, int64> map_string_int64 = 12;
  // Note: Map values with NUMBER encoding - if supported
}
  1. Add enum encoding test cases:
// Enum with custom JSON values
enum ExhaustiveStatus {
  EXHAUSTIVE_STATUS_UNSPECIFIED = 0 [(sebuf.http.enum_value) = "unknown"];
  EXHAUSTIVE_STATUS_ACTIVE = 1 [(sebuf.http.enum_value) = "active"];
  EXHAUSTIVE_STATUS_PENDING = 2 [(sebuf.http.enum_value) = "pending"];
  EXHAUSTIVE_STATUS_INACTIVE = 3;  // No custom value - uses proto name
}
 
// Enum without custom values
enum ExhaustivePriority {
  EXHAUSTIVE_PRIORITY_LOW = 0;
  EXHAUSTIVE_PRIORITY_MEDIUM = 1;
  EXHAUSTIVE_PRIORITY_HIGH = 2;
}
 
message EnumEncodingExhaustive {
  // Custom values (STRING encoding by default)
  ExhaustiveStatus status_default = 1;
  ExhaustiveStatus status_string = 2 [(sebuf.http.enum_encoding) = ENUM_ENCODING_STRING];
 
  // NUMBER encoding (uses numeric values)
  ExhaustivePriority priority_number = 3 [(sebuf.http.enum_encoding) = ENUM_ENCODING_NUMBER];
  ExhaustivePriority priority_string = 4 [(sebuf.http.enum_encoding) = ENUM_ENCODING_STRING];
 
  // Default behavior (proto names as strings)
  ExhaustivePriority priority_default = 5;
}
  1. Add service endpoint to test encoding:
service EncodingService {
  option (sebuf.http.service_config) = {
    base_path: "/api/v1"
  };
 
  rpc TestInt64Encoding(Int64EncodingExhaustive) returns (Int64EncodingExhaustive) {
    option (sebuf.http.config) = {
      path: "/encoding/int64"
      method: HTTP_METHOD_POST
    };
  }
 
  rpc TestEnumEncoding(EnumEncodingExhaustive) returns (EnumEncodingExhaustive) {
    option (sebuf.http.config) = {
      path: "/encoding/enum"
      method: HTTP_METHOD_POST
    };
  }
}

Ensure exhaustive.proto still imports the annotations:

import "sebuf/http/annotations.proto";
```bash # Verify proto compiles cd internal/httpgen/testdata && buf build # or protoc --proto_path=. --proto_path=../../../proto exhaustive.proto --go_out=. ``` - exhaustive.proto contains Int64EncodingExhaustive message with all int64 variants - exhaustive.proto contains ExhaustiveStatus enum with custom values - exhaustive.proto contains EnumEncodingExhaustive message with all enum variants - exhaustive.proto contains EncodingService with test endpoints - Proto compiles without errors
Task 2: Update golden files and run consistency verification internal/httpgen/exhaustive_golden_test.go internal/httpgen/testdata/golden/exhaustive_http_binding.pb.go internal/tsclientgen/testdata/golden/exhaustive_client.ts internal/openapiv3/testdata/golden/EncodingService.openapi.yaml 1. Ensure all generators use the updated exhaustive.proto (via symlinks or copying): ```bash # ts-client should already symlink to httpgen testdata ls -la internal/tsclientgen/testdata/exhaustive.proto

openapiv3 should symlink as well#

ls -la internal/openapiv3/testdata/exhaustive.proto


2. Run golden file generation for all generators:
```bash
UPDATE_GOLDEN=1 go test -run TestExhaustiveGoldenFiles ./internal/httpgen/...
UPDATE_GOLDEN=1 go test -run TestGoldenFiles ./internal/tsclientgen/...
UPDATE_GOLDEN=1 go test -run TestExhaustiveGoldenFiles ./internal/openapiv3/...
UPDATE_GOLDEN=1 go test -run TestGoldenFiles ./internal/clientgen/...
  1. Verify cross-generator consistency by inspection:

Go golden file should contain:

  • MarshalJSON for Int64EncodingExhaustive (only if NUMBER fields present)
  • MarshalJSON for ExhaustiveStatus enum (custom values)
  • Lookup maps for ExhaustiveStatus (statusToJSON, statusFromJSON)

TypeScript golden should contain:

export type ExhaustiveStatus = "unknown" | "active" | "pending" | "EXHAUSTIVE_STATUS_INACTIVE";
export interface Int64EncodingExhaustive {
  defaultInt64: string;       // STRING (default)
  numberInt64: number;        // NUMBER
  repeatedNumberInt64: number[];
}
export interface EnumEncodingExhaustive {
  priorityNumber: number;     // NUMBER encoding
  priorityString: ExhaustivePriority;
}

OpenAPI golden should contain:

ExhaustiveStatus:
  type: string
  enum: ["unknown", "active", "pending", "EXHAUSTIVE_STATUS_INACTIVE"]
Int64EncodingExhaustive:
  properties:
    defaultInt64:
      type: string
      format: int64
    numberInt64:
      type: integer
      format: int64
      description: "Warning: Values > 2^53 may lose precision in JavaScript"
```bash # Run all golden tests without UPDATE_GOLDEN - should pass go test -v -run Golden ./internal/httpgen/... go test -v -run Golden ./internal/tsclientgen/... go test -v -run Golden ./internal/openapiv3/... go test -v -run Golden ./internal/clientgen/... ``` All golden tests pass. - All golden files updated with encoding test cases - Go http and client golden contain matching marshal code - TypeScript golden contains correct number/string types - OpenAPI golden contains correct integer/string schemas - All golden tests pass
Task 3: Verify Phase 4 ROADMAP success criteria internal/httpgen/testdata/golden/exhaustive_http_binding.pb.go internal/tsclientgen/testdata/golden/exhaustive_client.ts internal/openapiv3/testdata/golden/EncodingService.openapi.yaml Manually verify each Phase 4 success criterion from ROADMAP.md:
  1. int64_encoding = STRING serializes as JSON strings in all generators

    • Go: Check golden for strconv.FormatInt or protojson default
    • TypeScript: Check for string type in interface
    • OpenAPI: Check for type: string in schema
  2. int64_encoding = NUMBER serializes as JSON numbers with precision warning

    • Go: Check golden for MarshalJSON with direct int64 assignment
    • Go: Check generation logs for warning message
    • TypeScript: Check for number type in interface
    • OpenAPI: Check for type: integer with warning description
  3. enum_encoding = STRING serializes enum values as proto names

    • Go: Check protojson default behavior preserved
    • TypeScript: Check union type uses proto names
    • OpenAPI: Check enum array uses proto names
  4. enum_value annotations map to custom JSON strings

    • Go: Check lookup maps with custom values
    • TypeScript: Check union type uses custom values
    • OpenAPI: Check enum array uses custom values
  5. OpenAPI schemas accurately reflect configured encoding

    • NUMBER int64: type=integer, format=int64
    • STRING int64: type=string, format=int64
    • NUMBER enum: type=integer with numeric enum
    • Custom enum: type=string with custom string values
  6. Cross-generator consistency

    • All generators produce equivalent JSON for same input
    • TypeScript types match what Go produces
    • OpenAPI documents what Go produces

Run full test suite to confirm no regressions:

./scripts/run_tests.sh
```bash # Full test suite with coverage ./scripts/run_tests.sh

Lint check#

make lint-fix

Build all binaries#

make build

All tests pass, 85% coverage maintained, lint clean.
  </verify>
  <done>
- All 6 Phase 4 success criteria verified
- Full test suite passes
- Coverage threshold maintained (85%)
- Lint clean
- All binaries build successfully
- Ready to close Phase 4
  </done>
</task>

</tasks>

<verification>
1. All golden tests pass: `go test -v -run Golden ./...`
2. Full test suite passes: `./scripts/run_tests.sh`
3. Coverage maintained: >= 85%
4. Lint clean: `make lint-fix`
5. All binaries build: `make build`
6. Phase 4 success criteria verified (6/6)
</verification>

<success_criteria>
Phase 4 complete when ALL of these are true:
1. int64_encoding = STRING produces JSON strings in all 4 generators
2. int64_encoding = NUMBER produces JSON numbers in all 4 generators (with warning)
3. enum_encoding = STRING produces proto name strings in all 4 generators
4. enum_value annotations produce custom JSON strings in all 4 generators
5. OpenAPI schemas accurately reflect configured encoding
6. Cross-generator consistency verified for every encoding combination
- Zero regressions in existing tests
- Backward compatible (protos without annotations unchanged)
</success_criteria>

<output>
After completion, create `.planning/phases/04-json-primitive-encoding/04-05-SUMMARY.md`
</output>