Skip to content

Commit 6eadb8a

Browse files
authored
Merge pull request #6 from negillett/hardening
feat: enforce generated models and release hardening
2 parents c8f96f2 + 81c6dee commit 6eadb8a

9 files changed

Lines changed: 130 additions & 45 deletions

File tree

.github/actions/setup-python-pip/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ inputs:
1414
runs:
1515
using: composite
1616
steps:
17-
- uses: actions/setup-python@v5
17+
- uses: actions/setup-python@v6
1818
with:
1919
python-version: ${{ inputs.python-version }}
2020
check-latest: ${{ inputs.check-latest == 'true' }}

.github/workflows/ci.yml

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ name: CI
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [main, master]
66
pull_request:
77

88
concurrency:
9-
group: ci-${{ github.workflow }}-${{ github.ref }}
9+
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
1010
cancel-in-progress: true
1111

1212
jobs:
@@ -16,9 +16,24 @@ jobs:
1616
steps:
1717
- uses: actions/checkout@v6
1818

19+
- name: Resolve pinned intentproof-spec revision
20+
id: spec_pin
21+
run: |
22+
set -euo pipefail
23+
python3 - <<'PY'
24+
import os
25+
import tomllib
26+
from pathlib import Path
27+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
28+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
29+
f.write(f"sha={c}\n")
30+
PY
31+
1932
- uses: actions/checkout@v6
2033
with:
2134
repository: IntentProof/intentproof-spec
35+
ref: ${{ steps.spec_pin.outputs.sha }}
36+
fetch-depth: 0
2237
path: intentproof-spec
2338

2439
- name: No handwritten model/types outside generated schema package
@@ -32,9 +47,24 @@ jobs:
3247
steps:
3348
- uses: actions/checkout@v6
3449

50+
- name: Resolve pinned intentproof-spec revision
51+
id: spec_pin
52+
run: |
53+
set -euo pipefail
54+
python3 - <<'PY'
55+
import os
56+
import tomllib
57+
from pathlib import Path
58+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
59+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
60+
f.write(f"sha={c}\n")
61+
PY
62+
3563
- uses: actions/checkout@v6
3664
with:
3765
repository: IntentProof/intentproof-spec
66+
ref: ${{ steps.spec_pin.outputs.sha }}
67+
fetch-depth: 0
3868
path: intentproof-spec
3969

4070
- uses: actions/setup-node@v6
@@ -52,9 +82,24 @@ jobs:
5282
steps:
5383
- uses: actions/checkout@v6
5484

85+
- name: Resolve pinned intentproof-spec revision
86+
id: spec_pin
87+
run: |
88+
set -euo pipefail
89+
python3 - <<'PY'
90+
import os
91+
import tomllib
92+
from pathlib import Path
93+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
94+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
95+
f.write(f"sha={c}\n")
96+
PY
97+
5598
- uses: actions/checkout@v6
5699
with:
57100
repository: IntentProof/intentproof-spec
101+
ref: ${{ steps.spec_pin.outputs.sha }}
102+
fetch-depth: 0
58103
path: intentproof-spec
59104

60105
- uses: actions/setup-node@v6
@@ -72,7 +117,7 @@ jobs:
72117
run: bash scripts/spec-conformance.sh
73118

74119
- name: Upload conformance report artifact
75-
uses: actions/upload-artifact@v4
120+
uses: actions/upload-artifact@v7
76121
with:
77122
name: conformance-report-python
78123
path: intentproof-spec/conformance-report.json
@@ -84,9 +129,24 @@ jobs:
84129
steps:
85130
- uses: actions/checkout@v6
86131

132+
- name: Resolve pinned intentproof-spec revision
133+
id: spec_pin
134+
run: |
135+
set -euo pipefail
136+
python3 - <<'PY'
137+
import os
138+
import tomllib
139+
from pathlib import Path
140+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
141+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
142+
f.write(f"sha={c}\n")
143+
PY
144+
87145
- uses: actions/checkout@v6
88146
with:
89147
repository: IntentProof/intentproof-spec
148+
ref: ${{ steps.spec_pin.outputs.sha }}
149+
fetch-depth: 0
90150
path: intentproof-spec
91151

92152
- uses: ./.github/actions/setup-python-pip
@@ -145,9 +205,24 @@ jobs:
145205
steps:
146206
- uses: actions/checkout@v6
147207

208+
- name: Resolve pinned intentproof-spec revision
209+
id: spec_pin
210+
run: |
211+
set -euo pipefail
212+
python3 - <<'PY'
213+
import os
214+
import tomllib
215+
from pathlib import Path
216+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
217+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
218+
f.write(f"sha={c}\n")
219+
PY
220+
148221
- uses: actions/checkout@v6
149222
with:
150223
repository: IntentProof/intentproof-spec
224+
ref: ${{ steps.spec_pin.outputs.sha }}
225+
fetch-depth: 0
151226
path: intentproof-spec
152227

