Literate AI

Repository documentation

ADR 0010: Canonical Flavor Names Use One Axis-Prefixed Dash Scheme, Migrated Behind Explicit Re-Pinning

Context

Flavor identity is currently spelled three incompatible ways inside canonical coordinates: dash-prefixed (lang-python, os-linux, build-make, toolchain-swift-apple), dot-prefixed (package.pip), and bare (google-workspace, microsoft-365). One flavor accepts up to five selector spellings (pip, package.pip, package-pip, packaging=pip, flavor://literate-ai/package.pip), four of the nine axes have no flat alias at all, and directory names, canonical names, and target values diverge (bazel / build-bazel / bazel-preferred). Since Phase 2 (MITIGATION-FW-002), the selector tables derive from the shipped catalog's flavor.md frontmatter, so adding a flavor no longer requires Python edits — but the coordinate scheme itself remains the mixed convention frozen into every shipped flavor.md.

The migration has been deferred for one reason: flavor authority bytes feed flavor revision identities, which are bound into checked-in component.lock.json files, sample harness manifests, and test receipts. Renaming package.pip to package-pip without a coordinated re-pin silently invalidates every lock that selected it, converting an aesthetic cleanup into distributed corruption.

Decision

  1. One canonical scheme. Every canonical Flavor name is <axis-prefix>-<value> with a dash separator, using this fixed prefix table:

    Axis Prefix Example
    platform.os os- os-linux
    platform.architecture arch- arch-arm64
    accelerator accel- accel-nvidia-cuda
    implementation.language-ecosystem lang- lang-python
    implementation.ui-framework ui- ui-react
    implementation.javascript-packages js- js-npm
    build.system build- build-make
    toolchain toolchain- toolchain-swift-apple
    packaging package- package-pip
    deployment deploy- deploy-docker
    documentation.ecosystem doc- doc-google-workspace

    The separator encodes no semantics; it exists so one regex can distinguish a canonical name from a bare value. Namespacing stays flavor://<namespace>/<name>.

  2. Deprecation aliases, not silent acceptance. The 0.8.0 window close removed dotted canonical names from live selectors. Bare target values remain valid indefinitely through flavor_candidate_aliases and the derived init alias table. Lock bytes always store the canonical form. canonical_flavor_coordinate still maps leftover dotted identities so duplicate alias directories fail closed instead of forking.

  3. Directory names follow canonical names (flavors/package-pip/, not flavors/pip/) so disk, catalog, and selector agree. Target values are decoupled: build-bazel's target value becomes bazel (dropping bazel-preferred) as part of this migration rather than a separate B4 item.

  4. Migration is a reviewed re-pin, not a rewrite. A litai catalog migrate-flavor-names command with --dry-run / --record semantics (mirroring scripts/review_lifecycle_driver.py) enumerates every affected lock, harness manifest, receipt fixture, and sample metadata identity, shows the exact drift, and re-pins only after review. The rename commit contains catalog changes and re-pinned consumers together; CI fails on any half-migrated state because byte-equality and current-lock checks cannot pass across the split.

  5. No wire-schema change. Coordinates are opaque strings in published contracts; historical documents that cite old coordinates remain valid records of past derivations. Only newly written locks carry canonical coordinates.

Classification (per ADR 0003)

Consequences

Read the Markdown sourceConference homePublished 1.3.0 · Patch 1.3.1 in progress · Development 1.4