ADR 0013 DTO classes instead of zod - agbossjs/agboss GitHub Wiki

ADR-0013 — DTO classes instead of zod

  • Status: Accepted (supersedes the zod part of ADR-0003)
  • Issue: #118 (spikes #113, #115)

Decision

  • Data shapes — tool inputs and outputs, server tools — are classes with one field decorator per field from agboss/dto (@Text, @Integer, @DateTime, @ListOf(Job), …). The decorator says what the model reads (prompt) and what is checked; the class is the type. zod is used only inside the framework (schemaOf), and the model sees jsonSchemaOf (io: "input", no $schema, fields in declaration order).
  • Compile time is exactly: decorator ↔ field type, optional ↔ ?, @OneOf values ≡ the union, the element type of @ListOf / @Nested, a public non-static field. Formats, limits, integer vs decimal and cross-field rules are runtime validation. An undecorated field or a DTO with behaviour is a startup error (dto.undecorated-field, dto.not-plain-data).
  • DTOs are plain data (no methods, no private fields), so object literals fit them; the price is structural typing — two DTOs with the same fields are interchangeable to the compiler.
  • Metadata goes through context.metadata; agboss sets Symbol.metadata when imported, because tsc output has none on Node 22–26. A test runs a tsc-compiled DTO against dist.
  • 16 standard DTOs ship with the framework (Standard DTOs); names come in pairs from one noun (FileRead → FileContent).

Consequences

  • One way to declare data in a workflow; job-scout, agboss g tool and agboss g mcp use DTOs.
  • Configuration (search.config.ts) and the run input keep zod until #121 / #117.
  • A server tool that returns only text declares TextAnswer (one field text) instead of z.string().