Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .squad/agents/fenster/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,7 @@

- Scribe recorded the YAML smoke release as shipped from commits `ac9ed97`, `e6f07ee`, and `55c260e`, preserving the two-layer split between the broad PowerShell sweep and the curated generated harness.
- The shared decision log now carries the collision-safe `__<format>__` smoke filename rule and the explicit wildcard denylist policy for deterministic pre-wrapper YAML parse failures.

### 2026-05-20T15:40:56Z — Binary request-body support (issue #53) — 8 atomic commits

- Implemented all converters + API generator changes for binary request-body support: added `Parameter.content_type` field, updated v2.0/v3.0/v3.1/v3.2 converters to capture media-type strings, introduced `BodyKind` classifier, parameterized `appendClientHeaders` for dynamic Content-Type, and emitted `[]const u8` for binary/text bodies. All 8 commits approved by Lando on `support-binary-payloads-opus` branch. Validation: `zig build test` ✓, `zig build run-generate` ✓, generated `uploadFile` now emits `requestBody: []const u8` + `Content-Type: application/octet-stream`. JSON paths byte-identical.
31 changes: 31 additions & 0 deletions .squad/agents/lando/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,34 @@ efAllDeclsRecursive wrapper if generator gaps slip through.
### 2026-05-01T09:50:14Z — Scribe closeout

- Scribe recorded Lando's release approval: the curated-vs-broad smoke split holds, JSON/YAML sibling output collisions are prevented by format-aware names, and the `openapi/v3.2` boundary stays JSON-only until a real YAML root fixture exists.

### 2026-05-20T15:40:56Z — Binary request-body support (issue #53) — design + review

- Led cross-functional effort: produced comprehensive design spec for binary request-body support (Parameter.content_type field, media-type capture in converters, BodyKind classifier, Content-Type parameterization, []const u8 emission for binary/text). Reviewed all 8 commits from Fenster against design specification and APPROVED for merge. Outcome: 8 atomic commits on `support-binary-payloads-opus`, all validation gates green, design fully realized with no regressions on JSON paths.

### 2026-05-20T17:03:44+02:00 — Binary request body design (issue #53)

- Issue #53 root cause confirmed across two layers: (1) the unified `Parameter` model in `src/models/common/document.zig` has no media-type field, so all converters silently drop the declared content type; (2) `api_generator.zig` at L611 and L1431 unconditionally JSON-stringifies, and `appendClientHeaders` at L507 hard-codes `Content-Type: application/json`.
- Edge-toolkit/core PR #49 reviewed: not a fix template. They post-process generated Zig with tree-sitter to swap a WASM extern; no upstream binary support. We design from scratch.
- Detection placement: parse-time in converters, not generate-time. Adds one allocator-owned `content_type: ?[]const u8` to `Parameter`. Keeps generator dumb; schema sniffing (`format: binary`) is unreliable for Swagger v2 anyway because v2 carries `consumes` operation-level.
- Selection rule (preserves current behavior on the JSON path): JSON wins → else first entry → else null (defaults to JSON downstream). Same rule across all three OpenAPI converters; Swagger converter resolves operation `consumes` then falls back to spec-level `consumes`.
- Generator emits via a private `BodyKind { none, json, binary, text, form }` classifier. Binary/text → `requestBody: []const u8` parameter and raw payload pass-through. `appendClientHeaders` signature changes from `include_content_type: bool` to `content_type: ?[]const u8`.
- Streaming explicitly out of scope for v1: payload stays a single `[]const u8` slice — same lifetime contract as today's stringified JSON buffer. Streaming uploads tracked as a follow-up.
- `multipart/form-data` and `application/x-www-form-urlencoded` are recognized but not implemented; generator emits a `TODO(#53-followup)` comment and falls through to today's behavior so consumers see why uploads silently misbehave.
- `` request bodies stay unsupported (`content_type = null` → JSON default). Following refs for media-type resolution is a much larger refactor and tracked separately.
- Test corpus: petstore v3.0 `uploadFile` is the canonical binary case (octet-stream, format: binary). Petstore v2.0 `uploadFile` is multipart and stays in the "documented limitation" bucket. Snapshot stability for petstore JSON paths (e.g. `addPet`) is the regression gate for C6 and C7.
- Decomposition for Fenster is 8 commits: C1 model field, C2/C3/C4 converters, C5 no-op classifier refactor, C6 header parameterization (byte-identical JSON output), C7 the user-visible fix at both call sites, C8 docs/smoke. JSON path must stay byte-stable except where binary kicks in — if C6 or C7 regress JSON, revert and re-split.
- Christian's standing instruction this stream: small frequent commits, NO `Co-authored-by` trailer.
- Decision recorded: `.squad/decisions/inbox/lando-binary-payloads-design.md`.

