For release engineers who already run CBE/CBP gates and need a defensible sidecar attachment decision.
Problem: a release can be "green" while the optional CBM artifact was never
built, was built from a different hash, or was attached with a stale manifest.
cbm-attach consumes the existing evidence, verifies the canonical hashes and
the build-once wheel, and emits a machine-readable report plus a human-readable
body fragment. It never re-runs CBE/CBP and is not a gate in their needs
graph.
Start:
pip install cbm-attach==0.2.1
cbm-attach --helpIf you need the exact build-once artifact, install the released wheel:
pip install https://github.com/JunxiangPan/cbm/releases/download/v0.2.0/cbm_attach-0.2.0-py3-none-any.whl
cbm-attach --helpFrom a checkout:
python -m pip install -e .
cbm-attach --helpThe import package remains cbm_attach; the distribution and CLI are
cbm-attach.
CBM v0.1.1 is a sidecar attachment tool, not a complete product certification and not an A-F live E2E proof. GitHub Release:
https://github.com/JunxiangPan/cbm/releases/tag/v0.1.1
v0.1.0 remains published and unchanged. PyPI upload is blocked pending a trusted publisher/credentials; use the exact wheel above until then.
- B1 CONTRACT_ACTIVE — contract-v1.7.1 §5.1 active; identity tests retained.
- 0.1.1 changes — Apache-2.0 license, canonical evidence-hash trust root,
cbm-attachdistribution/console script, uploader failure tests, in-toto Statement, generic evidence adapters. - B3 FIELD_LEVEL_COMPLETE — CBP 1.2.2 object manifest, base scope, artifact/evidence hashes.
- B4 UPLOADED_VERIFIED — object artifact repository matched; fail-closed boundaries retained.
- B5 FIXED — CBS body generator emits
CBM: NOT_UPLOADED+EVIDENCE_ABSENT.
PyPI: PUBLISHED — https://pypi.org/project/cbm-attach/0.1.1/
139 offline/static tests pass. The source is Apache-2.0. No A-F live E2E
or full-product certification is claimed. Evidence lives in
audit/v1.2.0-rc/.
CBE and CBP evidence_sha256 values are recomputed from canonical JSON before
the gate decides; a format-valid but wrong hash exits 3
EVIDENCE_HASH_MISMATCH. CBM's own report is sealed the same way. Raw-byte
report hashes remain separate from canonical evidence hashes.
verify and upload write an in-toto v1 Statement next to the CBM report
(cbm-report.statement.json by default, or --statement PATH). Its
predicateType is https://cbm.dev/attachment/v1; the existing
cbm-report.json is preserved inside the predicate.
For non-CBM subjects, cbm_attach.evidence exposes EvidenceInput,
load_evidence() and identity_errors_for(subject_package_id=..., producers=...).
This is an adapter layer; the four-piece CBE/CBP/CBM path is unchanged.
0.2.0 accepts CBS-style .zip bundles in addition to wheels. The bundle must
be listed in RELEASE_ASSETS.sha256; the report records artifact_type and
the bundle manifest SHA256. This does not replace CBS/CBP signing.
CBM can verify and copy an external semantic-verdict/v1 as a standalone
attachment. It does not run a semantic model, does not import CBF/CBP/CBE, and
does not change the 0.1.1/0.2.0 sidecar report contract.
cbm-attach verify-semantic --verdict semantic-verdict.json
cbm-attach attach-semantic --verdict semantic-verdict.json --output-dir semantic-out- CBM does not run CBE or CBP, and does not re-compute their gates.
- CBM does not prove code security.
- CBM does not rebuild the package. It verifies the build-once artifact and
refuses an artifact that is not listed in
WHEEL_SHA256andRELEASE_ASSETS.sha256. CBM 0.1.0 supportsartifact_type=wheelonly; a zip-bundle is rejected as an input error. - CBM does not write the whole CBS Release body. It emits one marker-delimited fragment; the CBS body generator merges it.
| Situation | cbm_gate_status |
upload_status |
exit |
|---|---|---|---|
| not claimed, no failure | NOT_UPLOADED |
NOT_UPLOADED |
0 |
| CBE/CBP FAIL blocks upload | FAIL |
NOT_UPLOADED |
3 |
| own hash/upload verification failure | FAIL |
NOT_UPLOADED or UPLOADED_UNVERIFIED |
3 |
| infrastructure failure | SKIPPED_INFRA |
NOT_UPLOADED |
3 |
| missing/invalid input | PRECONDITION_FAIL |
NOT_UPLOADED |
2 |
| skip ratio above 20% | FAIL |
NOT_UPLOADED |
4 |
| uploaded and digest verified | PASS |
UPLOADED_VERIFIED |
0 |
The Release body labels the attachment from upload_status, so a gate failure
is still shown as NOT_UPLOADED without being laundered into PASS.
# verify: consume reports, enforce the gate, write report/body/decision
python -m cbm_attach verify \
--version v0.1.0 --tag v0.1.0 --release-tag v1.7.0 \
--declared-attached true --upload-target github-release \
--cbe-report cbe.json --cbp-report cbp.json --build-once-dir build-once \
--output cbm-evidence/cbm-report.json \
--body cbm-evidence/cbm-body.md \
--decision cbm-evidence/cbm-decision.json
# upload: re-verify the build-once artifact, then upload idempotently
python -m cbm_attach upload \
--report cbm-evidence/cbm-report.json \
--decision cbm-evidence/cbm-decision.json \
--build-once-dir build-once \
--upload-target github-release \
--repository owner/repo --release-tag v1.7.0 \
--output cbm-evidence/cbm-report.final.json \
--body cbm-evidence/cbm-body.final.md
# finalize: choose the best report, regenerate the body fragment
python -m cbm_attach finalize \
--input cbm-evidence --output cbm-evidence-out/cbm-report.json \
--body cbm-evidence-out/cbm-body.md --declared-attached trueverify --fetch and upload --fetch download run artifacts through gh api.
artifact_repository must hold all three run artifacts (CBM build-once, CBE
report, CBP manifest); split repositories need an input extension in a later
version. Cross-repository download needs a token with actions:read on the
artifact repository; cross-repository release upload needs contents:write on
the release repository. Use a scoped GitHub App/PAT, not a broad personal token.
GitHub artifact downloads are repository-bound. The v1.7 release train must
upload the CBM build-once artifact, CBE report and CBP manifest to the same
artifact_repository. If CBE/CBP upload in their own repositories, CBM cannot
download them; verify --fetch will exit 2 with MISSING_BUILD_ONCE_ARTIFACT,
MISSING_CBE_REPORT or MISSING_CBP_REPORT. This is a release-train
orchestration requirement, not something CBM can fix alone. It should be added
to CONTRACT.md §11.
Audited on 2026-11-08: the checked-in CBS mock train
(reusable-v17-release.yml) uploads only v17-mock-artifact in the CBS repo
and has no CBM job; CBP ci.yml uploads quality-manifest-${run_id} in the
CBP repo. The artifacts are not co-located, so the same-repo contract is
not implemented yet; this is blocker B4, and it must be solved together
with B3 (CBP P0-06), not separately. CBM v0.1.0 takes one
artifact_repository; split repos need a copy step or a v0.2.0 input
extension. Required names:
cbm-build-once, cbe-report, cbp-quality-manifest (the actual CBP artifact
may be quality-manifest-<run_id>; pass it as cbp_report_artifact). See
docs/INTEGRATION_REQUIREMENTS.md.
CBM 0.1.0 supports artifact_type=wheel only. A CBS artifact_type=zip-bundle
requires CBM 0.2.0. CBM does not sign and does not consume CBS's
update-manifest.sig; that is a CBS release asset and must be listed in CBS's
own RELEASE_ASSETS.sha256 per §11. CBM reads only the CBM build-once
artifact's manifest.
The reusable workflow is .github/workflows/cbm-attach.yml. It has three jobs:
verifyconsumes CBE/CBP reports, enforces the gate, and writes a draftcbm-evidence/cbm-report.jsonpluscbm-evidence/cbm-body.md.uploadruns only whenneeds.verify.result == 'success'andneeds.verify.outputs.decision == 'UPLOAD'; it re-verifies the build-once artifact, uploads it, and writescbm-evidence/cbm-report.final.jsonpluscbm-evidence/cbm-body.final.md.needs.verify.resultisfailurefor exit 2/3/4, so missing input, gate failure and skip-over-threshold all skip upload.finalizeruns withif: ${{ !cancelled() }}, checks out the CBM tooling independently of verify's artifact, recovers every evidence artifact, and writescbm-evidence-out/cbm-report.jsonpluscbm-evidence-out/cbm-body.md(a marker-delimited fragment). A checkout or GitHub API outage can still prevent finalize from writing evidence. In that case CBS body generator supplies theCBM: NOT_UPLOADED+EVIDENCE_ABSENTrecord. CBS v1.7.0-integrationfa1740fimplements this; B5 is fixed and the test is flipped. Seedocs/V17_BLOCKERS.mdanddocs/CBS_BODY_REQUIREMENT.md.
The CBS body generator must consume the finalized cbm-evidence-out
artifact, not the verify draft. The verify draft says NOT_UPLOADED because the
upload has not happened yet; that is honest, but it is not the final attachment
status.
CBM does not edit the CBS Release body in v0.1.0. The fragment includes
<!-- CBM:START --> / <!-- CBM:END --> so the CBS generator can merge one
block idempotently. If a future asynchronous step updates the body, it must do a
latest-body read-modify-write, replace exactly the marker block, retry on
conflict, and never touch the body when the upload failed. body.py already
provides replace_marker_block for that future path.
If finalize finds no report at all, it writes
cbm_gate_status=SKIPPED_INFRA, reason=EVIDENCE_ABSENT (detail
WORKFLOW_FAILED_BEFORE_EVIDENCE) and upload_status=NOT_UPLOADED; it never
writes PASS. If the verify or upload job
failed after writing UPLOAD_PENDING, finalize turns that stale pending
report into FAIL / UPLOAD_FAILED (or EVIDENCE_INVALID for a
PASS-without-upload contradiction).
cbe_report_hash and cbp_report_hash are SHA-256 over the raw bytes of
the report that CBM read. They are not the report's evidence_sha256 field and
they are not a canonicalised JSON hash. evidence_sha256 on the CBM report is
the SHA-256 of the canonical JSON of the report without that field.
python -m pytest139 tests pass: exit-code mapping, gate-before-upload (including a mock uploader that records calls), NOT_UPLOADED body rendering, hashes, workflow static audit, schema/report lockstep, and a mock end-to-end verify/upload/finalize flow. These are offline/static tests, not real E2E.
tests/test_known_fail_silent.py is now the flipped B5 regression test: it
asserts the fixed CBS output (CBM: NOT_UPLOADED + EVIDENCE_ABSENT, no
- none) and the exact phrase when CBM is declared attached. It still checks the
probe's missing/import/unexpected modes; a missing CBS checkout skips with
SKIP_REASON=CBS_BODY_GENERATOR_NOT_FOUND, and an unimportable file fails with
FAIL_REASON=CBS_BODY_GENERATOR_UNIMPORTABLE.
139 passed = 139 normal tests; no KNOWN_FAIL_SILENT canary remains. Do not
reintroduce the old - none assertion as an accepted state.
Probe CLI:
python tools/check_b5.py --check-upstream| Output | Exit | Meaning |
|---|---|---|
STATUS=B5_MAYBE_FIXED |
0 | expected fixed state; B5 test is green |
STATUS=KNOWN_FAIL_SILENT |
0 | old/failed CBS behavior; B5 test fails as a regression |
SKIP_REASON=CBS_BODY_GENERATOR_NOT_FOUND |
0 | CBS checkout missing; test skips |
FAIL_REASON=CBS_BODY_GENERATOR_UNIMPORTABLE |
3 | CBS file exists but import/build fails; test fails |
FAIL_REASON=UNEXPECTED_OUTPUT |
2 | output is neither old defect nor fixed; inspect |
- §5.1 report identity. CBE/CBP reports currently use
package_idambiguously. CBM requirespackage_id == "cbm"and treats a producer name there asREPORT_IDENTITY_MISMATCH(exit 3, no upload). - §5.2 CBP body hash scope. CBM requires
release_body_hashandbody_hash_scope: "base"in the CBP quality manifest; missing or"full"exits 2.
The proposals are in
docs/CONTRACT_PROPOSAL_5.1.md; the full
blocker list is docs/V17_BLOCKERS.md. They are
change-control inputs for the CBS/CBP owners; this repository does not edit the
frozen contract. CBM 0.1.0 sidecar upload is verified; B3 field-level and B5
are complete; full product/A-F live E2E is not claimed.
CBF is not integrated yet. Status: PENDING_CBF_0.1.0; there is no CBF
PASS for this project or its releases. The cbf-privacy-gate CI job name is
reserved as a name only; no workflow file is added in Phase 0. No
continue-on-error / if: false bypass exists. See docs/CBF_ADOPTION.md.
CBF evidence is an optional attachment; it does not change the CBM 0.1.1/0.2.0 sidecar contract and CBM does not import or require CBF. After adoption, GitHub push / Release / PyPI / cloud-AI / local-AI use requires CBF PASS. CBF 0.1.0 is not released yet, so no CBF PASS exists. Until then, do not describe the project as CBF-compatible or CBF-passed.
CBF 0.1.0 has not been released. This project is not CBF-integrated and has no CBF PASS. Phase 0 preparation is recorded in docs/CBF_ADOPTION.md; no runtime dependency, no import, and no CBF-certified release exists yet.