Purpose: The Go client is the primary reference implementation. It must serialize requests exactly as the server expects and deserialize responses exactly as the server produces. Any inconsistency here will propagate to all future language clients that model themselves after this one.
Output: A Go client generator that is verified consistent with the server, with expanded test coverage including unwrap variants.
<execution_context> @/Users/sebastienmelki/.claude/get-shit-done/workflows/execute-plan.md @/Users/sebastienmelki/.claude/get-shit-done/templates/summary.md </execution_context>
- unwrap.proto -- All unwrap variants (map-value, root repeated, root map, combined). The httpgen has this and the tsclientgen has complex_features.proto which covers unwrap too.
- complex_features.proto -- Enums, optional fields, nested messages, all unwrap variants. The tsclientgen has this.
Add symlinks in internal/clientgen/testdata/proto/:
unwrap.proto -> ../../../httpgen/testdata/proto/unwrap.protocomplex_features.proto -> ../../../tsclientgen/testdata/proto/complex_features.proto
NOTE: The complex_features.proto file has go_package set to tsclientgen/testdata/generated. This is fine for golden file testing since the golden test just compares text output, not compiled Go. But verify the golden test handles different go_package values correctly. If not, create a clientgen-specific version with the correct go_package instead of symlinking.
Update internal/clientgen/golden_test.go to include the new protos in its test list. Read the existing test to understand the pattern -- it likely iterates over proto files in the testdata/proto directory. If it auto-discovers files, just adding the symlinks is sufficient. If it has a hardcoded list, add the new entries.
Generate initial golden files: UPDATE_GOLDEN=1 go test ./internal/clientgen/ -run TestExhaustiveGoldenFiles -count=1
Verify: go test ./internal/clientgen/ -count=1
go test ./internal/clientgen/ -count=1 -- all tests pass. Verify golden files exist: ls internal/clientgen/testdata/golden/unwrap_client.pb.go. Verify the generated unwrap client code handles all unwrap variants (root map, root repeated, map-value, combined).
1. Query parameter encoding consistency
Compare generateQueryParams in clientgen/generator.go with bindQueryParams in httpgen/generator.go:
- For each scalar type (string, int32, int64, uint64, bool, float32, float64), verify that
fmt.Sprint(req.Field)produces a string that the server'sconvertStringToFieldValuecan parse back correctly. - Pay special attention to float/double:
fmt.Sprint(1.5)produces1.5whichstrconv.ParseFloataccepts. Verify this. - For bool: client sends
true/falseviafmt.Sprint, server parses withstrconv.ParseBool. These are compatible. - For int64/uint64: client sends numeric strings, server parses with
strconv.ParseInt/strconv.ParseUint. Compatible. - Verify zero-value omission: client checks
req.Field != 0(or!= ""for strings,!= falsefor bool). This matches protojson's "omit default values" behavior.
2. Content-Type handling
- Verify the client sets
Content-Typeheader on all requests. - Verify the client handles response Content-Type correctly (uses request Content-Type for response parsing, matching server behavior from 03-02 fixes).
- Check that both JSON and protobuf binary paths work.
3. Error handling consistency
- Read
handleErrorResponsein clientgen/generator.go. Verify it correctly deserializesValidationError(400) andError(other status codes) from the response. - Verify the error body structure matches what the server produces:
ValidationErrorwithviolationsarray,Errorwithmessagefield. - Verify status code mapping: 400 = ValidationError, other 4xx/5xx = Error.
4. Path parameter URL encoding
- Verify the client uses
url.PathEscape(fmt.Sprint(req.Field))for all path parameters. - Verify this matches the server's expectation (server receives already-decoded path params from the HTTP router).
5. Header handling
- Verify service-level headers are sent on every request.
- Verify method-level headers are sent on specific requests.
- Verify header names match exactly (case-sensitive comparison with what server validates).
- Check the functional options API:
WithXxxHeader(name, value),WithXxxCallHeader(name, value), etc. Verify naming is consistent and complete.
6. Response deserialization
- For standard responses: verify
unmarshalResponseusesprotojson.Unmarshalfor JSON andproto.Unmarshalfor binary. - For unwrap responses: verify the client correctly handles custom JSON unmarshaling via the
json.Unmarshalerinterface.
For each inconsistency found, fix it in the generator code. After all fixes, update golden files.
IMPORTANT: If you find that the client is CORRECT and the server is WRONG (per protojson spec), note this but do NOT fix the server here -- server fixes are in plan 03-02. If the server was already fixed in 03-02, verify the client matches the fixed server behavior.
After all fixes, run make lint-fix to clean up formatting.
UPDATE_GOLDEN=1 go test ./internal/clientgen/ -count=1 to update golden files after any fixes. Then go test ./internal/clientgen/ -count=1 to confirm all pass. Run make build to verify full project builds. Run make lint-fix for clean code.
- Query param encoding for all scalar types matches server parsing
- Content-Type handling is bidirectionally consistent
- Error deserialization matches server error serialization
- Path params are correctly URL-encoded
- Headers are sent consistently with server validation expectations
- All inconsistencies found are fixed and captured in golden files
<success_criteria>
- Go client has complete test coverage including all unwrap variants, enums, optional fields, nested messages
- Every query param scalar type roundtrips correctly (client encodes, server parses)
- Error handling matches server error format exactly
- Header handling is consistent between client and server
- All clientgen golden file tests pass </success_criteria>