## 2026-05-20T17:03:44+02:00 — Review of #53 (Fenster, branch support-binary-payloads-opus)

- Verdict: APPROVED.
- 8 commits map 1:1 to design's C1-C8. Memory safety verified: Parameter.content_type alloc.dupe'd in all four converters, freed in Parameter.deinit. No leaks introduced.
- JSON-path: addPet/updatePet signatures and bodies functionally unchanged. Cosmetic whitespace drift in regenerated snapshots on multi-arg print format-args ('{ a, b }' to '{a, b}') and trailing-space comment lines. Smoke + tests + run-generate green.
- Edge cases: '*/*' to binary OK, null/empty content_type to JSON OK, multipart/x-www-form-urlencoded to form fallback with TODO(#53-followup) OK, +json suffix preferred over non-JSON OK.
- Deviations (non-blocking): (a) classifyBody routes any unmatched 'application/*' to binary, broader than design's enumerated list but conservative (JSON-stringify on unknown app types was already broken); (b) swagger_converter.selectConsumesMedia does not skip multipart before falling to list[0] - masked by .form fallback; (c) tests #4 ( body) and #11 (v2 multipart uploadFile TODO) not asserted - #4's behavior preserved by null default, #11 doesn't apply because petstore v2 uploadFile uses formData params (no body, no TODO emitted/expected).
- Implementation note: binary/text *Raw functions inline client.http.fetch instead of delegating to requestRaw (which hard-codes JSON Content-Type). Future cleanup opportunity: requestRawWithContentType helper.
- Commit hygiene: 8 atomic commits, descriptive messages, no Co-authored-by trailers, scoped paths.
- README: known-limitations note matches scope (multipart + form fallback called out). Minor doc gap: \ body limitation not mentioned. Not blocking.
- Approval artifact: .squad/decisions/inbox/lando-binary-payloads-approved.md
12 changes: 12 additions & 0 deletions .squad/agents/starkiller/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,15 @@
### 2026-05-01T09:50:14Z — Scribe closeout

- Scribe recorded Starkiller's acceptance gate: YAML is exercised in both smoke layers, `openapi/v3.2` remains JSON-only, and the remaining denylisted YAML parser/normalization gaps are follow-up backend work rather than blockers for this scoped smoke-coverage release.

### 2026-05-20T17:03:44+02:00 — Binary payload (#53) test plan drafted

- Anticipatory draft for binary-payload support landed at `.squad/decisions/inbox/starkiller-binary-payload-test-plan.md`.
- Spec inventory: v3 has petstore `uploadFile` (octet-stream), `api-with-examples` (`*/*`), and hubspot specs as realism checks. **Gap:** no v2.0 spec uses `consumes: [application/octet-stream]` — only `multipart/form-data` (petstore) or `*/*` (api-with-examples). Plan asks Fenster to add `openapi/v2.0/binary-upload.{json,yaml}` and `openapi/v3.0/binary-upload.{json,yaml}` minimal fixtures.
- Test layers identified: (A) unified-IR converter assertions, (B) generated-code substring assertions following the `openapi_v3_tests.zig` / `resource_wrapper_tests.zig` pattern, (C) memory loop test under `test_utils.createTestAllocator()`, (D) smoke harness + `generated/main.zig`, (E) negative/edge (multi-content priority, global vs operation `consumes`, `*/*` default header, multipart unchanged).
- Open questions parked for Fenster: IR field name for body-kind, default Content-Type for `*/*`, multi-content priority rule, generated binary parameter name.
- Quality-gate rule reaffirmed: no new smoke denylist entries permitted for this PR; new fixtures must auto-flow through `comprehensive_converter_tests.zig` directory iteration and `test/smoke-tests.ps1` discovery.

### 2026-05-20T15:40:56Z — Binary request-body support (issue #53) — test plan locked

- Confirmed test-plan contract for Fenster's 8-commit implementation: 31 test cases across IR (A1–A10), code-gen output shape (B1–B10), memory safety (C1–C2), smoke/E2E (D1–D4), and edge cases (E1–E5). All A-series assertions map cleanly to converter commits; all B-series output assertions align with generation commits. Validation gates: `zig build test` green, `smoke-tests.ps1` 88-case sweep unchanged (no new denylist entries), no memory leaks under `createTestAllocator()`. Ready for implementation validation post-merge.
11 changes: 11 additions & 0 deletions .squad/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,14 @@
- **Release gate:** Lando and Starkiller approved release as scoped: YAML is exercised in both smoke layers, and remaining denylisted YAML parser/normalization gaps stay explicit follow-up backend work rather than blocking this smoke-coverage expansion.
- **Session-scoped directive:** Use GPT-5.5 for the rest of this session only.
- **Sources merged:** `copilot-directive-2026-05-01T11-50-14.md`, `fenster-yaml-smoke.md`, `juno-yaml-smoke-docs.md`, `lando-yaml-smoke-verdict.md`, `starkiller-yaml-validation.md`.

### 2026-05-20: Binary Request Body Support (issue #53)

- **Decision:** Emit `[]const u8` raw-byte request-body parameters (with appropriate `Content-Type` headers) for specs declaring `application/octet-stream`, `image/*`, `video/*`, `audio/*`, `application/pdf`, and other binary MIME types, instead of JSON-stringifying the binary blob.
- **Architecture:** Add optional `content_type: ?[]const u8` field to the unified `Parameter` model at parse time. Converters capture the chosen media type (JSON-first, then first entry, then null). A private `BodyKind` classifier routes the generator to emit `[]const u8` for binary/text content types, `std.json` for JSON/form, and a TODO comment for multipart (future work). The `appendClientHeaders` runtime helper is parameterized from `bool` to `?[]const u8` to emit the declared `Content-Type` instead of hard-coded `application/json`.
- **Scope (included):** OpenAPI v3.0 / v3.1 / v3.2 and Swagger v2.0 request bodies declared as binary, image, video, audio, PDF, or wildcard (`*/*`). Text bodies get the same `[]const u8` path for consistency.
- **Scope (deferred):** Multipart form data, URL-encoded forms, `$ref` request-body resolution, true streaming uploads. These are documented as follow-ups and emit TODO markers in generated code when encountered.
- **Generated code contract:** Binary/text functions take `requestBody: []const u8` (caller-owned bytes), emit the declared media-type as `Content-Type` header, and pass raw bytes to the HTTP client without JSON serialization. JSON functions remain byte-identical (no snapshot churn except whitespace from Zig 0.16.0 formatting). Multipart/form operations fall back to JSON path + TODO.
- **Test coverage:** Unit tests in `unified_converter_tests.zig` assert IR field capture for binary, JSON, and wildcard content types. Generator tests assert function signatures, header emission, and absence of JSON serialization on the binary path. Memory ownership is validated under `test_utils.createTestAllocator()`. Smoke-test harness (`test/smoke-tests.ps1`) remains green; no new denylist entries required.
- **Session-scoped directive (2026-05-20T17:03:44+02:00, Christian Helle via Copilot):** All agents use Claude Opus 4.7 for this session only. Commits omit `Co-authored-by` trailer for detailed progress history (do not persist as permanent rule).
- **Sources merged:** `lando-binary-payloads-design.md`, `starkiller-binary-payload-test-plan.md`, `lando-binary-payloads-approved.md`, `copilot-directive-20260520T1703.md`.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ Generated files are self-contained Zig source files. The current unified generat

Parsed JSON responses use `.ignore_unknown_fields = true` so compatible providers can add response fields without breaking callers. Ambiguous or intentionally open-ended schemas use `std.json.Value`; see [`docs/json-value-typing-policy.md`](docs/json-value-typing-policy.md) for the current policy. For OpenAPI 3.1, the converter has stronger composite-schema handling for object/ref `allOf`, preserved `oneOf`/`anyOf` metadata, and nullable type arrays; do not assume every converter has identical composite support.

### Request body content types

The generator inspects each operation's request body (or Swagger 2.0 `consumes`) and picks the first JSON-flavoured media type when one is available. Bodies classified as binary (`application/octet-stream`, `image/*`, `audio/*`, `video/*`, `*/*`, other `application/*`) or text (`text/*`) generate a `requestBody: []const u8` parameter that is passed straight to `std.http.Client.fetch` with the matching `Content-Type` header — no JSON encoding is applied.

**Known limitations:** `multipart/form-data` and `application/x-www-form-urlencoded` request bodies are not yet supported. Operations declaring those media types currently fall back to JSON encoding and emit a `TODO(#53-followup)` comment in the generated source; full multipart support is tracked as follow-up work.

## Example Generated Code

The snippets below reflect the current output from `zig build run-generate-v3`.
Expand Down
Loading
Loading