# Quality Versioning Playbook: App, Assets, Docs, and Git
Generic companion to `quality_software.md`. It covers how DevPlace
versions its application, its static assets, and its git history,
plus the one practice DevPlace does NOT have today and you should
adopt from day one: versioned markdown docs.
Honest baseline (researched 2026-10-11, do not assume otherwise):
- App version: single source `pyproject.toml`, auto-bumped patch on
every commit by a checked-in pre-commit hook. Real and working.
- Asset version: composed `{APP_VERSION}-{BOOT_ID}`, stamped into
every static URL, served immutable. Real and working.
- Git: trunk `master`, remote `production` branch, per-ticket
branches, CI gate on push/PR to master. Real and working.
- Tags: none. Changelog file: none. Markdown versioning: none.
These are gaps, and sections 4 and 6 below define what to do
instead of copying the gap.
## 1. App versioning: one source, auto-bumped
- One file owns the version (DevPlace: `pyproject.toml`
`project.version`, currently `1.0.x`). The running app reads it
live at boot (DevPlace: `config.APP_VERSION` via `tomllib`), so
the version in code and the version on disk can never drift.
- A checked-in pre-commit hook bumps the patch number on every
commit (DevPlace: `.githooks/pre-commit`, activated by
`git config core.hooksPath .githooks` in the install target).
Skips merge commits and commits that already touch the version
line, so a manual minor/major bump is never double-bumped.
- Semver discipline: patch is automatic (every commit), minor is a
manual commit when a user-visible feature lands, major is a
manual commit on breaking change. Minor/major bumps are ordinary
commits whose message says what the release contains.
- The version is visible at runtime: stamped into a meta tag
(DevPlace: `` in `base.html`) so any
page view, bug report, or screenshot names the exact build.
Hosting example: the control panel footer and every support ticket
show `v2.14.3`; support never asks "which version are you on".
## 2. Asset versioning: cache forever, deploy instantly
- Compose the asset version from app version plus boot id:
`STATIC_VERSION = {APP_VERSION}-{BOOT_ID}` where the boot id is a
deploy-provided marker with a wall-clock fallback.
- Stamp it into every app-owned static URL
(`/static/v/...`) through one URL helper, and serve that
path immutable with a one-year `max-age`.
- Keep the reverse proxy in sync: the versioned-path location regex
must accept dots and hyphens, not digits only, or the immutable
header silently stops applying (this was a real production bug
class here, fixed once the version segment stopped being a bare
timestamp).
- Never hardcode a static path; every template and script builds
URLs through the one helper, so a version-shape change touches
one function.
## 3. Git workflow: trunk, tickets, production
- Trunk is `master`. All work lands there through small commits;
CI (serial full suite, coverage, failure screenshots) runs on
every push and PR to master and must be green before merge.
- One branch per ticket or task, named after the tracker
(`ticket-123`, `112-short-slug`). Short-lived: branch, commit,
merge, delete. No long-running feature branches.
- One `production` branch (or tag set, see section 6) marks what is
deployed. Development happens on master; promotion to production
is a deliberate merge, followed by deploy and live verification.
- Commit messages: imperative summary line under 72 chars, blank
line, body explaining why (not what, the diff shows what).
Optional trailers for attribution (`Co-Authored-By`) and session
links. Reverts say what behavior returned and why.
- Never rewrite published history: no amend/push-force, no rebase
of shared branches, no `reset --hard` on anything pushed. Fix the
working tree forward; a mistaken commit is corrected by a new
commit.
- Hooks live in the repo (`.githooks/`) and are wired by the
install target, so every clone gets the version bump and any
future checks with zero manual setup.
## 4. Markdown versioning: every doc carries its version
This is the rule DevPlace does not follow yet: **every markdown
file is versioned on every update.** Adopt it from the start; it
costs one line per edit and buys traceability for every claim a
doc makes.
Scheme (copy literally):
1. First line of the file is an invisible HTML comment:
``
It renders nothing and survives every markdown renderer.
2. Bottom of the file is a `Doc history` table with one row per
version: version, date, one-line change note.
3. Doc-semver: MAJOR for restructure or section renumbering, MINOR
for a new section or new rule, PATCH for wording, fixes, or
example updates. Every update bumps exactly one level and adds
exactly one history row.
4. The version bump and the content change ship in the same commit,
so `git log -p ` and the history table always agree.
5. Cross-doc references name the section, not the version ("see
`quality_software.md` section 9"), so minor doc bumps never
cascade into other files.
Enforcement options, cheapest first: a pre-commit check that fails
when a staged `.md` lacks a matching version-line change (same
pattern as the app-version hook, inverted: docs must be touched by
hand, so the hook only verifies); a CI grep that every tracked
`.md` starts with the `doc:` comment; a unit test listing tracked
markdown files and asserting the header parses.
Hosting example: `docs/sla.md` at `3.1.0` and `docs/pricing.md` at
`5.0.2` evolve independently; a customer dispute cites "SLA v3.1",
not "the docs, at some point".
## 5. What gets a version vs what gets a timestamp
- Versioned (semver, comparable, citable): the app, each markdown
doc, API contracts, terms/SLA/pricing pages, database export
formats, agent tool catalogs.
- Timestamped (point-in-time, never compared): deploys (boot id),
backups, audit events, usage ledger rows, generated reports.
- Never versioned alone: a bare counter with no history row, a date
with no change note. A version you cannot diff is decoration.
## 6. Releases and tags: close the gap deliberately
DevPlace has no tags and no changelog file; promotion is tracked by
the `production` branch. For a hosting company, where customers and
auditors ask "what changed and when", close this gap:
- Tag every production promotion with an annotated tag
(`v2.14.0`, message = release notes summary). Tags are cheap,
immutable pointers; the branch can move, the tag cannot.
- Keep one `CHANGELOG.md` (itself doc-versioned per section 4)
with one entry per minor/major release: date, tag, added,
changed, fixed, security notes. Patch releases batch into the
next minor entry; no per-commit changelog spam.
- Release flow in order: land feature commits on master (CI green),
manual minor/major bump commit, merge master into production,
tag, deploy, verify live, write the changelog entry. A release
that is not verified live is not released.
## 7. Replication checklist for your hosting site
1. Single version source read live by the app; pre-commit hook
auto-bumps patch; minor/major are manual release commits.
2. Asset URLs carry `{version}-{boot-id}`, served immutable;
proxy regex accepts the full version alphabet.
3. Trunk `master`, ticket branches, `production` promotion target,
CI gate on master, no history rewrites, hooks in-repo.
4. Every `.md` starts with the `doc:` version comment and ends with
a history table; every update bumps; CI verifies the header.
5. Terms, SLA, pricing, and API contracts are versioned docs with
citable versions, not wiki pages.
6. Annotated tags plus one changelog for customer-facing releases;
release = merge, tag, deploy, verify live, changelog entry.
## Doc history
| Version | Date | Change |
|---------|------|--------|
| 1.0.0 | 2026-10-11 | Created from repo research: app auto-bump hook, asset composition, trunk/ticket/production git model, new markdown-versioning scheme. |