# 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. |