Project State

Project Reference#

See: .planning/PROJECT.md (updated 2026-02-05)

Core value: Proto definitions are the single source of truth -- every generator must produce consistent, correct output that interoperates seamlessly. Current focus: Phase 7 complete -- JSON Structural Transforms (oneof, flatten). Ready for Phase 8+.

Current Position#

Phase: 7 of 11 (JSON - Structural Transforms) Plan: 4 of 4 in current phase Status: Phase complete Last activity: 2026-04-16 - Completed quick task 260416-kwq: SSE streaming documentation

Progress: [##############################] ~100% (29 plans of ~29 estimated total)

Performance Metrics#

Velocity:

  • Total plans completed: 29
  • Average duration: ~6.3m
  • Total execution time: ~3.0 hours

By Phase:

Phase Plans Total Avg/Plan
01 - Foundation Quick Wins 2/2 ~17m ~8.5m
02 - Shared Annotations 4/4 ~26m ~6.5m
03 - Existing Client Review 6/6 ~36m ~6.0m
04 - JSON Primitive Encoding 5/5 ~65m ~13.0m
05 - JSON Nullable & Empty 4/4 ~21m ~5.3m
06 - JSON Data Encoding 4/4 ~30m ~7.5m
07 - JSON Structural Transforms 4/4 ~28m ~7.0m

Recent Trend:

  • Last 5 plans: 06-04 (4m), 07-01 (7m), 07-02 (7m), 07-03 (7m), 07-04 (7m)
  • Trend: Phase 7 consistent at ~7m/plan

Updated after each plan completion

Accumulated Context#

Decisions#

Decisions are logged in PROJECT.md Key Decisions table. Recent decisions affecting current work:

  • Roadmap: Foundation refactoring (shared annotations) must precede all JSON mapping work

  • Roadmap: Existing Go and TS clients must be reviewed/polished before any new features (Phase 3)

  • Roadmap: Cross-generator consistency validation is mandatory in every JSON mapping and language phase

  • Roadmap: Two capital sins: breaking backward compatibility, and inconsistencies between docs/clients/servers

  • Roadmap: JSON-01 (nullable) must precede JSON-06 (empty objects) due to dependency

  • Roadmap: Language clients (Phases 8-10) parallelizable after Phase 7 completes

  • Roadmap: JSON-08 (nested flattening) kept in v1 scope despite research suggesting deferral

  • D-01-01-01: Two-pass generation pattern for cross-file unwrap (collect all unwrap info globally first, then generate per-file)

  • D-01-01-02: Preserve root unwrap functionality while adding cross-file resolution

  • D-02-01-01: Transparent structs with protogen parameters -- all exported structs have exported fields, all functions accept protogen types

  • D-02-01-02: Unified QueryParam struct with all 7 fields from all 4 generators (FieldName, FieldGoName, FieldJSONName, ParamName, Required, FieldKind, Field)

  • D-02-01-03: Two unwrap APIs -- GetUnwrapField (full validation) and FindUnwrapField (simple lookup) for different generator needs

  • D-02-01-04: Convention-based extensibility -- one file per annotation concept, GetXxx() function signatures

  • D-02-02-01: Dead code removal -- parseExistingAnnotation removed during migration (always returned empty string)

  • D-02-02-02: Test deduplication -- httpgen annotation tests removed since covered by shared package

  • D-02-03-01: BuildHTTPPath safe for both generators -- httpPath always initialized before path building

  • D-02-03-02: Generator-specific naming helpers kept in respective packages (snakeToUpperCamel, snakeToLowerCamel, headerNameToPropertyName)

  • D-02-04-01: Lowercase HTTP method constants in openapiv3 -- OpenAPI requires lowercase, shared package returns uppercase, resolved with strings.ToLower() + local constants

  • D-02-04-02: OpenAPI-specific functions (convertHeadersToParameters, mapHeaderTypeToOpenAPI) stay in openapiv3/types.go, not shared package

  • D-02-04-03: Cross-file error propagation -- 5 functions changed to return errors, fail-hard up to Generator.Generate()

  • D-02-04-04: Serialization audit confirmed no changes needed -- encoding/json correctly used for interface checks only

  • D-03-02-01: JSON default for unknown content types everywhere -- bindDataBasedOnContentType, marshalResponse, writeProtoMessageResponse, writeResponseBody all default to JSON

  • D-03-02-02: Content-Type set in three response-writing functions covering all paths: writeProtoMessageResponse, genericHandler success path, writeResponseBody

  • D-03-01-01: Added UnwrapService to httpgen unwrap.proto (alongside OptionDataService) for cross-generator root-level unwrap testing

  • D-03-01-02: Root-level unwrap RPCs use POST method (not GET) to satisfy httpgen GET-with-body validation

  • D-03-01-03: Proto3 optional support added to go-http and go-client plugins via SupportedFeatures declaration

  • D-03-03-01: Go client already consistent with server - no fixes needed (audit verified 6 key areas: query params, Content-Type, errors, path params, headers, unwrap)

  • D-03-04-01: TS client already consistent with Go server - no fixes needed (int64 as string, query encoding, FieldViolation fields, header handling, all 4 unwrap variants)

  • D-03-04-02: No JSDoc generation by design - minimalist generated code

  • D-03-05-01: Error schema uses single 'message' field matching sebuf.http.Error proto (not error+code)

  • D-03-05-02: int64/uint64 mapped to type:string per proto3 JSON spec for JavaScript precision safety

  • D-03-05-03: Added headerTypeUint64 constant and removed minimum constraint since uint64 is now string type

  • D-03-06-01: Default path inconsistency for services without HTTP annotations is accepted (backward compat fallback mode only)

  • D-03-06-02: Cross-generator consistency verified for all 10 key areas (paths, methods, params, schemas, errors, headers, unwrap)

  • D-04-01-01: Extension numbers 50010-50012 continue sequence from existing 50009 (unwrap)

  • D-04-01-02: UNSPECIFIED (0) always means "use protojson default" - explicit STRING value available for documentation

  • D-04-01-03: GetEnumValueMapping returns empty string (not nil) for consistency with Go string semantics

  • D-04-03-01: tsScalarTypeForField pattern - keep base tsScalarType unchanged, add encoding-aware variant

  • D-04-03-02: appendInt64PrecisionWarning called after description set - ensures comment text + warning combined

  • D-04-03-03: nolint directives for valid lint warnings - exhaustive (has default), funlen (big switch), nestif (existing pattern)

  • D-04-02-01: Use protojson for base serialization, then modify map for NUMBER fields - preserves all other field handling

  • D-04-02-02: Print precision warning to stderr during generation, not at runtime - developer sees during build

  • D-04-02-03: Identical encoding.go implementation in httpgen and clientgen - guarantees server/client JSON match

  • D-04-04-01: Separate enum_encoding.go files in httpgen/clientgen to avoid import conflicts with int64 encoding.go

  • D-04-04-02: Both proto name and custom value accepted in UnmarshalJSON for backward compatibility

  • D-04-04-03: NUMBER encoding returns 'number' type in TypeScript, 'integer' type in OpenAPI

  • D-04-05-01: Split TestEncodingConsistencyAcrossGenerators into separate test functions for linting compliance

  • D-04-05-02: Use normalizeGeneratorComment to allow byte-level comparison between go-http and go-client

  • D-04-05-03: Convert openapiv3 enum_encoding.proto from duplicate file to symlink for consistency

  • D-05-01-01: Extension numbers 50013 (nullable) and 50014 (empty_behavior) continue sequence from 50012

  • D-05-01-02: UNSPECIFIED (0) means default behavior (same as PRESERVE for empty_behavior)

  • D-05-02-01: Identical nullable.go in httpgen and clientgen for server/client JSON consistency

  • D-05-02-02: Nullable TypeScript fields use T | null (not optional ?) - always present with value or null

  • D-05-02-03: OpenAPI 3.1 type array syntax [T, null] instead of deprecated nullable: true

  • D-05-02-04: Nullable encoding placed before service check in clientgen (like httpgen) for message-only files

  • D-05-03-01: Identical empty_behavior.go in httpgen and clientgen for server/client JSON consistency

  • D-05-03-02: OpenAPI oneOf schema for NULL fields ({$ref} | {type: null}) instead of deprecated nullable:true

  • D-05-03-03: OMIT fields use standard $ref in OpenAPI (serialization-only behavior, schema unchanged)

  • D-05-03-04: Exhaustive switch for EmptyBehavior enum to satisfy linter

  • D-05-04-01: Added empty_behavior test proto to clientgen (Rule 3 deviation) to enable byte-level golden file comparison

  • D-06-01-01: Extension numbers 50015-50016 continue sequence from existing 50014 (empty_behavior)

  • D-06-01-02: UNSPECIFIED (0) always means protojson default -- RFC3339 for timestamps, BASE64 for bytes

  • D-06-01-03: HasTimestampFormatAnnotation excludes both UNSPECIFIED and RFC3339 (both produce default behavior)

  • D-06-01-04: HasBytesEncodingAnnotation excludes both UNSPECIFIED and BASE64 (both produce default behavior)

  • D-06-02-01: Timestamp detected before generic MessageKind in type switches to prevent $ref generation

  • D-06-02-02: google.protobuf.Timestamp skipped from tsclientgen messageSet (primitive, not nested object)

  • D-06-02-03: convertTimestampField helper in openapiv3 for clean format-to-schema mapping

  • D-06-02-04: nolint:exhaustive on tsTimestampType switch -- default handles RFC3339/DATE/UNSPECIFIED

  • D-06-03-01: HEX UnmarshalJSON needs both encoding/hex AND encoding/base64 imports (re-encodes decoded hex as standard base64 for protojson)

  • D-06-03-02: nolint:dupl on MarshalJSON/UnmarshalJSON across empty_behavior, timestamp_format, bytes_encoding (three similar files trigger dupl threshold)

  • D-06-03-03: OpenAPI HEX uses format:hex with regex pattern ^[0-9a-fA-F]*$ for validation

  • D-06-03-04: OpenAPI BASE64URL uses format:base64url (not base64 with modifier) for clarity

  • D-07-01-01: Extension numbers 50017-50020 continue sequence from 50016 (bytes_encoding)

  • D-07-01-02: OneofConfig uses OneofOptions (not FieldOptions) -- first use of this extension target type in project

  • D-07-01-03: ValidateOneofDiscriminator split into 3 helper functions to stay under cognitive complexity limit

  • D-07-04-01: Helper functions verifyOneofDiscriminatorPresent/Absent to stay under nestif complexity limit

  • D-07-04-02: containsInInterface helper for precise TypeScript interface field verification

  • D-07-04-03: 800-char window for NestedEvent OpenAPI lookup (600 was insufficient for deeply nested YAML)

Pending Todos#

None.

Blockers/Concerns#

None -- Phase 7 complete, ready for language phases (8-10).

Quick Tasks Completed#

# Description Date Commit Directory
1 Fix 6 TS generator bugs: enum query params, repeated fields, duplicate URL const, unused req param 2026-02-27 9fef624 1-fix-6-ts-generator-bugs-enum-query-param
2 Add unit tests for tscommon helpers and enum_value coverage 2026-02-27 20957fd 2-add-unit-tests-for-tscommon-helpers-and-
3 Fix TS server path param fields missing from initial body literal 2026-02-27 ba48113 3-fix-ts-server-path-param-fields-missing-
260416-eht Implement SSE streaming support across all 5 generators 2026-04-16 8759337 260416-eht-implement-sse
260416-kwq Update CLAUDE.md with SSE streaming documentation for all 5 generators 2026-04-16 37b98c3 260416-kwq-streaming-clients-docs

Session Continuity#

Last session: 2026-04-16 Stopped at: Completed quick task 260416-kwq (SSE streaming documentation) Resume file: None Next: Phase 8 (Go Client Language) or Phase 9 (TS Client Language)