Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions .github/workflows/actions.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ workflows:
'.github/workflows/estate-rules.yml':
- 'actions/checkout@v7.0.1'
'.github/workflows/governance.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/guix-policy.yml':
- 'actions/checkout@v7.0.1'
'.github/workflows/hypatia-scan.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/instant-sync.yml': []
'.github/workflows/label-triage.yml': []
'.github/workflows/labels.yml': []
'.github/workflows/lock-sync-gate.yml': []
'.github/workflows/mirror.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/openssf-compliance.yml':
- 'actions/checkout@v7.0.1'
'.github/workflows/pages.yml':
Expand All @@ -51,16 +51,16 @@ workflows:
'.github/workflows/runtime-policy.yml':
- 'actions/checkout@v7.0.1'
'.github/workflows/rust-ci.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/scorecard.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/secret-scanner.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/security-policy.yml':
- 'actions/checkout@v7.0.1'
'.github/workflows/squabble-action.yml': []
'.github/workflows/standards-pipeline.yml':
- 'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66'
- 'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d'
'.github/workflows/static-analysis-gate.yml':
- 'actions/checkout@v7.0.1'
- 'actions/download-artifact@v8.0.2'
Expand Down Expand Up @@ -200,35 +200,34 @@ dependencies:
commit: 'sha1-c1c9fa07992a02c1fd3d67a0dc1b08cccb852aef'
owner_id: 6759885
repo_id: 1352485172
'hyperpolymath/standards@427eb35c40552d912d8c757518e8326f0d20f597':
ref: '427eb35c40552d912d8c757518e8326f0d20f597'
commit: 'sha1-427eb35c40552d912d8c757518e8326f0d20f597'
owner_id: 6759885
'hyperpolymath/standards@d55304a01b5260b0511aae0d14ed8b8770b00293':
ref: 'd55304a01b5260b0511aae0d14ed8b8770b00293'
commit: 'sha1-d55304a01b5260b0511aae0d14ed8b8770b00293'
owner_id: 297579217
repo_id: 1116521501
uses:
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
- 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f'
- 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8'
- 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406'
- 'swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6'
- 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555'
'hyperpolymath/standards@75bd5994470661a099df562d6d5ad2fe744dbd66':
ref: '75bd5994470661a099df562d6d5ad2fe744dbd66'
commit: 'sha1-75bd5994470661a099df562d6d5ad2fe744dbd66'
owner_id: 6759885
'metadatastician/standards@8e48b1caade08a552646adeab66558ac43b4360d':
ref: '8e48b1caade08a552646adeab66558ac43b4360d'
commit: 'sha1-8e48b1caade08a552646adeab66558ac43b4360d'
owner_id: 297579217
repo_id: 1116521501
uses:
- 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9'
- 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
- 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9'
- 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed'
- 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f'
- 'dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8'
- 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393'
- 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124'
- 'github/codeql-action@b96794f015dfd88f77b49b1c93e0fa7110f94c63'
- 'goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406'
- 'haskell-actions/setup@0f8e8c99d88aeb3fbfd523f1ef2c6f762d10d64d'
- 'hyperpolymath/standards@427eb35c40552d912d8c757518e8326f0d20f597'
- 'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc'
- 'hyperpolymath/standards@d55304a01b5260b0511aae0d14ed8b8770b00293'
- 'ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e'
- 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6'
- 'swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6'
- 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555'
Expand All @@ -237,6 +236,11 @@ dependencies:
commit: 'sha1-2d1146689b8cda280b9bc96326124645441f03bc'
owner_id: 67707773
repo_id: 421101922
'ossf/scorecard-action@fa7117db83c52d787ae5a89f350999701505d78e':
ref: 'v2.5.0'
commit: 'sha1-fa7117db83c52d787ae5a89f350999701505d78e'
owner_id: 67707773
repo_id: 421101922
'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6':
ref: 'v2.2.0'
commit: 'sha1-0c5077e51419868618aeaa5fe8019c62421857d6'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/governance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ permissions:

jobs:
governance:
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/governance-reusable.yml@8e48b1caade08a552646adeab66558ac43b4360d
permissions:
actions: read
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/hypatia-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ permissions:

