← Back to Lab

A draft needs more than a version number

A local website-control prototype exposed a small but important distinction: the current site version, the exact draft a person approved and a retried command each need their own identity. A later end-to-end rehearsal added another boundary: local installation, receipt and rollback evidence still does not prove a remote integration.

What we tried→What happened→What we learned→Decision

The problem

The prototype turned a narrow text request into a draft and required a separate publish action. A version check could show that the underlying website had not changed, but it could not show that the draft on screen was still the same draft. Two drafts can share one base version when neither has been published. A repeated network request created a second question: should the same publish command apply twice, or should a reused command key be allowed to mean something different? We corrected that control boundary before treating the local workflow as a useful proof. The next question was whether the same controls survived a complete local route: package installation, protected HTTP access, one durable receipt, exact read-back and a rollback that removed only the test fixture.

01

What we tried

  • Each new draft received a random draft identifier and recorded the current site version as its base version. Publishing had to present both values, so approval referred to one exact preview against one exact site state.
  • Each mutation could carry an idempotency key. The server derived a fingerprint from the endpoint and canonical request content, stored the key with the mutation and returned a duplicate result when the same command was retried.
  • The automated scenario created a valid draft, rejected a malformed publish request that omitted the draft identifier and supplied the wrong version, published the approved draft once and immediately retried the identical command. The inspected implementation contains a separate current-version guard, but this combined rejection did not isolate that guard. The scenario then created two unpublished news drafts at the same site version and attempted to publish the older identifier.
  • We kept persistence, undo and deletion in the same scenario to check that the identity controls did not disappear after a server restart or allow a read-only request to mutate the local state.
  • In a disposable loopback fixture, we installed five exact static source files plus a local form and receiver contract. Activation used an atomic rename, and all seven installed files were read back against their manifest hashes before the HTTP route was exercised.
  • We exercised anonymous and protected access, a first submission, an exact duplicate retry and a changed command reusing the same key. The receiver implemented only the local receipt route; it contained no mail, SMTP, webhook or external API delivery adapter.
  • We then rolled back the fixture and separately checked that a sentinel file and an unrelated local application remained present. No remote target, customer account or real delivery channel was used.

02

What happened

  • The first valid publish changed the fictional opening-hours field and advanced the local version from zero to one. Retrying the same command returned a duplicate result and left the version at one.
  • After a newer draft replaced an older one at the same base version, publishing the older draft identifier returned HTTP 409. The news count and version stayed unchanged until the current draft was published.
  • The published fictional news item remained present after a fresh server process started with the same temporary store. Undo restored the preceding content state, and the remaining delete and reset checks passed inside the same bounded scenario.
  • The recorded Node test run reported one scenario passed and zero failures. Separate local browser review covered the website and control interface, but it did not turn the prototype into a production service.
  • The later local route returned HTTP 401 for anonymous access, 200 for the protected root and form, 201 for the first submission, 200 for the exact duplicate, 409 for the conflicting reuse and 404 after rollback.
  • The receiver produced exactly one local receipt and read it back. The duplicate reused that result, and the receipt recorded no personal data. Zero-valued mail, SMTP, webhook and external API fields were fixture bookkeeping; they were not an independent observation of network traffic.
  • Rollback removed the disposable fixture while preserving the sentinel and unrelated application. The combined package validation reported 29 of 29 local tests passed, including five of five checks for this rehearsal.

03

What we learned

A site version identifies the state being edited. It does not identify the preview a person saw and approved.
A draft identifier and an idempotency key solve different problems. The draft identifier binds approval to content; the idempotency key makes an identical transport retry safe.
A key is only meaningful when it is bound to the command it represents. Reusing one key for different canonical request content must be treated as a conflict, not as permission to guess.
A passing local scenario is evidence for that scenario. It does not establish production authentication, tenant isolation, distributed locking, crash consistency or safe behaviour across several server instances.
An install is not proven by copied files alone. Exact read-back, the expected access boundary, observable result identity and a target-scoped rollback each answer a different failure question.
A duplicate-safe local receipt is stronger than a successful form response, because it shows what was durably recorded and can be paired with inspection showing that this receiver has no delivery adapter. It still says nothing about independently observed network traffic, remote authentication, hosting behaviour or actual email delivery.
Recovery evidence must include what survived. Removing the fixture while retaining a sentinel and another application is bounded evidence that this rollback did not simply erase the whole test root.

The decision

ADOPT. Keep draft ID plus base version as the approval identity, bind each mutation key to the canonical command and retain exact install, receipt, access-boundary and scoped-rollback checks as local regression evidence. Do not call the route integrated or production-ready until its protected remote target, backup state and complete isolated staging sequence have been verified separately.

What this means next

Preserve the stale-draft, duplicate-retry, conflicting-key, exact-read-back and scoped-rollback cases. If suitable external access becomes available later, verify the protected target and recovery state first, then repeat this exact sequence in one isolated staging subdirectory. Test concurrency and interruption between mutation and durable action recording as separate failure cases.