Skip to content

fix(prompts): match the wire shapes the prompt version routes use - #355

Merged
garfiec merged 1 commit into
developfrom
fix/prompt-request-shapes
Aug 13, 2026
Merged

fix(prompts): match the wire shapes the prompt version routes use#355
garfiec merged 1 commit into
developfrom
fix/prompt-request-shapes

Conversation

@garfiec

@garfiec garfiec commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Summary

Editing a prompt's body never worked against a real server, and promoting a version
reported failure when it had succeeded. Both are wire-shape mismatches on the two
prompt-version routes. Neither was visible from inside the app: the client compiled,
and the Result.Error each produced was handled exactly as designed.

Closes #317. The picker's remaining stale case is an edit, and it needed both fixes
here: the edited body had to reach the server at all, and the promotion that makes it
live had to report success so the repository's revision bump ran.

Changes

core:model / core:network

  • POST /api/prompts/groups/{id}/prompts nests the version under prompt in both
    directions. The request was sent flat, so the route read the text where it expected
    an object and answered HTTP 400 to every body edit. AddPromptToGroupRequest now
    nests, reusing CreatePromptData.
  • The same route answers { prompt } rather than a bare prompt; AddPromptToGroupResponse
    unwraps it. Its field is non-nullable deliberately — savePrompt answers { message }
    with HTTP 200 when the write fails, so that body fails to decode and reports the failure
    rather than a success carrying nothing the caller then promotes on.
  • PATCH /api/prompts/{id}/tags/production answers { message }, not the prompt.
    Decoding it as a prompt threw, turning an accepted promotion into a Result.Error.
  • That route promotes req.params.promptId and never reads a body, so the client no
    longer sends one and UpdatePromptTagRequest is removed. It carried a
    productionPromptId that was silently ignored while reading as though it chose which
    version went live.

core:data

  • updatePromptProductionTag drops its request parameter and returns Result<Unit>.
    The decode failure happened before the revision bump, so an accepted promotion never
    announced itself and the composer's / picker went on offering the superseded body
    until the app restarted.

feature:chat

  • A body edit now promotes the version it writes. Adding a version leaves the group's
    productionId on the old one, so without the promotion the edit is stored where nothing
    reads it — the library row, the / picker and the editor all show the production body.
    publishNewVersion reports the save only when both calls land, using the id from the
    add's response. Web promotes on save the same way (alwaysMakeProd, default on).

Testing

  • New PromptsApiWireShapeTest pins both routes against live response captures from a
    v0.8.7 server: the nested request, the unwrapped response, the HTTP-200 failure body,
    and the promotion's empty request. Nothing above :core:network can see a JSON key,
    which is why these survived.
  • PromptEditorSaveOutcomeTest gains cases for the promotion — that it happens, that a
    failed promotion is not reported as saved, that an id-less response is not reported as
    saved, and that an unchanged body writes no version.
  • Each new behavioural guard was mutation-checked: reverting the response unwrap, the
    production-tag decode, and the promotion each fails exactly the intended tests.
  • :core:network, :core:data and :feature:chat unit suites, :app:assembleDebug, and
    detekt commonMain on all four modules pass.
  • Device-tested on a Pixel 10 Pro Fold emulator against a local v0.8.7 server: a body edit
    saves and reaches the library, the / picker and insertion with no restart; "Set
    Production" on an older version closes the sheet, moves the tag, and the picker follows
    with no restart.

Editing a prompt's body was impossible against any server, and promoting a
version reported failure on success.

`POST /api/prompts/groups/{id}/prompts` nests the version under `prompt` in
both directions. The client sent it flat, so the route read the text where it
expected an object and answered 400 on every body edit; its response is
`{ prompt }` rather than a bare prompt, which would have failed to decode next.
`savePrompt` answers `{ message }` with HTTP 200 when the write fails, so the
wrapper's field is non-nullable and that body reports the failure it is.

`PATCH /api/prompts/{id}/tags/production` promotes `req.params.promptId` and
never reads a body, and answers `{ message }`, not the prompt. Decoding it as a
prompt threw, which turned an accepted promotion into a `Result.Error` and with
it skipped the repository's revision bump — so the composer's `/` picker went on
offering the superseded body until the app restarted. It now sends no body, like
the web client: a body naming a different prompt was silently ignored while
reading as though it chose which version went live.

Adding a version also does not make it live. A body edit now promotes the
version it just wrote and reports the save on both calls, so the edit reaches
the library row, the picker and the editor rather than being stored where
nothing reads it. Web promotes on save the same way (`alwaysMakeProd`).

`PromptsApiWireShapeTest` pins live captures of both bodies, since nothing above
`:core:network` can see a JSON key.
@github-actions

Copy link
Copy Markdown
Contributor

Android debug APK

Artifact: switchboard-android-debug-355
Download: switchboard-android-debug-355.zip
Retention: 90 days
Commit: b3aec5b92da08190c69d8bf7eb66f45360de8afb

Download requires a GitHub login. Installs over previous debug builds without uninstalling (stable signing key).

@garfiec
garfiec merged commit 998b4a8 into develop Aug 13, 2026
6 checks passed
@garfiec
garfiec deleted the fix/prompt-request-shapes branch August 13, 2026 07:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Composer's / prompt picker never sees newly created or edited prompts

1 participant