Skip to content

Add the composition lock and the kit installer - #5

Merged
ahrazzle merged 2 commits into
mainfrom
release/kit-lock-v2
Sep 17, 2026
Merged

ahrazzle merged 2 commits into
mainfrom
release/kit-lock-v2

Conversation

@ahrazzle

Copy link
Copy Markdown
Contributor

Add the composition lock and the kit installer

The composer gains the two pieces a distribution needs: a manifest that pins every
ingredient, and one command that installs and verifies the set.

What this adds

  • kit.lock.json - six ingredients pinned by annotated tag, peeled commit SHA,
    and tree hash (protean-tree-v1), with the hard and soft edges and the resolved
    install order. A pin is immutable: a moved tag is an integrity failure, never a
    silent upgrade.
  • install.sh and build/install.py - resolve the selection, compute the hard
    requires closure, fetch and verify each pin, stage each ingredient, run its
    own gates in place, promote it, and re-hash every written file against the
    cached blob.
  • build/check-lock.py - validates the lock, the host and owner allowlist, the
    closure, install target collisions, version and contract parity, the absence of
    a submodule or vendored tree, and with --verify recomputes every pin's tag
    pairing and tree hash.
  • build/check-ingredient-contract.py - checks every pinned ingredient at its
    pinned commit for its README contract line, its committed license, its machine
    descriptor, its standalone entrypoint, its declared gates, and a non-empty
    tests/ directory.
  • tests/test_kit_lock.py - the lock, the resolver, the cache, the stage and
    promote path, the read-back, and the exit codes, all exercised offline against
    fixture repositories.
  • Documentation: LICENSING.md re-titled and re-scoped to this repository, both
    README.md and LICENSING.md naming aska-digital/protean-kit as the source
    of truth, and the README documenting the distribution.

Behaviour

Exit codes are fixed at 0 to 6: 0 ok, 1 usage or input, 2 lock invalid,
3 dependency, 4 integrity, 5 install, 6 post-install verification. A run
that installs five of six ingredients exits non-zero, and a partial success is
never reported as success.

The composer contains no ingredient payload and no submodule: composition is
manifest-only.

Order resolution reads the lock's installOrder and validates it against the
dependency graph before using it. A declaration that is not a valid topological
order of the selection is reported, and the resolver's own topological order is
used instead. A requires cycle is a hard failure with the cycle path printed,
and it is never resolved by dropping an edge.

Evidence register

Recorded from a clean clone of this branch, Python 3.9.6, macOS.

Item Command Result
release gates python3 build/verify-all.py exit 0 - 10 gates run, 10 passed, 0 failed
lock validity python3 build/check-lock.py kit.lock.json exit 0 - check-lock: PASS ingredients=6 order=protean-doctrine -> protean-ops -> protean-drafts -> protean-github-flow -> protean-sym2p -> protean-control-plane
pin verification python3 build/check-lock.py kit.lock.json --verify --offline --cache <cache> exit 0 - every pin's tag pairing and tree hash recomputed from the cache
ingredient contract python3 build/check-ingredient-contract.py --lock kit.lock.json --verify --offline --cache <cache> exit 0 - PASS ingredients=6 unverified=0
lock tests python3 -m unittest tests.test_kit_lock exit 0 - 28 tests, OK
whole-kit install bash install.sh --all --target <fresh dir> --cache <cache> exit 0 - 6 fetched, order as locked, 6 read-backs, 70 files installed
single ingredient bash install.sh --ingredient protean-drafts --target <fresh dir> exit 0 - 1 repository fetched and written, full kit untouched
closure install bash install.sh --ingredient protean-control-plane --target <fresh dir> exit 0 - exactly 3 repositories: doctrine, ops, control-plane
degraded mode bash install.sh --ingredient protean-control-plane --no-deps --target <fresh dir> exit 0 - 1 repository written, requirements reported degraded
offline, warm cache bash install.sh --all --target <fresh dir> --offline --cache <cache> exit 0 - zero network fetches
offline, cold cache bash install.sh --all --target <fresh dir> --offline --cache <empty> exit 4 - offline cache unavailable for protean-doctrine at the pinned sha
dry run bash install.sh --all --target ./protean-installed --dry-run exit 0 - full plan printed, no file and no directory written

The fixture suite that runs offline covers the negative paths as well: a branch
ref, a duplicate slug, an owner outside the allowlist, an install target
collision, a moved tree hash, a requires cycle, a missing closure member, a
failing post-install gate, and a read-back difference.

Continuous integration on this pull request is PENDING at the time of writing.
This body records only what was observed locally; it does not report a
continuous-integration result.

Notes on the pins

