DEV Community

kongkong
kongkong

Posted on

Bind the Rewrite to One Id, Not the Box

Bind the Rewrite to One Id, Not the Box

I can walk the late demo in my head, because the rewrite looks done and the room nods before anyone opens the database. A visitor clicks Rewrite bio, the spinner stops, and a polished paragraph appears in a toast that fades before anyone checks the row. The profile row is still the old sentence, the model log has no visitor id, and the access line only says 200. If that box disappeared before morning, which click would you replay, and which response would you trust?

I do not think the model failed, and I do not think the toast is the feature the visitor actually received. The first broken layer is the missing stamp that should have tied the click, the handler, the provider call, and the draft row together. Without that stamp, a free server is a stage with no script, and a free model is a voice with no cue sheet. Would you ship a payment form that could not name the exact charge it had just created for that visitor?

My position is blunt, and I am not interested in a softer version that leaves both doors politely open. Do not keep the rehearsal box as the source of truth, and do not let the model write the canonical profile first. Stamp one request id through the UI, the API, the provider seam, and the draft row, then prove a replay returns the stored draft. The free server is useful only while you are willing to wipe it and run the same entrypoint from the repository again.

The stamp has to cross the click

I put the stamp in the browser because the click is the only event a visitor can point at when the paragraph looks wrong. The handler inserts a pending draft with that id before it calls the model, so a crash still leaves a row you can find. The provider client must send the same id as metadata, or the model log becomes a novel you cannot join back to the click. Only after the draft is stored does the UI replace the toast with the saved text, not a hope the row never held.

The browser should mint the id once per click and show it beside the error, because a hidden header abandons the visitor. I keep that id on the button until the response is stored, so a double click reuses the stamp instead of minting another rewrite. If the fetch throws, the toast should name the status and the id, not a cheerful sentence that implies the profile changed. Can your current UI answer which click produced the paragraph, or can it only say that something pleasant happened?

async function rewriteBio(userId, currentBio, instruction, button) {
  const requestId = button.dataset.requestId || crypto.randomUUID();
  button.dataset.requestId = requestId;
  const response = await fetch("/bio/rewrites", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-request-id": requestId,
    },
    body: JSON.stringify({
      user_id: userId,
      current_bio: currentBio,
      instruction,
    }),
  });
  const body = await response.json();
  if (!response.ok) {
    throw new Error(`${response.status} ${body.error} ${requestId}`);
  }
  return { requestId, proposal: body.proposal, replayed: body.replayed };
}
Enter fullscreen mode Exit fullscreen mode

The slice below is a proposal you can run with the Python standard library, not a benchmark I measured on a shared host. I would rather fail with 409 when the same id arrives with a different body than silently start a second provider call. A 422 means the stamp or the body was missing, and a 502 means the seam failed after the pending row existed. A 200 means the draft row was stored, which is the only success I am willing to paint for the visitor who clicked.

#!/usr/bin/env python3
"""Proposed rehearsal server. Unmeasured example: swap fake_complete before any real provider."""
import hashlib
import json
import os
import sqlite3
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

DB = os.environ.get("DRAFT_DB", "drafts.sqlite")
PORT = int(os.environ.get("PORT", "8091"))

def connect():
    conn = sqlite3.connect(DB)
    conn.execute(
        """
        create table if not exists bio_drafts (
          request_id text primary key,
          user_id text not null,
          body_hash text not null,
          status text not null,
          proposal text,
          provider_calls integer not null default 0
        )
        """
    )
    return conn

def fake_complete(text, instruction, request_id):
    # Seam only. Free model access, if you use it, belongs here and nowhere else.
    return f"{text} ({instruction}) [{request_id[-4:]}]"

class Handler(BaseHTTPRequestHandler):
    def _send(self, code, payload):
        raw = json.dumps(payload).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(raw)))
        self.end_headers()
        self.wfile.write(raw)

    def do_POST(self):
        if self.path != "/bio/rewrites":
            return self._send(404, {"error": "not_found"})
        request_id = self.headers.get("X-Request-Id", "")
        if len(request_id) < 8:
            return self._send(422, {"error": "missing_request_id"})
        length = int(self.headers.get("Content-Length", "0"))
        try:
            payload = json.loads(self.rfile.read(length) or b"{}")
            user_id = payload["user_id"]
            current = payload["current_bio"]
            instruction = payload["instruction"]
        except (KeyError, json.JSONDecodeError, TypeError):
            return self._send(422, {"error": "bad_body", "request_id": request_id})
        digest = hashlib.sha256(
            f"{user_id}|{current}|{instruction}".encode()
        ).hexdigest()
        conn = connect()
        row = conn.execute(
            "select body_hash, status, proposal, provider_calls from bio_drafts where request_id = ?",
            (request_id,),
        ).fetchone()
        if row and row[0] != digest:
            conn.close()
            return self._send(409, {"error": "id_reused", "request_id": request_id})
        if row and row[1] == "stored":
            conn.close()
            return self._send(200, {
                "request_id": request_id,
                "status": "stored",
                "proposal": row[2],
                "provider_calls": row[3],
                "replayed": True,
            })
        if not row:
            conn.execute(
                "insert into bio_drafts(request_id, user_id, body_hash, status) values (?, ?, ?, 'pending')",
                (request_id, user_id, digest),
            )
            conn.commit()
        proposal = fake_complete(current, instruction, request_id)
        if not proposal.strip():
            conn.execute(
                "update bio_drafts set status = 'failed' where request_id = ?",
                (request_id,),
            )
            conn.commit()
            conn.close()
            return self._send(502, {"error": "empty_proposal", "request_id": request_id})
        conn.execute(
            """
            update bio_drafts
               set status = 'stored', proposal = ?, provider_calls = provider_calls + 1
             where request_id = ?
            """,
            (proposal, request_id),
        )
        conn.commit()
        calls = conn.execute(
            "select provider_calls from bio_drafts where request_id = ?",
            (request_id,),
        ).fetchone()[0]
        conn.close()
        self._send(200, {
            "request_id": request_id,
            "status": "stored",
            "proposal": proposal,
            "provider_calls": calls,
            "replayed": False,
        })

