# Versioned Publishing

> Design or change content, templates, forms, quizzes, pricing rules or configuration that is authored, published, and then consumed by sessions already in progress. Use when editing a published item changes what someone mid-way through sees, when "why did this record's score change?" has no answer, or when retiring an item breaks historical records.

- Skill: `chinthakat/versioned-publishing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add chinthakat/versioned-publishing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/chinthakat/versioned-publishing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: chinthakat (https://skillmd.com/u/chinthakat)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/chinthakat/versioned-publishing

---


# Versioned publishing

The moment an authored thing is used by someone else's in-progress work, editing
it in place stops being an edit and becomes a rewrite of history.

The symptom is always the same and always reported late: a completed record no
longer matches what the person actually saw. Scores move. A field they filled in
has disappeared. A report from March renders with April's wording.

## 1. Publishing creates a version; published versions are immutable

Authoring edits a **draft**. Publishing snapshots the draft into a new
numbered, immutable version. Nothing ever writes to a published version again —
not a typo fix, not a reorder, not "just the label".

```
template v1 (published)  <- submissions started before Tuesday reference this
template v2 (published)  <- submissions started after reference this
template    (draft)      <- being edited now
```

The typo fix is v3. This feels heavy for a one-character change and it is the
only thing that keeps old records readable.

## 2. In-flight sessions keep the version they started on

A record captures its version id at creation and holds it to completion. It does
not follow the latest.

The alternative — everything reads current — produces a form that gains a
required question halfway through, which the user cannot answer because they
have left the building.

Decide and document what happens to a long-lived draft when the template moves
on. The defensible answers are "it finishes on its version" or "it is offered an
upgrade explicitly". The one to avoid is "it silently migrates".

## 3. Reading a record means reading its version

Every consumer — the detail view, the export, the PDF, the score recomputation,
the analytics job — resolves through the version stored on the record.

This is where in-place editing usually leaks back in: the form renders correctly
from the stored version, and then the report joins to the current template for
the section titles. Grep for every place the template is loaded and check what
picks the version.

## 4. Archive; do not delete

Retiring something removes it from the pickers and from anything new. It does
not remove the row, because completed records still reference it.

- A status or lifecycle field, or a move to an archive store — either works,
  as long as the row remains resolvable by id.
- Every list that offers items to users filters on that status. Every list that
  renders historical records does not.
- **Audits must know about the archive.** A retired item looks identical to a
  missing one, and to a broken one. Findings about archived entities are almost
  always noise, and checking the status first is what stops an audit reporting
  defects in things nobody serves.

## 5. Ids are stable; everything else can change

An item's id is its identity across every version, both directions. Titles get
rewritten, options get reordered, sections get moved — all fine, all traceable,
as long as the id does not move.

Regenerating ids on publish, or deriving them from the title or the position,
breaks every historical reference at once and is close to unrecoverable. If ids
are currently positional, fixing that is more urgent than whatever you came here
to do.

## 6. The copy-then-customise pattern

A shared library of items plus per-tenant copies is a good shape, and it has one
sharp edge: **what happens to the copy when the original changes?**

Decide explicitly:

- **Fork on copy** — the tenant's copy is independent. Simple, predictable,
  and the library's improvements never reach anyone who already copied.
- **Track with opt-in** — the copy records its origin and version, and the
  tenant is shown "the library version has been updated" with a diff and a
  button.

Either is defensible. What is not defensible is pushing library edits into
tenant copies silently, which rewrites someone else's document without asking.

## 7. The version diff is the changelog

If a version is a snapshot, the diff between consecutive versions is a free,
accurate record of what changed. Generate it rather than asking people to
maintain release notes by hand — hand-maintained notes drift, and the diff
cannot.

Check it into the repo where a snapshot is exportable. It becomes the answer to
"when did this question change, and who changed it?", which is otherwise
archaeology.

## Checklist

- [ ] Publishing snapshots an immutable numbered version
- [ ] In-progress records store and keep their version id
- [ ] Every reader resolves through the record's version, including exports and reports
- [ ] Retiring archives rather than deletes; historical reads still resolve
- [ ] Audits check archived status before reporting a defect
- [ ] Ids stable across versions and never derived from title or position
- [ ] Library-to-copy propagation rule chosen and documented
- [ ] Version diff generated, not hand-written

