Skip to content

Commit 8d92aa1

Browse files
Nathan Gillettcursoragent
andcommitted
ci: split spec conformance and attestation like API
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 16273a1 commit 8d92aa1

6 files changed

Lines changed: 233 additions & 91 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -83,94 +83,6 @@ jobs:
8383
- name: SDK hardening contract audit (spec-only + drift guards)
8484
run: bash intentproof-spec/scripts/check-consumer-hardening.sh "${{ github.workspace }}"
8585

86-
intentproof-spec:
87-
runs-on: ubuntu-latest
88-
timeout-minutes: 15
89-
env:
90-
PUBLISH_CONFORMANCE_ROOT: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master') && github.actor != 'github-actions[bot]' && github.actor != 'intentproof-cert-bot[bot]' }}
91-
steps:
92-
- uses: actions/checkout@v6
93-
with:
94-
persist-credentials: false
95-
96-
- name: Resolve pinned intentproof-spec revision
97-
id: spec_pin
98-
run: |
99-
set -euo pipefail
100-
python3 - <<'PY'
101-
import os
102-
import tomllib
103-
from pathlib import Path
104-
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
105-
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
106-
f.write(f"sha={c}\n")
107-
PY
108-
109-
- uses: actions/checkout@v6
110-
with:
111-
repository: IntentProof/intentproof-spec
112-
ref: ${{ steps.spec_pin.outputs.sha }}
113-
fetch-depth: 0
114-
path: intentproof-spec
115-
116-
- uses: actions/setup-node@v6
117-
with:
118-
node-version-file: intentproof-spec/.nvmrc
119-
cache: npm
120-
cache-dependency-path: intentproof-spec/package-lock.json
121-
122-
- name: Canonical spec conformance (pin check + oracle + replay)
123-
env:
124-
INTENTPROOF_SPEC_ROOT: ${{ github.workspace }}/intentproof-spec
125-
INTENTPROOF_SDK_ID: python
126-
INTENTPROOF_REPLAY_VERIFY: "1"
127-
INTENTPROOF_CONFORMANCE_JSON: "1"
128-
INTENTPROOF_CERTIFICATE_SCHEMA_VERSION: "v2"
129-
INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PATH: ${{ github.workspace }}/intentproof-spec/keys/spec-integrity.public.pem
130-
run: bash scripts/spec-conformance.sh
131-
132-
- name: Upload conformance report artifact
133-
uses: actions/upload-artifact@v7
134-
with:
135-
name: conformance-report-python
136-
path: intentproof-spec/conformance-report.json
137-
if-no-files-found: error
138-
139-
- name: Upload conformance certificate artifact
140-
uses: actions/upload-artifact@v7
141-
with:
142-
name: conformance-certificate-python
143-
path: intentproof-spec/conformance-certificate.json
144-
if-no-files-found: error
145-
146-
- name: Mint cert-bot installation token
147-
id: cert_bot_token
148-
if: ${{ env.PUBLISH_CONFORMANCE_ROOT == 'true' }}
149-
uses: actions/create-github-app-token@v1
150-
with:
151-
app-id: ${{ vars.INTENTPROOF_CERT_BOT_ID }}
152-
private-key: ${{ secrets.INTENTPROOF_CERT_BOT_PRIVATE_KEY }}
153-
154-
- name: Publish conformance certificate and report at repo root
155-
if: ${{ env.PUBLISH_CONFORMANCE_ROOT == 'true' }}
156-
env:
157-
INTENTPROOF_CERT_BOT_TOKEN: ${{ steps.cert_bot_token.outputs.token }}
158-
run: |
159-
set -euo pipefail
160-
cp intentproof-spec/conformance-certificate.json conformance-certificate.json
161-
cp intentproof-spec/conformance-report.json conformance-report.json
162-
if git diff --quiet -- conformance-certificate.json conformance-report.json; then
163-
echo "No conformance artifact updates to commit."
164-
exit 0
165-
fi
166-
git config user.name "intentproof-cert-bot[bot]"
167-
git config user.email "intentproof-cert-bot[bot]@users.noreply.github.com"
168-
git config --local --unset-all http.https://github.com/.extraheader || true
169-
git remote set-url origin "https://x-access-token:${INTENTPROOF_CERT_BOT_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
170-
git add conformance-certificate.json conformance-report.json
171-
git commit -m "chore(ci): update conformance certificate and report"
172-
git push
173-
17486
spec-golden-parity:
17587
runs-on: ubuntu-latest
17688
timeout-minutes: 10
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Trusted-path attestation: signed conformance certificate + validation.
2+
# Mirrors intentproof-api/.github/workflows/conformance-attestation.yml.
3+
# PR checks stay on spec-conformance.yml (no signing secrets required).
4+
name: Conformance Attestation
5+
6+
on:
7+
push:
8+
branches: [main, master]
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: conformance-attestation-${{ github.ref }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
conformance-attestation:
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 30
22+
env:
23+
PUBLISH_CONFORMANCE_ROOT: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master') && github.actor != 'github-actions[bot]' && github.actor != 'intentproof-cert-bot[bot]' }}
24+
steps:
25+
- name: Checkout SDK repository
26+
uses: actions/checkout@v6
27+
with:
28+
persist-credentials: false
29+
30+
- name: Resolve pinned intentproof-spec revision
31+
id: spec_pin
32+
run: |
33+
set -euo pipefail
34+
python3 - <<'PY'
35+
import os
36+
import tomllib
37+
from pathlib import Path
38+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
39+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
40+
f.write(f"sha={c}\n")
41+
PY
42+
43+
- name: Checkout intentproof-spec
44+
uses: actions/checkout@v6
45+
with:
46+
repository: IntentProof/intentproof-spec
47+
ref: ${{ steps.spec_pin.outputs.sha }}
48+
fetch-depth: 0
49+
path: intentproof-spec
50+
51+
- uses: ./.github/actions/setup-python-pip
52+
with:
53+
python-version: "3.x"
54+
check-latest: true
55+
56+
- name: Install tox
57+
run: pip install "tox>=4"
58+
59+
# secrets context is not allowed in step `if:`; pass PEMs via env and verify in shell.
60+
- name: Require attestation secrets
61+
env:
62+
INTENTPROOF_CERTIFICATE_SIGNING_KEY_PEM: ${{ secrets.INTENTPROOF_CERTIFICATE_SIGNING_KEY_PEM }}
63+
INTENTPROOF_CERTIFICATE_PUBLIC_KEY_PEM: ${{ secrets.INTENTPROOF_CERTIFICATE_PUBLIC_KEY_PEM }}
64+
INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PEM: ${{ secrets.INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PEM }}
65+
run: |
66+
if [[ -z "${INTENTPROOF_CERTIFICATE_SIGNING_KEY_PEM}" || -z "${INTENTPROOF_CERTIFICATE_PUBLIC_KEY_PEM}" ]]; then
67+
echo '::error::Configure repository secrets INTENTPROOF_CERTIFICATE_SIGNING_KEY_PEM and INTENTPROOF_CERTIFICATE_PUBLIC_KEY_PEM.'
68+
exit 1
69+
fi
70+
if [[ -z "${INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PEM}" ]]; then
71+
echo '::error::Configure repository secret INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PEM (spec integrity verify during oracle).'
72+
exit 1
73+
fi
74+
75+
- name: SDK quality gates (tox static + cov)
76+
env:
77+
INTENTPROOF_SPEC_ROOT: ${{ github.workspace }}/intentproof-spec
78+
run: tox run -e static,cov
79+
80+
- name: Set up Node.js for spec oracle
81+
uses: actions/setup-node@v6
82+
with:
83+
node-version-file: intentproof-spec/.nvmrc
84+
cache: npm
85+
cache-dependency-path: intentproof-spec/package-lock.json
86+
87+
- name: Conformance (trusted attestation gate)
88+
env:
89+
INTENTPROOF_SPEC_ROOT: ${{ github.workspace }}/intentproof-spec
90+
INTENTPROOF_SDK_ID: python
91+
INTENTPROOF_REPLAY_VERIFY: "1"
92+
INTENTPROOF_CONFORMANCE_JSON: "1"
93+
INTENTPROOF_CERTIFICATE_SCHEMA_VERSION: "v2"
94+
INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PEM: ${{ secrets.INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PEM }}
95+
INTENTPROOF_CERTIFICATE_SIGNING_KEY_PEM: ${{ secrets.INTENTPROOF_CERTIFICATE_SIGNING_KEY_PEM }}
96+
INTENTPROOF_CERTIFICATE_SIGNING_KEY_ID: intentproof-ci-ed25519-v1
97+
run: bash scripts/spec-conformance.sh
98+
99+
- name: Validate conformance certificate
100+
working-directory: intentproof-spec
101+
env:
102+
INTENTPROOF_CERTIFICATE_PUBLIC_KEY_PEM: ${{ secrets.INTENTPROOF_CERTIFICATE_PUBLIC_KEY_PEM }}
103+
INTENTPROOF_CERTIFICATE_REQUIRE_SIGNATURE: "1"
104+
INTENTPROOF_CERTIFICATE_SCHEMA_VERSION: "v2"
105+
run: npm run validate:conformance-certificate
106+
107+
- name: Upload conformance report and certificate
108+
uses: actions/upload-artifact@v7
109+
with:
110+
name: conformance-artifacts
111+
path: |
112+
intentproof-spec/conformance-report.json
113+
intentproof-spec/conformance-certificate.json
114+
if-no-files-found: error
115+
116+
- name: Mint cert-bot installation token
117+
id: cert_bot_token
118+
if: ${{ env.PUBLISH_CONFORMANCE_ROOT == 'true' }}
119+
uses: actions/create-github-app-token@v1
120+
with:
121+
app-id: ${{ vars.INTENTPROOF_CERT_BOT_ID }}
122+
private-key: ${{ secrets.INTENTPROOF_CERT_BOT_PRIVATE_KEY }}
123+
124+
- name: Publish conformance certificate and report at repo root
125+
if: ${{ env.PUBLISH_CONFORMANCE_ROOT == 'true' }}
126+
env:
127+
INTENTPROOF_CERT_BOT_TOKEN: ${{ steps.cert_bot_token.outputs.token }}
128+
run: |
129+
set -euo pipefail
130+
cp intentproof-spec/conformance-certificate.json conformance-certificate.json
131+
cp intentproof-spec/conformance-report.json conformance-report.json
132+
if git diff --quiet -- conformance-certificate.json conformance-report.json; then
133+
echo "No conformance artifact updates to commit."
134+
exit 0
135+
fi
136+
git config user.name "intentproof-cert-bot[bot]"
137+
git config user.email "intentproof-cert-bot[bot]@users.noreply.github.com"
138+
git config --local --unset-all http.https://github.com/.extraheader || true
139+
git remote set-url origin "https://x-access-token:${INTENTPROOF_CERT_BOT_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
140+
git add conformance-certificate.json conformance-report.json
141+
git commit -m "chore(ci): update conformance certificate and report"
142+
git push
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# PR / manual: SDK quality gates + canonical spec oracle (no signing secrets).
2+
# Trusted signed attestation + repo-root certificate publish lives in conformance-attestation.yml.
3+
name: Spec Conformance
4+
5+
on:
6+
workflow_dispatch:
7+
pull_request:
8+
9+
permissions:
10+
contents: read
11+
12+
concurrency:
13+
group: spec-conformance-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
spec-conformance:
18+
name: Spec conformance + SDK quality gates
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 25
21+
22+
steps:
23+
- name: Checkout SDK repository
24+
uses: actions/checkout@v6
25+
26+
- name: Resolve pinned intentproof-spec revision
27+
id: spec_pin
28+
run: |
29+
set -euo pipefail
30+
python3 - <<'PY'
31+
import os
32+
import tomllib
33+
from pathlib import Path
34+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
35+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
36+
f.write(f"sha={c}\n")
37+
PY
38+
39+
- name: Checkout intentproof-spec
40+
uses: actions/checkout@v6
41+
with:
42+
repository: IntentProof/intentproof-spec
43+
ref: ${{ steps.spec_pin.outputs.sha }}
44+
fetch-depth: 0
45+
path: intentproof-spec
46+
47+
- uses: ./.github/actions/setup-python-pip
48+
with:
49+
python-version: "3.x"
50+
check-latest: true
51+
52+
- name: Install tox
53+
run: pip install "tox>=4"
54+
55+
- name: SDK quality gates (tox static + cov)
56+
env:
57+
INTENTPROOF_SPEC_ROOT: ${{ github.workspace }}/intentproof-spec
58+
run: tox run -e static,cov
59+
60+
- name: Set up Node.js for spec oracle
61+
uses: actions/setup-node@v6
62+
with:
63+
node-version-file: intentproof-spec/.nvmrc
64+
cache: npm
65+
cache-dependency-path: intentproof-spec/package-lock.json
66+
67+
- name: Run canonical spec conformance
68+
env:
69+
INTENTPROOF_SPEC_ROOT: ${{ github.workspace }}/intentproof-spec
70+
INTENTPROOF_SDK_ID: python
71+
INTENTPROOF_REPLAY_VERIFY: "1"
72+
INTENTPROOF_CONFORMANCE_JSON: "1"
73+
INTENTPROOF_CERTIFICATE_SCHEMA_VERSION: "v2"
74+
INTENTPROOF_SPEC_INTEGRITY_PUBLIC_KEY_PATH: ${{ github.workspace }}/intentproof-spec/keys/spec-integrity.public.pem
75+
run: |
76+
bash scripts/spec-conformance.sh
77+
cp intentproof-spec/conformance-report.json conformance-report.json
78+
79+
- name: Upload conformance report artifact
80+
uses: actions/upload-artifact@v7
81+
with:
82+
name: conformance-report-python
83+
path: conformance-report.json
84+
if-no-files-found: error

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ All notable changes to this repository are documented here. **PyPI** releases us
66

77
## Unreleased
88

9-
- None yet.
9+
- **CI / attestation alignment:** split canonical conformance out of **`ci.yml`** into **`spec-conformance.yml`** (PR + manual) and **`conformance-attestation.yml`** (trusted **`main`** signing + validation + root publish), matching **`intentproof-api`** and the Node SDK layout.
1010

1111
## 0.1.3 — 2026-05-08
1212

CONTRIBUTING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ tox run -e static
1010
tox run -e cov
1111
```
1212

13+
- **Spec Conformance** (`.github/workflows/spec-conformance.yml`): pull requests and `workflow_dispatch`**`tox run -e static,cov`**, then **`scripts/spec-conformance.sh`**; uploads a conformance report artifact (no signing secrets).
14+
- **Conformance Attestation** (`.github/workflows/conformance-attestation.yml`): trusted runs on **`main`**/**`master`** — same shape as **`intentproof-api`**: PEM secret checks, signed certificate, validation, artifact upload, optional cert-bot commit of root conformance JSON.
15+
1316
PyPI releases use **`release.yml`** (trusted publisher / OIDC). For undisclosed security issues, use [**Security**](https://github.com/IntentProof/intentproof-sdk-python/security) advisories.

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,9 @@ Custom **`body`** serializers: if **`body(event)`** raises, **`HttpExporter`** n
347347

348348
- **Version pin:** **`[tool.intentproof].spec-version`** and **`spec-commit`** in **`pyproject.toml`** match **`spec.json`** and the spec **`HEAD`** checkout; **`scripts/check-consumer-spec-pin.sh`** delegates to **`intentproof-spec`** **`scripts/check-consumer-spec-pins.sh`** before conformance.
349349

350-
- **CI:** every push/PR checks out this SDK plus **`intentproof-spec`** and runs **`scripts/spec-conformance.sh`** (pin check + full oracle; see `.github/workflows/ci.yml`). The **`spec-golden-parity`** job runs **`tests/unit/test_spec_golden_conformance.py`** against the same **`golden/execution_event_cases.jsonl`** using **`jsonschema`** + semantics mirrored from the spec (`tests/spec_semantics.py`).
351-
- **Repo-root certificates:** each run uploads **`conformance-report.json`** and **`conformance-certificate.json`** as workflow artifacts; after a green default-branch push, the conformance GitHub App commits the same files at the repo root when they differ from **`main`**.
350+
- **CI:** `.github/workflows/ci.yml` runs hardening, golden parity, audit, and the **`tox`** matrix against a pinned **`intentproof-spec`** checkout.
351+
- **Spec conformance (PR):** `.github/workflows/spec-conformance.yml` runs **`tox run -e static,cov`** then **`scripts/spec-conformance.sh`** and uploads **`conformance-report.json`** (committed spec public key path; no signing secrets on PRs). The **`spec-golden-parity`** job in CI runs **`tests/unit/test_spec_golden_conformance.py`** against **`golden/execution_event_cases.jsonl`** using **`jsonschema`** + semantics mirrored from the spec (`tests/spec_semantics.py`).
352+
- **Trusted attestation (`main`):** `.github/workflows/conformance-attestation.yml` follows **`intentproof-api`**: tox gates, signed oracle output, **`npm run validate:conformance-certificate`** in the spec checkout, **`conformance-artifacts`** upload, and cert-bot publish of root **`conformance-certificate.json`** / **`conformance-report.json`** when they change.
352353
- **Local:** clone `intentproof-spec` **next to** this repository (`../intentproof-spec`), then:
353354

354355
```bash

0 commit comments

Comments
 (0)