protean-doctrine is pinned at v1.0.2. That repository's v1.0.0 shipped a
workflow matrix expression that would not have run, and v1.0.1 corrected it.
Because a published tag is never re-pointed, the correction was released as a new
tag, and v1.0.2 then synchronised the descriptor version with the tag it
belongs to. The other five ingredients are pinned at v1.0.0.

What this does not do

  • No merge, no visibility change, no archive, no transfer.
  • No claim is made about any ingredient's internal review state. The pins record
    what was published, not what was approved.

Posted by the build agent; the review and the merge decision are the reviewer's.

kit.lock.json pins six standalone ingredients by annotated tag, peeled commit
SHA, and tree hash (protean-tree-v1). install.sh, a thin entrypoint over
build/install.py, resolves a selection, computes the hard requires closure,
fetches and verifies each pin, stages and installs in dependency order, runs each
ingredient's own gates in place, and reads every written file back against its
cached blob.

Two gates join the release run: build/check-lock.py validates the lock, the pin
allowlist, the closure, install target collisions, and each pin's contract
parity, and recomputes every tree hash with --verify.
build/check-ingredient-contract.py checks each pinned ingredient at its pinned
commit for its README contract line, its committed license, its descriptor, its
entrypoint, its declared gates, and a non-empty tests directory.

Exit codes are fixed at 0 to 6: usage, lock invalid, dependency, integrity,
install, post-install verification. A run that installs part of the selection
exits non-zero and never reports a partial success as success. The installer
contains no ingredient payload and the composer uses no submodule.

Order resolution reads the lock's installOrder and validates it against the
dependency graph before use; a declaration that is not a valid topological order
of the selection is reported and the resolver's own order is used. A requires
cycle is a hard failure with the cycle path printed.

Documentation: LICENSING.md is re-titled and re-scoped to the composer, names
aska-digital/protean-kit as the source of truth, and states the three-zone model
plus the ingredient-license boundary; the README documents the distribution, the
dependency behaviour, the offline cache, staging and read-back, the exit table,
and the limits.

Evidence: build/verify-all.py runs ten gates, all passing; tests/test_kit_lock.py
covers the lock, the resolver, the cache, the stage and promote path, the
read-back, and the exit codes against fixture repositories, offline.
@ahrazzle

Copy link
Copy Markdown
Contributor Author

Base moved under this branch: rebase and re-verification required

This branch was cut from main at f6ff58f. While it was open, main advanced to
0c99ff0 ("merge KIT partition"), which reduces the tracked tree to nine files:
LICENSING.md, README.md, WHY.md, four files under choreography/, and
registry/openshorts-route.yaml.example. The build tooling, the tests, the
workflow, and CHANGELOG.md are not present at that commit.

Consequence: this pull request reports as CONFLICTING, and its diff against
0c99ff0 would re-add or modify paths the partition removed.

Affected paths, by class:

  • Present on this branch and absent at 0c99ff0: .github/workflows/verify.yml,
    CHANGELOG.md, build/check-ingredient-contract.py, build/check-lock.py,
    build/install.py, build/verify-all.py, install.sh, kit.lock.json,
    tests/test_kit_lock.py.
  • Present at both and modified here: LICENSING.md, README.md.

Everything recorded in the body above was produced against this branch's own tree
as cut from f6ff58f. It is unchanged and reproducible from that tree: the lock
validates, all six pins verify against their tags and tree hashes, and the
whole-kit, single-ingredient, closure, degraded, offline, and dry-run installs were
all run from it. It has not been re-run against 0c99ff0, and no claim in this
body should be read as testing the partitioned tree.

This pull request is left open and unready. It is not merged, not closed, and not
force-pushed. Rebasing it onto 0c99ff0, or re-cutting the composition layer
against the partitioned tree, changes the composer's own file set and is a
decision for the owner of that landing. After that decision, the whole
verification set in the body above has to be re-run on the new base before this
work is treated as verified.

Recorded for the reviewer: no workflow run is registered on this branch, so the
continuous-integration state is PENDING, not passing.

Resolve PR #5 conflicts against origin/main (1f14eec) per the six-path table:

- verify.yml, CHANGELOG.md, build/verify-all.py: kept the HEAD (branch) composition versions (modify/delete).
- install.sh: took main's version (identical blob 6256d73, mode 100755).
- kit.lock.json: took main's 7-pin version (six PR pins byte-identical; protean-handoff added).
- LICENSING.md: content merge from the branch composition-layer version plus main's canonical-site (https://proteus.askaconsult.com/) and Protean Team host wording; repository stays aska-digital/protean-kit.
- README.md: accepted git's auto-merge.
- All other paths: accepted the merge default (main deletions stay deleted).
@ahrazzle
ahrazzle merged commit 42a8257 into main Sep 17, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant