Model the special-cased search branches and person_minimal's guaranteed keys
Two spec-shape corrections, one commit per issue, each carrying its own full regeneration across all six SDKs. Both were audited against bc3 at exactly the provenance pin (all four special-cased search branches read from the object store at the pinned revision, every fact re-verified before encoding).
SearchResult models what the four special branches actually emit (#651)
bc3's search rendering special-cases four result types (searches/attachment, chats/lines/line, kanban/lists/list, gauges/needles/needle — exhaustively enumerated from the dispatch helper; there is no fifth). Only the attachment branch violates the old @required set; the others contribute optional keys.
- Demoted
id,title,type,url,app_urlfrom@required— the attachment branch emits none of them.content/descriptionstay required: the show template nil-overwrites them after every branch partial. - Added 26 optional members: the attachment branch's 10 file keys (width/height are float-spelled ints upstream → the existing
*types.FlexIntnullable pattern), kanban list keys (on_holdreusesCardColumnOnHold, whose 8 subkeys match the branch emission exactly), gauge-needle keys, chat-line keys, and common-envelopesubscription_url. attachmentsgot its own element shape. The old doc comment claimed top-levelattachmentsalways duplicates the rich-text companion array — falsified by chat upload lines, which build a bespoke inline{title, url, filename, content_type, byte_size, download_url}aggregate and have norich_text_content, so the overwrite never fires and the bespoke shape reaches the wire. It missesRichTextAttachment's requiredid/sgid/previewable/preview_url/thumbnail_url, soSearchResultAttachmentis an optional-field superset of both variants (the TimelineAttachment idiom) with only the 4 both-variant keys required.RichTextAttachmentitself is untouched — its other emitters do guarantee those fields.
openapi diff is exactly the intended change: required [app_url,content,description,id,title,type,url] → [content,description], 26 properties added, 0 removed, one new schema, paths identical, no projection side effects.
Fixtures: spec/fixtures/search/results.json grew from 4 to 8 hits — one per special branch, including a float-spelled 1920.0 width, a chat-upload bespoke aggregate, color: null, and on_hold — with the coverage guard red-proved against the old schema (fails on exactly the five demotions + the unknown shape) and green after. Go/TS/Ruby/Python search tests each assert an attachment-branch hit end-to-end; the Go wrapper carries the 26 fields under the wrapper-drift gate (92 pairs, 1245 fields).
person_minimal's guaranteed keys become @required (#659)
people/_person_minimal.json.jbuilder unconditionally emits id, name, avatar_url (avatar_url is a route helper, never nil); of its three consuming shapes, UpcomingSchedulePerson already marked all three @required. MyAssignmentAssignee and OutOfOfficePerson now match. No projection patch needed — proven by build+diff: exactly the two schemas' required arrays change and nothing else.
Generated flips: Go *string → string; Swift var String? → let String (decode now throws on a missing key — noted as class B in MIGRATING); TS name?: → name:; Python NotRequired[str] → str; Ruby required_fields. Kotlin is a no-op for both commits (no typed Kotlin model exists for these shapes; services return JsonElement).
MIGRATING
New Unreleased section with per-SDK was/now tables for both changes, counts derived per the document's stated-derivation rule.
Verification
Full serial make check green at HEAD ("All checks passed"), including go/kt/py drift, check-go-optional-pointers, check-kotlin-optional-arrays-and-scalars, fixture coverage, wrapper drift, and doc-constants. Per-suite: Go 0 failures, Ruby 1412 runs/0 failures, TS 1485 passed, Python 1215 passed, Swift 375 passed.
Deliberately out of scope (declared in the commit message): the attachment branch's grandparent parent-id/title quirk (semantic, not structural) and chat lines' dead subscription_url/position. Follow-up to be filed on close: a six-runner Search conformance fixture (no runner has a mock Search fixture today; Swift doesn't dispatch Search at all).
Summary by cubic
Models the four special-cased search result branches and makes person_minimal’s three keys required. Previously SearchResult required id/title/type/url/app_url; attachment hits omitted them and the Go wrapper fabricated zeros. Now only content and description are required, and the Go wrapper uses omitempty to preserve key absence.
- Adds optional fields for attachments, kanban lists, gauge needles, chat lines, and shared envelope; width/height accept float-spelled values. Top-level
attachmentsis now a typedSearchResultAttachmentthat covers both rich-text and chat-upload shapes. - Requires
avatar_urlandnameonMyAssignmentAssigneeandOutOfOfficePerson, matchingperson_minimal. Adds per-SDK tests asserting all three keys on My Assignments reads; updates the out-of-office disabled stub to includeavatar_url. - Retires the “attachments” gap in docs by marking it absorbed; the spec references
SearchResult$attachments/SearchResultAttachment.
Migration
- Do not assume
id,title,type,url, orapp_urlexist on everySearchResult. Continue to expectcontentanddescriptionas present (nullable). - Treat
attachmentselements asSearchResultAttachment; do not assume the rich‑text attachment shape. - Update callers to require
avatar_urlandnameonMyAssignmentAssigneeandOutOfOfficePerson(e.g., non-optional in Swift/TS, non-pointer in Go, required in Python/Ruby).