Skip to content

[BLOCKER] tdbin rejects struct-form union variants — verify fails on the first union in a real schema #61

Description

@MelbourneDeveloper

Problem

tdbin refuses any union whose variant carries named fields. Our production schema fails on the very first union:

$ typediagram verify docs/models/live-ipc.td
   0:0  error   tdbin: variant 'OpenRange' must be bare or a single tuple field in v0

The offending declaration is ordinary typeDiagram DSL that --to rust, --to typescript, --to csharp etc. all accept:

union FindSimilarInput {
  OpenRange {
    path: String
    start_byte: Int
    end_byte: Int
  }
  Snippet {
    snippet: String
    language: String
  }
}

This is not a rare shape. In one schema we have four of them, and they are all load-bearing wire types:

Union Variants with fields
FindSimilarInput OpenRange { path, start_byte, end_byte }, Snippet { snippet, language }
AnalysisState Running { started_at_ms }, Errored { message }
RequestId Number { value }, String { value }
MergeVerdict AiOrHuman { reason }

Because verify aborts at the first one, tdbin currently covers zero percent of our schema. There is no incremental adoption path — not "most types work", but "the file is rejected".

Why this looks like a generator gap, not a format gap

docs/tdbin-wire-format.html already specifies the encoding:

"Records and struct-unions are pointer-encoded as separate objects."

and the evolution rules already talk about payload variants:

"Appending bare or payload variants to unions (if discriminant width unchanged)"

So the wire format contemplates struct-shaped variants. The restriction appears to live in the emitter/verifier, phrased as a v0 limitation.

Ask

Support struct-form union variants in encode / decode / verify, encoded per the existing struct-union pointer rules: discriminant in the data section, variant payload as a pointer to a struct laid out by the normal [TDBIN-REC-ALLOC] rules.

If that genuinely cannot land in v0, then at minimum:

  1. Make the error message say what the workaround is, and
  2. Do not abort the whole file — report every unsupported declaration in one pass so a consumer can see the total scope of the problem instead of fixing one and re-running.

Rewriting each variant into a named side record is not an acceptable workaround for us: the .td file is the single source of truth for the JSON wire shape and the Rust/TS types, and flattening the unions changes the JSON representation that shipped clients already depend on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CRITICALCritical severity — fix immediately

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions