zeromodel

Post-split remediation — Stage 2: zeromodel-trust artifact trust and authorization kernel

Baseline SHA (start of this stage): c1ce710db50655a6082567fd3f376c3134095ea2 (branch main, working tree clean at start — confirmed via git branch --show-current, git rev-parse HEAD, git status --short before any change). Final SHA / working-tree state: uncommitted at the time of writing — every change described below is staged in the working tree, pending an explicit commit decision (nothing was committed, tagged, published, or pushed in this stage). Objective: build a cryptographic artifact-trust and deployment-authorization kernel as a new, independent distribution, without restoring the root zeromodel package, without making core or artifacts depend back on trust, and without inventing cryptography.

Prerequisite finding and scope decision

The stage brief assumed an already-merged “Artifacts stage” (an ArtifactRef + resolver/store protocol package). No such package, ADR, or trust model existed anywhere in the repository (confirmed by exhaustive find/grep across packages/, docs/, and git log --all). Per explicit user direction, a minimal zeromodel-artifacts package was built first (tracked as an implicit “Stage 1”) to unblock this stage; Trust and Navigation both depend on it. See package-boundaries.toml for the resulting dependency graph.

Architecture built

Files

The five decisions, preserved separately

TrustDecisionDTO carries integrity_valid, signature_valid, signer_known, signer_trusted, artifact_kind_allowed, scope_authorized, time_valid, epoch_valid, not_revoked as independent booleans, plus a failure_codes tuple and a final decision of "authorized" / "rejected" / "indeterminate". blocks_execution is True for both "rejected" and "indeterminate".

indeterminate is reached only when every other check passed and only revocation resolution could not return a confident answer (RevocationStatus.INDETERMINATE) — a definite REVOKED status still produces "rejected". This required a real bug fix during development: signer_trusted was initially computed by conflating “not CLEAR” with “untrusted,” which made an indeterminate revocation backend incorrectly reject rather than report "indeterminate". Caught by test_indeterminate_revocation_blocks_but_is_distinct_from_rejected on first test run; fixed by making signer_trusted track only definite REVOKED status, with indeterminate-vs-revoked routed entirely through the separate not_revoked computation.

Tests

packages/trust/tests/ — 31 tests, all passing:

Governance integration

Wired into every standard tool (shared with Stage 3 — see the “Combined governance wiring” section in post-split-stage-3-navigation-validation.md): requirements-dev.txt, root pyproject.toml (pythonpath, mypy_path), package-boundaries.toml, scripts/run_fast_tests.py (TEST_ROOTS), scripts/check_quality.py (all three governed path lists), scripts/validate_release_candidate.py (PACKAGES["trust"]), a dedicated .github/workflows/trust-package.yml, and .vscode/settings.json.

Claims boundary (as documented in packages/trust/README.md)

Supported: verifying integrity, signature, and declared deployment authorization of an identified artifact under a bounded trust policy. Explicitly not claimed: secure hardware, remote attestation, PKI deployment, regulatory certification, supply-chain security closure, or tamper-proof storage.

Validation run this session

python -m pytest -q packages/trust/tests          # 31 passed
python -m mypy packages/trust/src                  # Success: no issues found
python -m ruff check packages/trust/src packages/trust/tests    # All checks passed
python -m ruff format --check packages/trust/src packages/trust/tests  # passed (after one format pass)
python scripts/check_quality.py                     # Quality checks passed (all 9 packages)
python scripts/run_fast_tests.py                    # see combined report below
python scripts/validate_release_candidate.py        # Release candidate validation passed

Explicitly not run without further authorization