jobs:
hypatia:
uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/hypatia-scan-reusable.yml@8e48b1caade08a552646adeab66558ac43b4360d
2 changes: 1 addition & 1 deletion .github/workflows/lock-sync-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name: Lock Sync Gate
# of a repository's CI that way, because Dependabot rewrites `uses:` refs in the
# YAML and cannot touch the lockfile. Measured across 200 repositories on
# 2026-09-22: 39 had silently dead CI from exactly this cause.
# See hyperpolymath/standards#968.
# See metadatastician/standards#968.
#
# This workflow deliberately carries NO `uses:` of its own. It checks out by
# calling git in a `run:` step instead of using actions/checkout, so it has no
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mirror.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ permissions:
contents: read
jobs:
mirror:
uses: hyperpolymath/standards/.github/workflows/mirror-reusable.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/mirror-reusable.yml@8e48b1caade08a552646adeab66558ac43b4360d
# Only the seven forge keys the callee declares (standards#1108 §6b);
# `secrets: inherit` forwarded every repository secret (WH008, zizmor
# secrets-inherit) to a workflow that asks for exactly these.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ permissions:
contents: read
jobs:
rust-ci:
uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/rust-ci-reusable.yml@8e48b1caade08a552646adeab66558ac43b4360d
permissions:
actions: read
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ permissions:

jobs:
scorecard:
uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/scorecard-reusable.yml@8e48b1caade08a552646adeab66558ac43b4360d
permissions:
actions: read
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/secret-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ permissions:
# .github/workflows/README.adoc.
jobs:
scan:
uses: hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/secret-scanner-reusable.yml@8e48b1caade08a552646adeab66558ac43b4360d
# No `secrets:` block, deliberately: secret-scanner-reusable.yml @75bd5994
# declares no caller secrets and reads no `secrets.*` context, so
# `secrets: inherit` only forwarded every repository secret to a workflow
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/standards-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
# not an adoption, and it is expected to be informative whether it is green or
# red.
#
# `.machine_readable/pipeline-allow.txt` in hyperpolymath/standards is seeded
# `.machine_readable/pipeline-allow.txt` in metadatastician/standards is seeded
# EMPTY by doctrine. Its own text: "An entry here must be EARNED BY
# OBSERVATION: the pipeline ran in that repository and went red. It must never
# be seeded by inference." and "The honest seed is a dry-run wave: adopt the
# caller in a deliberate sample, record the real verdict per repository, and add
# only what actually went red." This caller is how that observation is earned.
#
# ⚠ KNOWN DEFECT DISCLOSED: hyperpolymath/standards#976 — ci-pipeline.yml's
# ⚠ KNOWN DEFECT DISCLOSED: metadatastician/standards#976 — ci-pipeline.yml's
# `detect` job counts `*.ncl` into its ecosystem denominator, and the RSR
# template ships a six-file k9 Nickel set into every seeded repository, so
# TOTAL can be lifted above zero by boilerplate alone and the zero-denominator
Expand All @@ -33,10 +33,10 @@ permissions:

jobs:
pipeline:
# hyperpolymath/standards @ 75bd5994 (standards main 2026-10-19; see actions.lock).
# metadatastician/standards @ 8e48b1ca (standards main 2026-10-25; see actions.lock).
#
# NO `secrets:` block, deliberately. ci-pipeline.yml declares
# `workflow_call.inputs` only, has no `secrets:` block and references no
# `secrets.*` anywhere; `secrets: inherit` would hand the whole store to a
# workflow that consumes none of it.
uses: hyperpolymath/standards/.github/workflows/ci-pipeline.yml@75bd5994470661a099df562d6d5ad2fe744dbd66
uses: metadatastician/standards/.github/workflows/ci-pipeline.yml@8e48b1caade08a552646adeab66558ac43b4360d
10 changes: 10 additions & 0 deletions .machine_readable/root-allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,13 @@ CHANGELOG.adoc # Project documentation (AsciiDoc-by-default).
CODE_OF_CONDUCT.adoc # Project documentation (AsciiDoc-by-default).
SECURITY.adoc # Project documentation (AsciiDoc-by-default).
.mise.toml # Repo-specific mise config; must sit at the root to be found.

# ─── Declared 2026-10-25: downstream integration contracts.
# Two published, immutable-revision contracts that OTHER repositories
# integrate against: the read-only squabble CI invocation, and the
# Proven-Tests evidence format a Rust repository submits to. They are a
# top-level component rather than documentation: a consumer clones this
# tree at a pinned commit and runs contracts/bin/ from it, so the path is
# part of the contract's published surface and cannot be moved without
# breaking every pin. See contracts/README.adoc.
contracts/
22 changes: 22 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,28 @@ crg-badge:
test-all: test e2e aspect bench readiness
@echo "All test categories passed — safe to merge!"

# ── Downstream integration contracts ──────────────────────────────────────────
# Two published, immutable-revision contracts live in contracts/: the read-only
# CI invocation of squabble (contracts/squabble), and the evidence format a Rust
# repository submits to Proven-Tests (contracts/proven-tests). Both suites need
# bash, awk, coreutils and jq — no Rust and no Idris2 — so they run anywhere.
# See contracts/README.adoc.

# Run both contract conformance suites
contract-test:
#!/usr/bin/env bash
set -euo pipefail
bash contracts/squabble/tests/run-conformance.sh
bash contracts/proven-tests/tests/run-conformance.sh

# Verify a contract pin exactly as a downstream consumer does: just contract-pin squabble 1.0.0
contract-pin CONTRACT REVISION:
bash contracts/bin/verify-pin.sh {{CONTRACT}} {{REVISION}}

# Regenerate a contract's sha256 manifest after an intended change: just contract-manifest squabble
contract-manifest CONTRACT:
bash contracts/bin/make-shasums.sh contracts/{{CONTRACT}}

# Run all quality checks
quality: fmt-check lint test
@echo "All quality checks passed!"
Expand Down
34 changes: 34 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,40 @@ healthy. See `docs/proposals/squabble-modes-and-app-layer.adoc` for where
this is heading: upstream conformance, custodian rulesets, and the app-aware
layer.

== Downstream integration contracts (pinned, immutable revisions)

`contracts/` publishes two contracts that other repositories integrate against.
Both are consumed by *revision id plus a sha256 manifest*, never by a branch:

[cols="1,2"]
|===
| `contracts/squabble` — `squabble-contract/1.0.0`
| The read-only CI invocation: which subcommands are in contract, the six read
permissions the gate needs (and the `administration: read` an App token must
supply), the machine-readable gate/verdict documents, the exit semantics, and
the decision procedure for `legitimate_green` — a commit-bound envelope whose
*every* required check ran and passed. Missing, skipped, cancelled, pending and
failing required checks each have a negative fixture proving they cannot reach
it.

| `contracts/proven-tests` — `proven-tests-evidence/1.0.0`
| The evidence format a Rust repository submits to Proven-Tests: real executed
tests with a log digest, example/property/proof told apart, coverage derived
only from executed non-debt passes, the required Idris2 toolchain for the
framework itself (not for submitting), and a minimal downstream Rust fixture.
|===

[source,sh]
----
just contract-test # both conformance suites
just contract-pin squabble 1.0.0 # verify a pin as a consumer does
bash contracts/bin/verify-pin.sh proven-tests 1.0.0
----

Nothing in either contract writes: no merges, no re-runs, no edits, no admin
overrides. The consumer audit refuses a workflow that grants a write scope, pins
to a moving ref, masks a gate step, or calls `fight --apply`.

== Core principle: squabble ≠ bypass

Every "green" the squabbler declares is backed by the gate's *own* required
Expand Down
101 changes: 101 additions & 0 deletions contracts/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: CC-BY-SA-4.0
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
= Downstream integration contracts
:toc: preamble

Two contracts live here, one per repository that downstream CI may integrate
against. Both are *published as immutable revisions*, not as branches: a
downstream repository pins a revision id and the sha256 manifest that comes
with it, and a change to any published byte is a new revision.

