Skip to content

Standards Pack Upgrades (P2-25)

The org/team standards layer is pulled and pinned by cortexbuild standards pull (see 07-ORG_ROLLOUT and the standards sync docs). This page covers upgrading a pinned pack to a newer version safely and reversibly.

Versioning

A pack declares a semantic version in <pack>/pack.yaml:

version: 2.1.0

The version is recorded next to the resolved SHA in the supply-chain lockfile (.cortexbuild/standards/standards.lock.json, field pack_version). The bump kind of an upgrade drives the trigger:

Transition Bump Behaviour
1.2.31.2.4 patch preview, auto-applyable
1.2.31.3.0 minor preview, auto-applyable
1.2.32.0.0 major breaking — requires --confirm to apply
1.2.31.1.0 downgrade use standards downgrade (snapshot restore)

A missing or non-semver pack.yaml version resolves to 0.0.0 (never raises).

Preview → Apply

# 1. Preview only (no mutation). Resolves the ref to an EXACT SHA.
cortexbuild standards upgrade v2.0.0 --source git@example.com:org/pack.git

# 2. Apply. Re-fetches and pins the previewed SHA.
cortexbuild standards upgrade v2.0.0 --source ... --apply --confirm

The preview reports the version transition, bump kind, added/removed rule ids, and any override conflicts (counts + ids only — no proprietary bodies leave the machine; the output is routed through the egress redactor and carries no wall-clock, so it is byte-stable).

Fail-closed pinning (S5)

preview resolves the target ref to a full SHA. apply re-fetches and refuses to proceed if the ref now resolves to a different SHA than the one the preview pinned (a moved or forged tag) — raising a supply-chain error. After materialising the exact SHA, the pack content is re-hashed and verified against the lockfile before the upgrade is considered done.

Override conflicts (B3)

Customer overrides live in .cortexbuild/rules/{foundational,adaptive}/. Before an upgrade can clobber the org-side version of an overridden rule, the override is classified against the new pack via the shared reconcile classifier. Any conflicting-tier override is surfaced in the preview and blocks a silent apply unless --confirm is passed. A major (breaking) bump always requires --confirm as well.

Reversibility (B3)

Every --apply first writes an atomic pre-upgrade snapshot of the reversible surface:

  • .cortexbuild/standards/ (pack + lockfile, incl. pack_version)
  • .cortexbuild/ownership.json (the render ownership manifest)
  • .cortexbuild/rules/ (customer overrides)

The snapshot is built in a temp sibling and atomically swapped into .cortexbuild/standards-backups/<id>/, so a crash mid-snapshot can never leave a half-written backup. To roll back:

cortexbuild standards downgrade --backup-id pre-1.0.0-to-2.0.0-<sha12>

downgrade restores the snapshot byte-identically (overrides, lockfile, and ownership manifest) and re-renders all tool surfaces from the restored SSoT, so the whole repo — not just .cortexbuild/ — returns to its pre-upgrade state with no half-old / half-new surfaces.

Atomic apply (no half-migration)

If any step of an apply fails (e.g. post-upgrade content verification, or a migrator error), the engine restores the pre-upgrade snapshot before re-raising — the repo is never left half-old / half-new.

Migrations

Breaking pack changes can ship a reversible migrator registered against the exact version endpoints it bridges:

from cortexbuild_core.standards.migrate import register_migrator

@register_migrator("1.0.0", "2.0.0")
def _rename_rule(repo_root):
    ...
    return ["renamed rule-foo -> rule-bar"]

run_migrations(repo_root, from_version, to_version) dispatches every registered migrator whose endpoints fall within the upgrade span, in a deterministic order. The default registry is empty (no pack migrations exist yet), so an upgrade runs zero migrators — but the framework + dispatch are in place and tested.

Audit trail

Each applied upgrade and each downgrade appends an upgrade-kind event to the append-only governance audit log (.cortexbuild/audit-log.yaml, see 07-ORG_ROLLOUT). The audit log is append-only and is deliberately not rolled back by a downgrade — the record of what happened survives the reversal.