if __name__ == "__main__":
    ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
Enter fullscreen mode Exit fullscreen mode

Replay it, then throw the room away

I would start it with a sqlite file beside the script, because a memory dict vanishes when the free box recycles the process. The first curl should return provider_calls set to 1 and replayed set to false, which tells you the seam actually ran. The second curl, with the same id and the same body, should return replayed true and leave provider_calls at 1. If the second call increments the counter, that is not a replay, it is a second rewrite in the first id's coat.

DRAFT_DB=drafts.sqlite python3 rewrite_stamp.py
# second shell
curl -sS -D - http://127.0.0.1:8091/bio/rewrites \
  -H 'content-type: application/json' \
  -H 'X-Request-Id: req-1001' \
  -d '{"user_id":"u1","current_bio":"I build APIs.","instruction":"shorter"}'
curl -sS http://127.0.0.1:8091/bio/rewrites \
  -H 'content-type: application/json' \
  -H 'X-Request-Id: req-1001' \
  -d '{"user_id":"u1","current_bio":"I build APIs.","instruction":"shorter"}'
curl -sS -D - http://127.0.0.1:8091/bio/rewrites \
  -H 'content-type: application/json' \
  -H 'X-Request-Id: req-1001' \
  -d '{"user_id":"u1","current_bio":"I build APIs.","instruction":"longer"}'
Enter fullscreen mode Exit fullscreen mode

I would kill the process, start it again with the same DRAFT_DB, and send the same curl before I trusted any host. The row should still be stored, the proposal text should match, and the provider counter should refuse to climb. Change only the instruction, keep the id, and you should get 409, because an id is a key, not a suggestion. That failure is the one I want in the pull request, since a silent overwrite is how a demo eats a visitor's real bio.

There is a hole I will not wallpaper over, because the pending window can still double-call if you crash after insert and before store. A retry in that window has a row, no proposal, and a temptation to call the provider again under the same id. The honest fix is to make the provider seam idempotent on that id, or to store a reservation token the seam can recognize. Until you have that, the script proves the happy replay, not the crash replay, and pretending otherwise is how vibe-coded features rot.

Think of the free server as a rented rehearsal room, not as the orchestra's archive of scores. You can play the piece there, invite a colleague to hear that same request id, and lock the door when the slot ends. You cannot leave the only copy of the score on the piano and call that a release. The repository, the sqlite contract or its real successor, and the curl that expects 200 are the score.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. Free model access belongs behind fake_complete, as a seam you can replace without teaching the route a vendor SDK. The free server option is only a place to run this same entrypoint, replay the curl, and wipe the box on purpose. I will not freeze a token quota, a model name, or a hardware shape here, because those settings move.

Read the current project page before you budget a rehearsal around them, and treat any older number you remember as untrusted. A free allowance is a budget you can exhaust, not a reason to skip the draft row or the replay check. If the project later changes what free includes, the stamp and the database contract should still boot from the repository. That is the tradeoff I care about: cost can change overnight, while an untraceable rewrite stays broken forever.

Who should walk away

This slice leaves auth out so the replay is easy to read, and that is not a suggestion to ship an open route. The session should own user_id, or a caller who guesses a stamp can store a draft against someone else's profile. I would reject a mismatch with 403 before the pending insert, so a bad caller never creates a row you must later explain. Permissions are part of the feature, not a coat you add after the free server has already impressed the room.

This approach is the wrong gate if the rewrite must become legal, billing, or medical copy without a human apply step after the draft. Skip it if your canonical store cannot upsert on request_id, because a free server will only hide that gap until the first restart. Skip it if a proxy strips X-Request-Id, since a stamp that dies at the edge never reaches the draft row you planned to query. If you need a multi-region write path this week, do not pretend one sqlite file on a free box is that path.

The stamp proves you can find the proposal, not that the proposal is good, kind, or safe to publish on a profile. A provider that drops metadata will still return text, and your join will fail even though the toast looks perfect. I have not measured latency, cost, or concurrency here, and I will not invent those numbers to make the slice sound finished. Swap in a real provider only after the replay stays at one call, or you will debug taste instead of identity.

Before I trust the slice, I want those checks in the pull request, as commands a teammate can paste without a tour. A missing id should return 422, and a changed body with the same id should return 409 rather than a fresh paragraph. A repeat should return 200 with provider_calls still at 1, including after you kill the process and start it on the same database file. If any check needs a file that exists only on the rehearsal box, the check is theater.

Which handoff is least stable in your stack: the browser stamp, the handler insert, the provider metadata, or the draft read after restart? Send the failure status you actually get, not the status you hoped the toast was hiding. I will take a 409 and a request id over a paragraph that no log can name.

Top comments (1)

Collapse
 
alexshev profile image
Alex Shev •

Binding the UI click, draft row, provider call, and replay to the same request ID makes the failure mode inspectable instead of merely visible. Reusing that ID for a duplicate click is also a useful idempotency boundary; pairing it with a server-side unique constraint or idempotency record would keep the protection intact if the UI state is lost or two tabs submit at once.