153228
- uses: ./.github/actions/setup-python-pip

.github/workflows/release.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,24 @@ jobs:
7272
steps:
7373
- uses: actions/checkout@v6
7474

75+
- name: Resolve pinned intentproof-spec revision
76+
id: spec_pin
77+
run: |
78+
set -euo pipefail
79+
python3 - <<'PY'
80+
import os
81+
import tomllib
82+
from pathlib import Path
83+
c = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["tool"]["intentproof"]["spec-commit"]
84+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
85+
f.write(f"sha={c}\n")
86+
PY
87+
7588
- uses: actions/checkout@v6
7689
with:
7790
repository: IntentProof/intentproof-spec
91+
ref: ${{ steps.spec_pin.outputs.sha }}
92+
fetch-depth: 0
7893
path: intentproof-spec
7994

8095
- uses: ./.github/actions/setup-python-pip

CHANGELOG.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,13 @@ All notable changes to this repository are documented here. **PyPI** releases us
66

77
## Unreleased
88

9-
- Add spec-driven generated models package under `src/intentproof/generated` (`execution_event`, `intentproof_config`, `normative_schemas`, and package exports) and switch SDK type aliases/wire paths to these generated schema models.
10-
- Add schema validation helpers in `src/intentproof/schema_validate.py` for execution events, wrap options, and runtime config; export them from `intentproof.__init__`.
11-
- Add deterministic codegen pipeline (`scripts/generate_schema_models.py`) that writes embedded normative schemas plus `src/intentproof/generated/spec_fingerprint.json` (spec version + per-schema/aggregate SHA-256 digests).
12-
- Add generated artifact drift checks (`scripts/verify-generated-types.sh`) and bundled schema guard (`scripts/check-no-bundled-schema.sh`) for release hygiene.
13-
- Add explicit no-handwritten-model policy enforcement (`scripts/check-no-handwritten-model-types.sh`) by delegating to the shared `intentproof-spec` checker, with a dedicated CI check-run (`no-handwritten-model-types`), the same step in **`tox -e static`** and release publish (parity with Node/Java), and release preflight gating on that check.
14-
- Strengthen CI/release hardening: add `hardening` job, enforce required check-runs in release preflight, and upload canonical conformance report artifact (`conformance-report-python`).
15-
- Improve spec-conformance wrapper (`scripts/spec-conformance.sh`) with strict spec pin verification (`scripts/check-sdk-spec-pin.sh`), standardized report metadata (`INTENTPROOF_SDK_NAME`, `INTENTPROOF_SDK_LANGUAGE`, `INTENTPROOF_SDK_VERSION`), and `./intentproof-spec` fallback when the env var and sibling clone are absent.
16-
- Pin `datamodel-code-generator` in `pyproject.toml` for stable code generation and make generated headers deterministic (no timestamp/random temp filenames).
17-
- Add/expand tests for spec semantics and generated artifacts: `tests/spec_semantics.py`, `tests/unit/test_spec_golden_conformance.py`, `tests/unit/test_schema_validate.py`, `tests/unit/test_generated_fingerprint.py`, with related SDK/exporter/wire test updates.
18-
- Update docs (`README.md`) and local quality wiring (`tox.ini`) to reflect spec-first model generation, dedicated policy gating, and deduplicated CI enforcement.
19-
- CI: **`no-handwritten-model-types`** job only checks out the SDK + spec and runs the delegated script (no **`pip install`**); **push** triggers also include **`master`** (parity with Node/Java).
9+
- **Spec-generated wire models:** Add `src/intentproof/generated` (Pydantic models from intentproof-spec JSON Schemas, embedded normative schema dicts for `jsonschema`, and `spec_fingerprint.json` with spec version plus per-schema and aggregate SHA-256). Route SDK types and wire serialization through these models; add `schema_validate` helpers for execution events, wrap options, and runtime config and export them from `intentproof`.
10+
- **Codegen and drift guards:** Add `scripts/generate_schema_models.py` with pinned `datamodel-code-generator` and deterministic generation; add `scripts/verify-generated-types.sh`, `scripts/check-no-bundled-schema.sh`, and `scripts/check-sdk-spec-pin.sh` enforcing `[tool.intentproof]` `spec-version` and `spec-commit` against the spec checkout.
11+
- **No handwritten schema models:** `scripts/check-no-handwritten-model-types.sh` delegates to `intentproof-spec`’s shared checker (requires a spec checkout via `INTENTPROOF_SPEC_ROOT`, sibling `../intentproof-spec`, or `./intentproof-spec`). Wired into **`tox -e static`**, a dedicated CI job (`no-handwritten-model-types`) that checks out the pinned spec revision without installing the SDK, the Release workflow before build, and release preflight required check-runs.
12+
- **CI and release:** Checkout intentproof-spec at **`pyproject.toml`** `spec-commit` across conformance and policy jobs; add **`hardening`** and **`spec-golden-parity`**; upload conformance report artifact **`conformance-report-python`**; run conformance with replay/metadata env vars; extend **push** triggers to **`master`**; release preflight requires **`no-handwritten-model-types`**, **`hardening`**, **`intentproof-spec`**, **`spec-golden-parity`**, and **`tox`** static/cov matrix successes.
13+
- **Conformance wrapper:** `scripts/spec-conformance.sh` gains spec-pin integration, standardized SDK report metadata (`INTENTPROOF_SDK_NAME`, `INTENTPROOF_SDK_LANGUAGE`, `INTENTPROOF_SDK_VERSION`), and clearer resolution when the spec checkout path is not set.
14+
- **Tests and docs:** Add golden JSONL conformance, spec semantics mirroring, fingerprint validation, schema validation tests, and updates to SDK/exporter/wire tests; refresh **`README.md`** and **`tox.ini`** for the spec-first workflow.
15+
2016
## 0.1.1 — 2026-05-04
2117

