"A repo has a dozen version numbers. Exactly one of them is the product, and exactly one thing may change it."
Which Version, Exactly?
Say a repo is 'version 1.2.0.' Which one? The npm package version? The native bundle build number? The database schema version? The API/protocol version? The model version? The deployment number? The Git tag? A mature project has many of these, and they legitimately move independently. Firelink names one of them the product version — the human-facing release — and gives it a single home: a small file, cwk-product.json, holding a product id and a normalized SemVer string (no leading v; prerelease and build parts allowed when they carry real meaning).
The values are meaningful by convention: 0.0.0 is a scaffold shell, 0.y.z is an unstable product contract, and 1.0.0 declares the first stable production contract. Display surfaces add the v back for readers; the stored value stays clean.
One Owner, One Editor, One Field
This is one-owner-per-fact made concrete, and it goes one step further: not just one owner, but one editor. Firelink reads the manifest before any legacy heuristic source, and heuristics remain read-only fallbacks for members without a valid manifest. The Web repository card is the only thing that may write it, and it may change only the version field — through a previewed, audited flow that requires a clean worktree, records coop history before the commit, preserves every other byte in the file, and rejects a lower SemVer precedence unless a downgrade override is explicitly enabled. The Native Launcher is entirely read-only here.
Why the Ceremony Is the Point
A version bump looks trivial, which is exactly why it's dangerous to leave unguarded. Bumping a product version is a claim — 'this is now 1.0.0, a stable contract.' If any surface could scribble that number, the claim would mean nothing. By making the bump a single-field, previewed, history-recorded, non-force-pushed operation from one editor, Firelink keeps the version honest: it changed on purpose, someone can see exactly what changed, and the change is a real commit with a real trail.