feat: generated-surface attachment inputs for send_message (COD-406, IRIS-11 PR3) - #30
Conversation
Adopt hydra v0.2.0 json parameters: declare `attachments` in api/operations.yaml as a body json parameter carrying the closed inline/stored union schema plus CLI representation overrides (repeatable --attach with companion --attach-mime). Regenerate CLI/MCP artifacts and pin hydra to tag v0.2.0. Runtime decoding lives in iris-core::wire (pure, no I/O): - decode_attachments: union -> OutboundAttachment::Bytes/Stored, rejecting malformed base64, invalid UUIDs, empty inline bytes, missing MIME, mixed/unknown fields before any dispatch - plan_attachments: local-path vs iris://attachment/UUID split, --attach-mime cardinality (exactly one per local path, in order, stored refs consume none) with explicit override of extension inference HTTP routes and the MCP handler decode through the shared module (400 / JSON-RPC rejection on malformed unions); the CLI reads local files at the boundary and builds the OutboundMessage. Surface tests: HTTP 400 on mixed/unknown/bad-base64, MCP tool rejection, CLI flag parsing + boundary reads + mime override ordering, generated-schema union assertions, and a codegen freshness test mirroring `iris-codegen check`. 180/180 tests; build/clippy/fmt/codegen green.
…anel) Review panel findings on COD-406: - Reviewer B [MAJOR]: decode_item mapped explicit `filename: null` to None, widening the declared optional-but-not-nullable union schema. Now rejected before dispatch like any other unknown/malformed union member. - Reviewer A [MAJOR]: CLI --attach-mime overrides accepted empty/whitespace values while HTTP/MCP reject blank mime_type. plan_attachments now rejects blank overrides, keeping all three surfaces on the same contract. Both regression-tested in iris-core::wire. The panel's Docker-deletion BLOCK was a false positive caused by base drift: PR #29 added Docker files to main after this branch was cut; the rebase onto 0c6293c resolves it. No Docker files are touched by this branch.
🤖 Automated Review PanelPanel: dual independent review of the branch diff (pre-PR). Findings triaged below; blocking/major items were fixed in c20a938 before this PR was opened.
Reviewer A (glm-5.1, panel seat A)BLOCK: unrelated Docker packaging files are deleted, causing a deployment regression outside COD-406 scope.
Reviewer B (glm-5.1, panel seat B)BLOCK: unrelated deletion of Docker packaging files regresses self-hostable/container deployment and is outside COD-406 scope.
Post-fix verification: 182/182 tests (+2 regression), build/clippy |
Auto-Merge Gate rationaleConfidence: 0.93 COD-406's stated T8–T11 goal is clearly implemented: an explicit closed inline/stored attachment union is declared in Checks observed: GitHub CI Test, Format Check, Clippy, Secret Scan, Dependency Audit, and Creed context drift all passed. The gate also re-ran Scope/safety: 15 files, confined to the attachment API/codegen/runtime surfaces and their tests. No secrets, deployment/cutover, billing, destructive behavior, or manual-review marker was found in COD-406 or the PR. The prior review-panel concerns were addressed in the final head commit and have regression tests. |
COD-406 — IRIS-11 PR3: Generated-surface attachment inputs (T8–T10)
Third implementation slice of the
add-outbound-attachmentsOpenSpec change (7/11 on main after #26). PR1 (#21) landed the core contract + store resolution + provider plumbing; PR2 (#26) landed Telegram multipart + Email MIME sends. This PR closes the gap: the generated surfaces (HTTP/MCP/CLI) can now express attachment inputs.What changed
T8 — API definition + codegen (via hydra v0.2.0 json-params, adopted from hydra PR#3 / COD-411):
api/operations.yaml:send_messagegains anattachmentsbody parameter declared astype: jsonwith the closed inline/stored union schema — inline requiresmime_type+data_base64, permits only optionalfilename, forbidsstored_id; stored requires onlystored_id, forbids inline fields. No name-based inference; the union is declared explicitly.--attachwith companion--attach-mime.crates/iris-codegenpinned to hydrav0.2.0(c3923dc);generated/cli.rs+generated/mcp.jsonregenerated.T9 — wire decoding + CLI planning (new
iris-core::wire, pure, no I/O):decode_attachments: union JSON →OutboundAttachmentvariants, rejecting malformed base64, invalid UUIDs, empty inline bytes, missing MIME, mixed/unknown fields — before any dispatch.plan_attachments: local-path vsiris://attachment/UUIDsplit;--attach-mimecardinality (exactly one per local-path attachment, in order; stored refs consume none) with explicit override of extension MIME inference.OutboundMessage.T10 — tests: HTTP 400 on mixed/unknown/bad-base64 unions, MCP tool rejection, CLI flag parsing + boundary reads + mime override ordering, generated-schema union assertions, codegen freshness test (
crates/iris-codegen/tests/freshness.rs) mirroringiris-codegen check.Verification (T11)
All five gates green on this branch, re-verified this run:
cargo build --all-targets✅cargo test --all-targets✅ — 180/180 (was 150)cargo clippy --all-targets -- -D warnings✅cargo fmt --all -- --check✅cargo run -p iris-codegen --bin iris-codegen -- check✅(
imap-protofuture-incompat warning is pre-existing on main.)OpenSpec
add-outbound-attachmentstasks T8–T11 checked on this branch; the change is now 11/11 pending this merge.Linear: COD-406