2218
- **Security:** upgrade **pip** to **≥26.1** after Python setup in the composite **`.github/actions/setup-python-pip`** action, and in the default **`[testenv]`** via **`commands_pre`**, addressing **CVE-2026-3219** and **CVE-2026-6357**.

CONTRIBUTING.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Contributing
2+
3+
Cross-repository **pins**, **`INTENTPROOF_*`** environment variables, and script naming are documented in the **[`intentproof-spec` CONTRIBUTING guide](https://github.com/IntentProof/intentproof-spec/blob/main/CONTRIBUTING.md#terminology-shared-with-sdk-repos)**.
4+
5+
Typical local checks (see **`tox.ini`**):
6+
7+
```bash
8+
pip install "tox>=4"
9+
tox run -e static
10+
tox run -e cov
11+
```
12+
13+
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: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Ordinary telemetry shows that *something ran*. It rarely ships an **auditable st
4444
**Package:** `intentproof-sdk` (PyPI).
4545

4646
- [PyPI — `intentproof-sdk`](https://pypi.org/project/intentproof-sdk/)
47-
- [GitHub Releases — IntentProof Python SDK](https://github.com/intentproof/intentproof-sdk-python/releases)
47+
- [GitHub Releases — IntentProof Python SDK](https://github.com/IntentProof/intentproof-sdk-python/releases)
4848

4949
Pin the **version** you want from PyPI or from GitHub Releases. Replace **`x.y.z`** below with that version.
5050

@@ -323,7 +323,7 @@ run_probe()
323323

324324
## Security
325325

326-
For **vulnerability reporting**, use this repository’s [**Security**](https://github.com/intentproof/intentproof-sdk-python/security) tab (private advisories).
326+
For **vulnerability reporting**, use this repository’s [**Security**](https://github.com/IntentProof/intentproof-sdk-python/security) tab (private advisories).
327327

328328
Every **`ExecutionEvent`** you emit is data you may ship off-process. Treat them like audit-grade execution records: they can include PII, secrets, stack traces, and business identifiers depending on your **`snapshot`** / **`capture_*`** hooks.
329329

@@ -340,9 +340,11 @@ Custom **`body`** serializers: if **`body(event)`** raises, **`HttpExporter`** n
340340

341341
## Canonical specification (`intentproof-spec`)
342342

343-
Schemas, golden oracles, and the **Vitest conformance oracle** live in the **[IntentProof specification repository (`intentproof-spec`)](https://github.com/intentproof/intentproof-spec)**.
343+
**Shared pins and terminology** (`INTENTPROOF_SPEC_ROOT`, **`spec-commit`**, script names): **[`intentproof-spec` CONTRIBUTING — Terminology](https://github.com/IntentProof/intentproof-spec/blob/main/CONTRIBUTING.md#terminology-shared-with-sdk-repos)**.
344344

345-
- **Version pin:** **`[tool.intentproof].spec-version`** in **`pyproject.toml`** matches **`spec.json`** in that repo; **`scripts/check-sdk-spec-pin.sh`** enforces it before conformance.
345+
Schemas, golden oracles, and the **Vitest conformance oracle** live in the **[IntentProof specification repository (`intentproof-spec`)](https://github.com/IntentProof/intentproof-spec)**.
346+
347+
- **Version pin:** **`[tool.intentproof].spec-version`** and **`spec-commit`** in **`pyproject.toml`** match **`spec.json`** and the spec **`HEAD`** checkout; **`scripts/check-sdk-spec-pin.sh`** enforces this before conformance.
346348

347349
- **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`).
348350
- **Local:** clone `intentproof-spec` **next to** this repository (`../intentproof-spec`), then:
@@ -365,6 +367,8 @@ Schemas, golden oracles, and the **Vitest conformance oracle** live in the **[In
365367

366368
## Project development
367369

370+
Contributing and shared **`intentproof-spec`** terminology: **[`CONTRIBUTING.md`](CONTRIBUTING.md)**.
371+
368372
Layout: **`src/`** tree, built with **Hatchling** ([Hatch](https://github.com/pypa/hatch); **`build-backend = "hatchling.build"`** in `pyproject.toml`). Requires **Python** 3.11 or newer. Release history: [`CHANGELOG.md`](CHANGELOG.md).
369373

370374
Checks run via **[tox](https://tox.wiki/)** (`tox.ini`): **`static`** runs **ruff** (format + lint); **`cov`** runs **pytest** with **pytest-cov** and enforces **100%** line coverage; **`py311`****`py314`** install the package with **`dev`** extras and run **pytest**. CI matches this.

pyproject.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ dev = [
4545
]
4646

4747
[tool.intentproof]
48-
# Must match https://github.com/intentproof/intentproof-spec/blob/main/spec.json — enforced by scripts/check-sdk-spec-pin.sh
48+
# Must match vendored intentproof-spec spec.json + git HEAD — enforced by scripts/check-sdk-spec-pin.sh
4949
spec-version = "spec-v1.0.0"
50+
spec-commit = "c712a84f6bea82bd57448175e648cf4ba492d844"
5051

5152
[project.urls]
52-
Homepage = "https://github.com/intentproof/intentproof-sdk-python"
53-
Repository = "https://github.com/intentproof/intentproof-sdk-python"
54-
Issues = "https://github.com/intentproof/intentproof-sdk-python/issues"
53+
Homepage = "https://github.com/IntentProof/intentproof-sdk-python"
54+
Repository = "https://github.com/IntentProof/intentproof-sdk-python"
55+
Issues = "https://github.com/IntentProof/intentproof-sdk-python/issues"
5556

5657
# --- Hatchling (wheel & sdist) -------------------------------------------------
5758

scripts/check-sdk-spec-pin.sh

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,9 @@
11
#!/usr/bin/env bash
2-
# Fail if this SDK's declared IntentProof spec version does not match spec.json from the checkout.
2+
# Fail if this SDK's declared IntentProof spec version + commit pins do not match the spec checkout.
33
# Usage: check-sdk-spec-pin.sh /absolute/or/relative/path/to/intentproof-spec
44
set -euo pipefail
55

66
spec_root="$(cd "$1" && pwd)"
77
sdk_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
88

9-
if [[ ! -f "${spec_root}/spec.json" ]]; then
10-
echo "check-sdk-spec-pin: not a spec checkout (missing spec.json): ${spec_root}" >&2
11-
exit 2
12-
fi
13-
14-
spec_version="$(python3 -c "import json, pathlib, sys; print(json.loads(pathlib.Path(sys.argv[1]).read_text())['version'])" "${spec_root}/spec.json")"
15-
16-
declared="$(cd "$sdk_root" && python3 -c "
17-
import tomllib
18-
from pathlib import Path
19-
p = Path('pyproject.toml')
20-
data = tomllib.loads(p.read_text(encoding='utf-8'))
21-
print(data['tool']['intentproof']['spec-version'])
22-
")"
23-
24-
if [[ "$declared" != "$spec_version" ]]; then
25-
echo "check-sdk-spec-pin: pyproject [tool.intentproof] spec-version=${declared} but spec.json version=${spec_version}" >&2
26-
exit 1
27-
fi
28-
29-
echo "SDK spec pin OK (${spec_version})"
9+
exec bash "${spec_root}/scripts/check-sdk-spec-pins.sh" "${sdk_root}" "${spec_root}"

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ commands =
2121
extras =
2222
dev
2323
commands =
24+
bash {toxinidir}/scripts/check-sdk-spec-pin.sh {env:INTENTPROOF_SPEC_ROOT}
2425
bash {toxinidir}/scripts/check-no-bundled-schema.sh
2526
bash {toxinidir}/scripts/check-no-handwritten-model-types.sh
2627
bash {toxinidir}/scripts/verify-generated-types.sh

0 commit comments

Comments
 (0)