Skip to content

fix(api): PATCH /api/posts/:id — update preserves surface kind#155

Merged
benvinegar merged 1 commit into
mainfrom
elucid/update-preserves-surface-kind
Jun 26, 2026
Merged

fix(api): PATCH /api/posts/:id — update preserves surface kind#155
benvinegar merged 1 commit into
mainfrom
elucid/update-preserves-surface-kind

Conversation

@elucid

@elucid elucid commented Jun 26, 2026

Copy link
Copy Markdown
Member

Problem

sideshow update <id> <file> always treated the file as HTML, regardless of the original surface's kind. Updating a markdown post turned it into an html post; updating a code post lost the language; updating a diff lost its layout — every non-html surface kind was clobbered.

Wherein I argue this is good enough for now

This isn't a full solution to this problem. The API that sideshow update was originally using allows you to do a fine grained update of all the parts/surfaces of a post. However, there's no concept of managing individual surfaces elsewhere in the cli api surface (except implicitly via sideshow publish if you give multiple --<format> args.

This is a stand-in fix before proposing something more complicated (e.g. offering the ability to update --part N of a post, but to support this there should probably also be sideshow show :postId, have this list part indices, etc.

I think the current PR is a pragmatic solution because auditing sessions of agents using sideshow, I see that they often just do sideshow update :postId /path/to/something.md. So this is the expectation from the cli help documentation and I think it's reasonable considering most posts are single surface anyway. After making the update, the agent will notice that it accidentally set the kind to html, then they try stuff like sideshow update :postId --md /path/to/something.md, probably imitating how sideshow publish kind flags work, and then eventually resort to making a raw PUT to the API.

Again, I want to make a deeper fix for this but IMO this would solve most of the problem right away.

Solution

A new PATCH /api/posts/:id endpoint that accepts raw content and slots it into the existing surface's kind, preserving all extra fields.

NOTE: I didn't want to modify the existing update API and make it "clever" by detecting this special case because I think it's useful to be able to do more complicated fine grained updates. Again, I think there should be a better cli ui for doing those updates.

Server: PATCH /api/posts/:id

Accepts { content, title, kits } — all optional, at least one of content/title required. When content is provided:

  1. Fetches the existing post
  2. Reads the first surface's kind and maps content into the correct field (markdown.markdown, code.code, diff.patch, terminal.text, etc.)
  3. Preserves all kind-specific extra fields (language, cols, layout, etc.)
  4. For json surfaces, parses the content string into a value
  5. For html surfaces, optionally updates kits
  6. Delegates to the existing reviseSurface flow (validation, history, SSE broadcast, feedback collection)

Errors cleanly on:

  • Multi-surface posts → 400 (until we add --surface N targeting)
  • Non-text surface kinds like image/trace → 400
  • Invalid JSON content for a json surface → 400

The existing PUT /api/posts/:id full-replacement API is unchanged.

CLI

sideshow update now uses PATCH with { content, title, kits } instead of constructing { kind: "html", html: content } and PUTting. No flag changes — it just works.

Tests

18 new API tests covering:

  • Every text-content surface kind (html, markdown, code, diff, terminal, mermaid, json)
  • Extra field preservation (language, cols, layout)
  • Title-only updates, title + content updates
  • Error cases (multi-surface, unsupported kind, invalid JSON, empty body, 404)
  • Kits update on html surfaces
  • userFeedback piggybacking
  • Version bumps and history preservation

Validation

npm test (282/282) · typecheck (3 programs) · lint · format:check — all green.

sideshow update always treated content as HTML, clobbering the original
surface kind (markdown → html, code → html, etc.). Add a PATCH endpoint
that accepts raw content and slots it into the existing surface's kind,
preserving extra fields (language, cols, layout, etc.).

- PATCH /api/posts/:id: accepts { content, title, kits }, reads the
  existing post's first surface kind, maps content into the correct
  field, validates, and delegates to reviseSurface. Errors on
  multi-surface posts (400) and non-text kinds like image/trace (400).
  JSON surfaces parse the content string. HTML surfaces accept kits.
- CLI update command: switches from PUT with hardcoded {kind:'html'} to
  PATCH with {content, title, kits}.
- 18 new API tests covering every surface kind, field preservation,
  error cases, title-only updates, version history, and feedback.

Closes the update-kind-bug where `sideshow update <id> file.md` on a
markdown post would render raw markdown as HTML.
@elucid elucid requested a review from benvinegar June 26, 2026 03:11
@benvinegar

Copy link
Copy Markdown
Member

However, there's no concept of managing individual surfaces elsewhere in the cli api surface (except implicitly via sideshow publish if you give multiple -- args.

I think I want the proper solution or we're just moving stuff around and forgetting why it's there.

@benvinegar benvinegar merged commit 3672369 into main Jun 26, 2026
9 checks passed
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.

2 participants