"Product, bundle build, state schema, URL protocol, libmpv build, and golden-corpus revision each advance for their own compatibility reasons."
The One-Number Fallacy
It's tidy to imagine an app has 'a version.' It doesn't. Ashen Reel has at least six things that carry a version, and each of them changes for a completely different reason. Force them all onto one number and you get one of two failures: either you bump the product version every time you repin a dylib (false churn that means nothing to the user), or you quietly change the deep-link grammar without anyone noticing a compatibility break (hidden breakage that means everything). One number can't be honest about six independent kinds of change.
Six Domains, Six Reasons
The fix is to name each domain and let it advance on its own clock:
- Product version — the visible release, owned by
cwk-product.jsonand advanced through Firelink. This is the number a human means by 'what version.' - Bundle build number — a deployment counter that ticks on any shippable build. A tally, not a story.
- State schema revision — bumps only when the local SQLite shape changes and a migration is needed.
- URL protocol version — bumps only when the
cwkashenreel://handoff grammar changes. - libmpv build — advances when the embedded engine is repinned to a new build.
- Golden-corpus revision — advances when the required-media matrix changes.
The whole scheme fits on a card, and keeping the card is what stops the conflation:
cwk-product.json — carries the visible product release, and display surfaces add the 'v.' Package versions, bundle build numbers, schema revisions, and Git commits are NOT competing product versions; treating any of them as 'the version' is how a build number ends up on a marketing page. One canonical product number, many private compatibility counters.Why Separation Is Kindness to Future-You
Independent version domains are a gift to the person debugging this in a year. When something breaks, the versions tell a precise story: the schema is at rev 4 but this database is rev 3, so migrate; the deep link says protocol 2 but this build only speaks 1, so reject. If all six were mashed into one number, every one of those diagnoses would be a guess. Naming the domains turns 'it's version 1.0.0, why doesn't it work?' into a set of specific, answerable compatibility questions. That's the whole track's lesson in miniature: honesty about what's actually independent makes the system legible.