[cols="1,2,2"]
|===
| Contract | Revision | What it pins
| `squabble/` | `squabble-contract/1.0.0` | How a repository invokes cicd-squabbler read-only from CI, the GitHub permissions that invocation needs, the machine-readable output, the exit semantics, and when the tool may declare `legitimate_green`.
| `proven-tests/` | `proven-tests-evidence/1.0.0` | How a Rust repository submits real, executed test evidence; how example, property and proof evidence are told apart; how coverage is derived without claiming untaken tests; the required Idris2 toolchain; and a minimal downstream fixture.
|===

== Layout

[source]
----
contracts/
├── bin/
│ ├── make-shasums.sh write or --check a contract's SHASUMS manifest
│ └── verify-pin.sh verify a downstream pin (revision + digest) end to end
├── squabble/
│ ├── CONTRACT.adoc the contract itself (normative prose)
│ ├── REVISION the revision id (one line)
│ ├── SHASUMS sha256 of every file in this directory
│ ├── schema/ machine-readable shapes: gate, verdict, envelope, output
│ ├── bin/ legitimate-green, bind-gate, audit-consumer-workflow
│ ├── fixtures/ positive and negative fixtures, each with an expectation
│ └── tests/run-conformance.sh
└── proven-tests/
├── EVIDENCE-CONTRACT.adoc the contract itself (normative prose)
├── LANDING.adoc how this package lands in proven-tests-and-benches
├── REVISION, SHASUMS
├── taxonomy-v1.json the 17 categories × 14 aspects this revision knows
├── schema/
├── bin/ verify-evidence, collect-evidence
├── fixtures/ positive and negative evidence, libtest logs, expectations
├── downstream-rust-fixture/ a minimal Rust repository that submits evidence
└── tests/run-conformance.sh
----

== Why a revision and not a branch

A downstream CI job that does `uses: hyperpolymath/cicd-squabbler@main` or
`git clone --branch main` is trusting whatever landed last. Two consequences,
both measured in this estate:

* A required check silently changes meaning between the run that was reviewed
and the run that gates the merge.
* A green claim becomes unreproducible: nobody can say which rule produced it.

So every pin in these contracts is a content pin:

[cols="1,2"]
|===
| Pin | How
| The contract itself | revision id + `SHASUMS` digest of that revision, recorded by the consumer
| The squabble action | `uses: hyperpolymath/cicd-squabbler/.github/actions/squabble@<40-hex sha>`
| The squabble binary | release tag + asset sha256 + build-provenance attestation (checked by `.github/actions/squabble/install.sh`)
| Proven-Tests evidence tooling | commit sha + `SHASUMS` digest of `contracts/proven-tests`
|===

`contracts/bin/verify-pin.sh` checks a pin as a consumer would:

[source,sh]
----
bash contracts/bin/verify-pin.sh squabble 1.0.0
bash contracts/bin/verify-pin.sh proven-tests 1.0.0
----

It fails if the `REVISION` file names a different revision, if any listed file
changed, if a listed file is missing, or if a file exists that the manifest
does not list. An unlisted file is a change: the manifest is the closed set.

== What these contracts deliberately do not do

* *They do not move.* A revision is frozen. Fixes ship as `1.0.1`; anything
that changes meaning ships as `2.0.0` and consumers opt in.
* *They do not write.* Neither contract's tooling merges, re-runs, edits,
dismisses, or overrides anything. `squabble`'s write-capable subcommands
(`fight --apply`, `board`, `inbox-sweep`) are out of contract, and the
consumer audit refuses a workflow that calls them.
* *They do not claim more than they can see.* Where a check cannot be made —
a verdict that carries no per-requirement run states, a coordinate a human
must review — the tooling says so and refuses, rather than inferring a pass.

== Running the conformance suites

[source,sh]
----
just contract-test
----

which runs both suites. Each suite prints one line per fixture and fails unless
every planned case ran, so a fixture that stops being exercised cannot quietly
become a pass. They need `bash`, coreutils and `jq` (v1.6 or later); they do not
need a Rust or Idris2 toolchain.
Loading
Loading