Skip to content

[CRITICAL] Express field metadata (constraints, defaults, descriptions, aliases, config) so generated DTOs need zero hand-maintenance #39

Description

@MelbourneDeveloper

Make typeDiagram a complete single source of truth for API DTOs — full requirements

Companion to #38 (scalar types) and #29 (optional-vs-null). This is the complete list of what a contract-first DTO generator must express so consumers never hand-maintain a parallel copy. Every item below is something we currently CANNOT put in the .td and are therefore forced to hand-write in Python (Pydantic) — which then drifts from the contract. Each maps cleanly to Pydantic Field(...), C# DataAnnotations/JSON attributes, and OpenAPI.

1. Field-level constraints (validation metadata) — HIGHEST PRIORITY

Real fields we hand-maintain solely for these:

our field needed metadata
ChatRequest.message min_length=1, max_length=100_000
SshKeyRequest.public_key min_length=1, max_length=10_000
BundleFileWriteRequest.content max_length=2_000_000
ConversationSummary.message_count ge=0
ToolFileWriteRequest.mode ge=0, le=0o777
Manifest.build_command min_length=1, max_length=1000

Proposed syntax (attributes on the field):

type ChatRequest {
  message: Option<String> @minLen(1) @maxLen(100000)
}
type ConversationSummary {
  message_count: Int @min(0)
}

Constraint vocabulary: @minLen/@maxLen, @min/@max (inclusive), @exclusiveMin/@exclusiveMax, @multipleOf, @pattern("regex").
Lowering: Pydantic Field(min_length=, max_length=, ge=, le=, gt=, lt=, multiple_of=, pattern=); C# [MinLength]/[MaxLength]/[Range]/[RegularExpression]; OpenAPI minLength/maxLength/minimum/maximum/pattern.

2. Explicit default values (not just Option→null)

Fields whose default is a non-null literal — currently un-expressible:
ToolEntry.method = "POST", Manifest.template_dir = "template", ProvisionRequest.workspace_git_push_mode = "manual_only", ToolResultIn.ok = true.

type ToolEntry { method: String @default("POST") }

Lowering: Pydantic field default; C# property initializer; OpenAPI default.

3. String formats

@format(email|uri|uuid|date-time|hostname|ipv4|ipv6|byte) → Pydantic EmailStr/AnyUrl/Field(pattern=...), OpenAPI format.

4. Field descriptions / doc comments

Doc comments must survive into the output (Pydantic Field(description=...), C# XML ///, OpenAPI description). Example we hand-maintain: MessageResponse.tool_calls has a paragraph explaining the content/tool_calls split.

type MessageResponse {
  /// Tool calls emitted this turn, separate from `content` so UIs can hide tool activity.
  tool_calls: List<ToolCallOut>
}

5. Field aliases (wire name ≠ language name)

We have from_: str = Field(alias="from", serialization_alias="from") because from is a Python keyword. Need @alias("from") → Pydantic alias + populate_by_name, C# [JsonPropertyName].

6. Model-level config

@extra(forbid|ignore|allow), @frozen. We use extra="forbid" on request DTOs to reject typos (ConversationSummary, WorkspaceInstanceSummary) and extra="ignore" elsewhere. Lowering: Pydantic model_config = ConfigDict(extra=, frozen=); C# record immutability / JsonExtensionData.

7. @example(...) and @deprecated

For OpenAPI/docs quality: per-field examples and a deprecation marker.

8. (from #38) native DateTime/Uuid/Decimal scalars + fail-closed on unknown type identifiers.

Why this is critical infrastructure

With items 1–6 NAP can delete its entire hand-written DTO module and generate 100% from the .td. Without them, every constrained/aliased/defaulted/documented field forces a hand copy, the hand copy drifts, and we ship incidents (the conversation-history leak we just fixed was exactly this failure mode). We will adopt a patch build the moment 1, 2, and 4 land — those three remove ~90% of our hand-maintenance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CRITICALCritical severity — fix immediately

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions