Skip to content

Repin bc3 to 2c0dafba13 and absorb the Folders API it brings into range - #593

Merged
jeremy merged 4 commits into
mainfrom
feat/folders-and-repin
Aug 3, 2026
Merged

Repin bc3 to 2c0dafba13 and absorb the Folders API it brings into range#593
jeremy merged 4 commits into
mainfrom
feat/folders-and-repin

Conversation

@jeremy

@jeremy jeremy commented Aug 3, 2026

Copy link
Copy Markdown
Member

Repins bc3 provenance d0edc1283b2c0dafba13 and absorbs the Folders API
that repin brings into range, as one change. 238 → 243 operations.

Why these are coupled

doc/api/sections/folders.md does not exist at the old pin, so the five
/stacks routes are absent from spec/bc3-routes.json today. Repinning alone
makes them appear in the route table with no operation modelling them, and the
#586 parity gate (direction 2) then demands a disposition for five routes we
have every reason to model. Absorbing alone would model a contract the pin says
isn't there. Doing both is the only coherent order.

The repin — 11 commits, one API-surface change

Commit What Action
dc6cd10714 #12384 Folders API absorbed (below)
344581a379 #12494 Drafts keep subscribers when an update doesn't address them repin only
2c0dafba13 #12501 Screen a drafted entry's participants before subscribing repin only
19956c5579 #12488 Web deprioritize goes exact-target repin only — see below
845df62b6b #12487 action_push_native structured errors not API surface
112c1664bf 63a34abe30 5f889d8365 faf5fe9dfc pre-commit hook harness dev tooling
1fad1ef608 c664cdc9de Turbo-morph controller split web only

#12494 / #12501 are behaviour fixes to endpoints the SDK already models.
They add prose to five doc/api sections but no route and no payload field —
proven, not assumed: regenerating spec/bc3-routes.json at the new pin yields
exactly five new entries, all /stacks.

#12488 verified independently, and the brief's read of it holds. It deletes
a request.format.json? branch, which looks like an API change. It isn't. The
removed branch returned nil for JSON when the recording's own assignment
wasn't prioritized; the replacement returns that same assignment unconditionally
and &.unprioritize is a no-op when there's no priority to remove. Both
spellings target exactly the id in the URL and both answer 204. What changed
is that the web path stopped cascading to a sibling step — it adopted the
contract the JSON API has had since bc3#12483 (absorbed in SDK #528).

The absorption — FoldersService

ListFolders    GET    /{accountId}/stacks.json        200  bare array of Folder
GetFolder      GET    /{accountId}/stacks/{folderId}  200  FolderWithProjects
CreateFolder   POST   /{accountId}/stacks.json        201  FolderWithProjects
UpdateFolder   PUT    /{accountId}/stacks/{folderId}  200  FolderWithProjects
DeleteFolder   DELETE /{accountId}/stacks/{folderId}  204  no body

The wire type stays Stack. The product was renamed, the payload was not.
The operations, structures and generated methods use Folder; the URI segment
and the type discriminator keep stack/Stack. Anything matching on type
must match "Stack".

Two output structures, not one. Folder is what the list returns;
only FolderWithProjects carries the expanded projects. A single shape with
an optional member would make every generated list-item type declare a field the
list response never populates.

Three required-and-nullable fields. gauges_url, color and image_url
are emitted unconditionally by app/views/api/stacks/_stack.json.jbuilder and
are commonly null, so @required models the presence and smithy-build.json
jsonAdd layers type: ["string","null"] on top — SPEC §"A third wire state",
the same treatment Wormhole.color/destination_url already get. Go types them
*string because the value is nullable, not because the key is optional.

project_ids doesn't round-trip. Create takes it; the response reports the
same ids as bucket_ids and expands them as projects. Three names, one
relationship. An unreachable id fails the whole request with 404 and writes
nothing, so CreateFolder lists NotFoundError. UpdateFolder lists
ValidationError because a blank name is a real 422 — create defaults it to
New folder instead. UpdateFolder/DeleteFolder are naturally idempotent;
CreateFolder is not.

Generator overrides: one. The Folders tag resolves to FoldersService
through every generator's default fallback (zero service-group overrides), and
ListFolders infers the folder resource type cleanly. The one addition is
GetFoldergetFolder in TS/Kotlin/Swift: the bare rule collapses it to
folder(id), a noun with no verb, while Ruby and Python already emit
get_folder. Same shape as the GetBookmark override — all six SDKs now agree.

Go note. FolderWithProjects repeats the base fields rather than embedding
Folder. Embedding promotes them correctly at runtime but hides them from
check-wrapper-drift, which walks declared fields and reported all twelve as
missing. Repeating them keeps the guard able to verify every field.

Registry and coordination

  • spec/api-gaps/folders-api.mdabsorbed-in-sdk, seven smithy_refs.
  • spec/api-gaps/stack-doc-and-smithy.md keeps confirmed-not-api-resource
    on purpose. It records a decision that was correct when made; the supersession
    note in folders-api.md exists to preserve that history, not to be erased by
    it. Registration issue Register the Folders API gap (bc3 #12384) and supersede stack-doc-and-smithy #559 is already closed.
  • spec/bc3-route-allowlist.yml needs no change — no /stacks entry existed
    (the routes weren't in the table), and no entry became redundant.
  • COORDINATION.md named pin 338b7a11, three repins stale. Now names
    2c0dafba13 and says the provenance file is the only authority.

Verification, with real exit codes

Gate Result
make check REAL_EXIT=0
BC3_REPO_PATH=… make bc3-routes-check REAL_EXIT=0
make bc3-route-parity REAL_EXIT=0 — five /stacks routes matched both directions
Swift (macOS-gated, actually ran) swift build + swift test: 306 tests, 0 failures; swift run ConformanceRunner executed
Ruby 1092 runs, 2572 assertions, 0 failures (make rb-check)
Python 867 passed, mypy clean, ruff clean
Conformance 147 passed, 0 failed, 1 skipped (pre-existing Go-runner origin case)

The five new paths.json cases PASS in all six runners — Go, Kotlin,
TypeScript, Ruby, Python and Swift each got a dispatch branch, so none silently
skips. Confirmed per-runner in the log, and for TypeScript (whose summary is
compact) by a --reporter=verbose run naming all five.

Counts bumped deliberately, each derived from behavior-model.json rather than
incremented by hand: check-idempotency-parity 77/199 → 79/203;
SPEC/SECURITY/AGENTS 238 → 243 operations, 122 → 124 GETs, 47 → 48
PUTs, 23 → 24 DELETEs, 39 → 40 single-attempt POSTs, 188 → 192
retry-eligible ops at max 3.

New hand-written tests: typescript/tests/services/folders.test.ts (11),
python/tests/services/test_folders_service.py (11),
ruby/test/basecamp/services/folders_service_test.rb (14). Each covers the
list-has-no-projects / get-has-projects split and the always-present-null
decode, plus a 4xx per operation.

The API version tracks the pin date, so every SDK's API_VERSION moves to
2026-08-02.


Summary by cubic

Repins BC3 to 2c0dafba13 and adds the Folders API (/stacks) across all SDKs, introducing 5 operations and moving the API version to 2026-08-02. Operation count increases 238 → 243 with updated retry/idempotency metadata.

  • New Features

    • Added FoldersService with List/Get/Create/Update/Delete at /stacks.json and /stacks/{folderId}.json.
    • Two shapes: Folder (list; no projects) and FolderWithProjects (get/create/update; includes projects). Wire type stays "Stack".
    • project_ids is input-only on create; responses return bucket_ids and expanded projects. gauges_url, color, image_url are required-and-nullable.
    • Idempotency: UpdateFolder/DeleteFolder idempotent; CreateFolder non-idempotent with standard retry. Naming override GetFoldergetFolder in TS/Kotlin/Swift.
    • Services, models, and tests added in Go/Kotlin/Python/Ruby/Swift/TypeScript.
  • Bug Fixes

    • Folder writes now return FieldValidationError (field-keyed 422) instead of ValidationError for create/update. Verified with new Go tests that assert JSON422 decoding.
    • SPEC §2 prose corrected: per-op retry.max distribution counts the five Folder ops (max:3 bucket 195 → 200; totals now match 243 ops).
    • Swift conformance: parse project_ids via the throwing intArray helper to avoid silent truncation.

Written for commit 5fda7c2. Summary will update on new commits.

Review in cubic


Post-review correction to this description

Two statements above were written before review and are superseded by what shipped:

  • Both Folders writes declare FieldValidationError, not just UpdateFolder. bc3's stacks_controller.rb renders { errors: <ActiveModel::Errors> } on create (:27) as well as update (:51), via the RecordInvalid rescue — and ActiveModel::Errors serializes field-keyed, so the wire is {"errors":{"name":["can't be blank"]}} for both. The review only named UpdateFolder; CreateFolder had the identical defect. The TS/Python/Ruby tests already modelled the field-keyed body correctly — the spec was the wrong half. Verified by a Go test that pins the typed JSON422 field and is red-proofed by failing to compile against the previous generated client (*ValidationErrorResponseContent has no field or method Errors). Wrapper-level assertions here are vacuous, because checkResponse parses the raw body and never reads the typed field.
  • "One API-surface change" should read "one newly modeled API surface." The same section already acknowledges two API behavior fixes in the repin range (bc3 #12494 / #12501), so the original phrasing undercounts what the repin carries.

The repin and the absorption are one change on purpose. `doc/api/sections/
folders.md` does not exist at the old pin `d0edc1283b`, so the five `/stacks`
routes are absent from `spec/bc3-routes.json` today. Repinning alone would make
them appear in the route table with no operation modelling them, and the #586
parity gate would then demand a disposition for five routes we have every
reason to model. Absorbing alone would model a contract the pin says isn't
there. 238 -> 243 operations.

## The repin: d0edc1283b -> 2c0dafba13 (11 commits)

Exactly one touches API surface:

- `dc6cd10714` #12384, the Folders API — the absorption half, below.

Two more are behaviour fixes to endpoints already modelled, with the wire shape
unchanged, so they need the repin and nothing else: `344581a379` (#12494) and
`2c0dafba13` (#12501) stop a draft's subscribers being recomputed when an update
doesn't address them. They add prose to five `doc/api` sections but no route and
no payload field — proven by regenerating the route table, whose only delta is
the five `/stacks` routes.

`19956c5579` (#12488) deserves its own sentence because it *deletes* a
`request.format.json?` branch, which reads like an API change. It is not. The
removed branch returned `nil` for JSON when the recording's own assignment
wasn't prioritized; the replacement returns that same assignment unconditionally
and `&.unprioritize` is a no-op when there is no priority. Both spellings are
exact-target on the id in the URL and both answer `204`. What actually changed
is that the *web* path stopped cascading to a sibling step — it adopted the
contract the JSON API has had since bc3#12483 (absorbed in SDK #528).

The remaining seven are four dev-tooling (`112c1664bf`, `63a34abe30`,
`5f889d8365`, `faf5fe9dfc`), two Turbo-morph web-only (`1fad1ef608`,
`c664cdc9de`), and one push-notification backend swap (`845df62b6b`).

`spec/bc3-routes.json` is regenerated at the new pin (369 routes, 64 sections);
`make provenance-sync` keeps the go:embed copy in step. The API version tracks
the pin date, so every SDK's `API_VERSION` moves to 2026-08-02.

## The absorption: FoldersService, five operations

Folders group projects on one person's home screen. They are per-user, so the
collection is flat rather than bucket-scoped. **The wire type stays `Stack`** —
the product was renamed, the payload was not — and so does the URI segment.
The operations, structures and generated methods use `Folder`.

    ListFolders    GET    /{accountId}/stacks.json        200, bare array
    GetFolder      GET    /{accountId}/stacks/{folderId}  200
    CreateFolder   POST   /{accountId}/stacks.json        201
    UpdateFolder   PUT    /{accountId}/stacks/{folderId}  200
    DeleteFolder   DELETE /{accountId}/stacks/{folderId}  204

Two output structures, not one. `Folder` is what the list returns; only
`FolderWithProjects` carries the expanded `projects`. A single shape with an
optional member would make every generated list-item type declare a field the
list response never populates.

`gauges_url`, `color` and `image_url` are **required-and-nullable** (SPEC §"A
third wire state"): the jbuilder emits all three unconditionally and all three
are commonly `null`, so `@required` models the presence and `smithy-build.json`
`jsonAdd` layers `type: ["string","null"]` on top. Go types them `*string`
because the value is nullable, not because the key is optional. Same treatment
`Wormhole.color`/`destination_url` already get.

`project_ids` does not round-trip: the create input takes it, and the response
reports the same ids as `bucket_ids` *and* expands them as `projects`. Three
names, one relationship. An unreachable id fails the whole request with 404 and
writes nothing, so `CreateFolder` lists `NotFoundError`; `UpdateFolder` lists
`ValidationError` because a blank name is a real 422 (create defaults it to
"New folder" instead).

`UpdateFolder` and `DeleteFolder` are naturally idempotent; `CreateFolder` is
not, and gets the inert retry block every non-idempotent POST carries.

The `Folders` tag resolves to `FoldersService` through every generator's default
fallback — zero service-group overrides, and `ListFolders` infers the `folder`
resource type cleanly, so no override there either. One deliberate generator
addition: `GetFolder` -> `getFolder` in TS/Kotlin/Swift, because the bare rule
collapses it to `folder(id)`, a noun with no verb, while Ruby and Python already
emit `get_folder`. Same shape as the `GetBookmark` override; all six SDKs now
agree on listFolders/getFolder/createFolder/updateFolder/deleteFolder.

Go's `FolderWithProjects` repeats the base fields rather than embedding `Folder`.
Embedding promotes them correctly at runtime but hides them from
`check-wrapper-drift`, which walks declared fields and reported all twelve as
missing. Repeating them keeps the guard able to verify every field.

## Registry and coordination

`spec/api-gaps/folders-api.md` moves to `absorbed-in-sdk` with seven
`smithy_refs`. `spec/api-gaps/stack-doc-and-smithy.md` deliberately keeps its
`confirmed-not-api-resource` status: it records a decision that was correct when
made, and the supersession note in `folders-api.md` exists precisely to preserve
that history. `COORDINATION.md` named pin `338b7a11`, three repins stale; it now
names `2c0dafba13` and says the provenance file is the only authority.

## Verified

`make check` exit 0. Swift is not skipped on this host: `swift build`/`swift
test` ran 306 tests with 0 failures, and `swift run ConformanceRunner` ran the
conformance suite. Ruby 1092 runs / 0 failures. Conformance 147 passed, 1
skipped (a pre-existing Go-runner origin case), and the five new `paths.json`
cases PASS in **all six** runners — Go, Kotlin, TypeScript, Ruby, Python and
Swift each have a dispatch branch, so none of them silently skips.
`BC3_REPO_PATH=... make bc3-routes-check` exit 0. Counts updated deliberately:
`check-idempotency-parity` 77/199 -> 79/203, SPEC/SECURITY/AGENTS 238 -> 243
operations, 122 -> 124 GETs, 47 -> 48 PUTs, 23 -> 24 DELETEs, 39 -> 40
single-attempt POSTs, 188 -> 192 retry-eligible ops at max 3.
Copilot AI review requested due to automatic review settings August 3, 2026 06:21
@jeremy jeremy added the enhancement New feature or request label Aug 3, 2026
@github-actions github-actions Bot added typescript Pull requests that update TypeScript code ruby Pull requests that update the Ruby SDK go kotlin swift spec Changes to the Smithy spec or OpenAPI conformance Conformance test suite python Pull requests that update the Python SDK labels Aug 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7548dab83

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread spec/basecamp.smithy Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Human review recommended

It is a broad cross-cutting change (provenance repin plus a new service generated across all six SDKs, conformance, and hand-maintained spec tallies) that warrants human verification, and it already contains one stale op-count in SPEC.md that should be corrected.

Pull request overview

This PR repins bc3 provenance from d0edc1283b2c0dafba13 and, as one coupled change, absorbs the Folders API (/stacks routes, wire type Stack) that the repin brings into range. It adds a FoldersService (List/Get/Create/Update/Delete) across all six SDKs — starting from the Smithy spec, cascading through openapi.json and every generated service/model layer, plus conformance dispatch and per-SDK metadata. Operation count moves 238 → 243 and the API version to 2026-08-02.

Changes:

  • Adds ListFolders/GetFolder/CreateFolder/UpdateFolder/DeleteFolder with two shapes (Folder for lists, FolderWithProjects for singular ops); gauges_url/color/image_url are required-and-nullable, project_ids is input-only.
  • Repins provenance + regenerates bc3-routes.json, and updates the hand-maintained op-count tallies in SPEC.md/SECURITY.md/AGENTS.md, check-idempotency-parity (79/203), and API-version constants in all six SDKs.
  • Wires the new service into every client, adds hand-written TS/Ruby/Python unit tests and 5 conformance path cases dispatched by all six runners.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File summaries
File Description
spec/basecamp.smithy Adds 5 Folders operations + Folder/FolderWithProjects structures (wire type Stack)
spec/overlays/tags.smithy, spec/smithy-build.json Tags new ops to Folders; layers nullable types on 3 fields
openapi.json, typescript/src/generated/{schema.d.ts,openapi-stripped.json,path-mapping.ts,metadata.ts} Regenerated OpenAPI + TS artifacts
typescript/src/generated/services/folders.ts, index.ts, client.ts, scripts/generate-services.ts New TS service, wiring, naming/type-alias overrides
go/pkg/basecamp/folders.go, client.go, url-routes.json Hand-written Go service over generated client + route table
ruby/.../folders_service.rb, types.rb, client.rb, metadata.json Ruby service, types, wiring
python/.../services/folders.py, types.py, client.py, async_client.py, metadata.json Sync+async Python service, types, wiring
kotlin/.../services/folders.kt, models/Folder(WithProjects).kt, Types.kt, Metadata.kt, Config.kt, ServiceAccessors.kt Kotlin service, models, generator config
swift/.../FoldersService.swift, Models/*.swift, Metadata.swift, AccountClient+Services.swift, MethodNaming.swift Swift service, models, wiring
conformance/tests/paths.json + 6 runners 5 path cases + dispatch branches in all runners
SPEC.md, SECURITY.md, AGENTS.md, scripts/check-idempotency-parity Operation-count/retry/idempotency tally updates
spec/api-provenance.json, go/.../api-provenance.json, spec/bc3-routes.json, COORDINATION.md, spec/api-gaps/* Provenance repin, route table, registry status
*/version constants (go/ruby/python/kotlin/swift/ts) API_VERSION → 2026-08-02
tests: folders.test.ts, folders_service_test.rb, test_folders_service.py Hand-written unit tests
Review details
  • Files reviewed: 41/68 changed files
  • Comments generated: 1
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread SPEC.md Outdated
…nder

CreateFolder and UpdateFolder declared `ValidationError`, whose required wire
member is a singular `error` string. bc3 renders neither create nor update that
way. `stacks_controller.rb:51` answers a rejected rename with
`render json: { errors: @stack.errors }`, and `:27` answers a rejected create
with the same rendering from its `RecordInvalid` rescue. `ActiveModel::Errors`
serializes field-keyed, so the wire body is:

    {"errors":{"name":["can't be blank"]}}

`FieldValidationError` already exists for exactly this shape and is already
carried by `UpdateCalendar`, `UpdateMyNote` and `UpdateMyPreferences`. Both
folder writes now use it. No other operation is touched.

The failure this fixes is silent, which is why it earns a test. While the
operations declared `ValidationError`, Go's typed `JSON422` was a
`*ValidationErrorResponseContent`; decoding the field-keyed body into it
succeeds — `encoding/json` does not care that `error` is absent — and leaves
`JSON422.Error` as `""`. A caller reading the typed field got an empty string
and no error of any kind.

`TestFoldersGenerated_JSON422_DecodesFieldKeyedBody` goes at the generated
client directly and pins `JSON422.Errors` for both operations. It was shown to
fail before the fix: against the previous commit's `client.gen.go` it does not
compile, because `*ValidationErrorResponseContent` has no `Errors` field.

The two wrapper-level tests beside it (`TestFoldersService_{Update,Create}_
FieldKeyed422`) are deliberately not the proof. They were run against the
un-fixed generated client and **passed**, because `checkResponse` parses the raw
body itself and never reads the typed field. A wrapper-only assertion here would
have been vacuous.

The hand-written TS/Python/Ruby tests already modelled the field-keyed body, so
they needed no change — they were right about the server and the spec was the
wrong half.

make check: REAL_EXIT=0. Swift ran rather than skipped (306 tests, 0 failures;
`swift run ConformanceRunner` executed). Ruby 1092 runs, 0 failures. Conformance
147 passed / 1 skipped (pre-existing).
Copilot AI review requested due to automatic review settings August 3, 2026 06:40
Copilot caught this on #593. All five Folders operations carry `max: 3` in
behavior-model.json, so the `max:3` bucket is 200, not 195 — and 200 + 43 = 243
matches the new operation total. The neighbouring derived figures in the same
section (192 retry-eligible at max 3, 203 retry-eligible total) were updated
with the absorption, so leaving 195 made the section internally inconsistent.
Re-derived from behavior-model.json rather than incremented by hand. This prose
carries no CI gate.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Human review recommended

It repins bc3 provenance and spans generated code, spec, and count metadata across all six SDKs with a large blast radius, so it warrants final human verification despite only one minor optional nit being found.

Review details

Suppressed comments (1)

conformance/runner/swift/Sources/ConformanceRunner/Dispatch.swift:356

  • Minor consistency nit: this hand-rolls project_ids parsing with compactMap, which silently drops any element that isn't an integer or that overflows Int. That's the exact "one step quieter" silent-drop fault the helper docstring at the top of this file warns about (lines 19–24), and every other array/scalar fixture read here goes through the throwing helpers (intArray, longParam, optString, stringParam). intArray (defined at lines 77–90) already returns [Int]? and throws badParameter on a malformed element, so the request body assertion can't be pinned against a quietly truncated array. Consider using it here for consistency and fail-closed behavior. Optional.
                projectIds: rb?["project_ids"]?.arrayValue?.compactMap {
                    $0.intValue.flatMap { Int(exactly: $0) }
                }))
  • Files reviewed: 42/69 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 3, 2026 06:50
Copilot flagged this as a suppressed low-confidence comment on #593, and it is
right. The Folders dispatch hand-rolled project_ids parsing with compactMap,
which silently drops any element that is not an integer or that overflows Int —
the exact "one step quieter" fault Dispatch.swift's own helper docstring
(lines 19-24) exists to warn about. A quietly truncated array would still have
satisfied the requestBody assertion.

intArray (lines 77-90) already returns [Int]? and throws badParameter on a
malformed element, and every other array read in this file goes through it.
Mine was the only exception.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Human review recommended

It combines a provenance repin with a new API surface spanning generated code, hand-written wrappers, conformance runners, and count metadata across all six SDKs, which warrants final human verification even though no objective defects were found.

Review details
  • Files reviewed: 42/69 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 3, 2026 06:59
@jeremy

jeremy commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Three review findings addressed. Two were on threads (both replied to and resolved); the third was a suppressed comment with no thread, so recording it here.

1. Codex P2 — folder writes declared the wrong 422 shape (fde399462)

Verified against bc3 rather than taken on faith, and it was broader than reported: stacks_controller.rb renders { errors: <ActiveModel::Errors> } on both create (:27, the RecordInvalid rescue) and update (:51), so the wire body is field-keyed for both. CreateFolder and UpdateFolder now declare FieldValidationError.

The hand-written TS/Python/Ruby tests already modelled the field-keyed body — they were right about the server, the spec was the wrong half.

The failure was silent: decoding {"errors":{"name":[...]}} into the old *ValidationErrorResponseContent doesn't error, it just leaves JSON422.Error as "". TestFoldersGenerated_JSON422_DecodesFieldKeyedBody pins the typed field for both ops and was shown to fail before the fix (it doesn't compile against the previous client.gen.go). Notably the two wrapper-level tests beside it pass against the un-fixed client — checkResponse parses the raw body and never reads the typed field — so a wrapper-only assertion would have been vacuous.

2. Copilot — stale per-op retry.max distribution in SPEC §2 (312a8ec7d)

195200. All five Folders ops carry max: 3, and 200 + 43 = 243. I'd updated the neighbouring 192/203 figures but not this one, leaving the paragraph internally inconsistent.

3. Copilot (suppressed) — Swift conformance dropped malformed project_ids silently (5fda7c2f5)

Correct, and worth un-suppressing. The Folders dispatch hand-rolled project_ids with compactMap, which silently drops any element that isn't an integer or overflows Int — the exact fault Dispatch.swift's own helper docstring (lines 19–24) exists to warn about, and a quietly truncated array would still have satisfied the requestBody assertion. intArray (lines 77–90) already throws badParameter on a malformed element and every other array read in that file uses it; mine was the only exception. Now fixed.

Not fixed here, flagged for separate work

SPEC §5's account-scoped roster claims 46 services and lists 46, but the real count is 51 — it's missing bookmarks, calendars, drafts, myNotes from previously-merged work plus folders from this PR ((43 more services) on line 70 has the same root cause; true total is 51 + 1 client-level = 52, not 47). Four of the five predate this PR, so correcting it means restating counts for other lanes' merged work. Half-fixing it would be worse than leaving it — adding only folders and writing 47 is wrong in a new way. Raised with the coordinator to file separately.

make checkREAL_EXIT=0 on the final tree (5fda7c2f5): Swift ran rather than skipped (306 tests, 0 failures; swift run ConformanceRunner executed), Ruby 1092 runs / 0 failures, conformance 147 passed / 1 pre-existing skip. The five Folders conformance cases still PASS in all six runners.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jeremy
jeremy merged commit 9459083 into main Aug 3, 2026
46 of 47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conformance Conformance test suite enhancement New feature or request go kotlin python Pull requests that update the Python SDK ruby Pull requests that update the Ruby SDK spec Changes to the Smithy spec or OpenAPI swift typescript Pull requests that update TypeScript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants