Skip to content

Commit 122ebfa

Browse files
frawarecursoragent
andcommitted
Add pcs-bench producer gate with release-grade reproducibility ingest.
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent fcb3bd6 commit 122ebfa

30 files changed

Lines changed: 1368 additions & 63 deletions

.github/workflows/pcs.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@ on:
1616
- "policy/schemas/pcs/RegenerationReport.v0.schema.json"
1717
- "policy/schemas/pcs/FailureCaseManifest.v0.schema.json"
1818
- "policy/schemas/pcs/BenchmarkCase.v0.schema.json"
19+
- "policy/schemas/pcs/BenchmarkArtifactRef.v0.schema.json"
20+
- "policy/schemas/pcs/ReproducibilityBenchmarkManifest.v0.schema.json"
1921
- "docs/labtrust-benchmark-profile.md"
2022
- "docs/reference-workflow-template.md"
2123
- "schemas_or_docs/**"
2224
- "templates/pcs_workflow_template/**"
25+
- "Makefile"
26+
- "scripts/pcs_bench_producer.*"
27+
- "scripts/generate_pcs_bench_ingest_fixture.py"
28+
- "tests/fixtures/pcs_bench_ingest/**"
2329
- ".github/workflows/pcs.yml"
2430
pull_request:
2531
branches: [main]
@@ -36,10 +42,16 @@ on:
3642
- "policy/schemas/pcs/RegenerationReport.v0.schema.json"
3743
- "policy/schemas/pcs/FailureCaseManifest.v0.schema.json"
3844
- "policy/schemas/pcs/BenchmarkCase.v0.schema.json"
45+
- "policy/schemas/pcs/BenchmarkArtifactRef.v0.schema.json"
46+
- "policy/schemas/pcs/ReproducibilityBenchmarkManifest.v0.schema.json"
3947
- "docs/labtrust-benchmark-profile.md"
4048
- "docs/reference-workflow-template.md"
4149
- "schemas_or_docs/**"
4250
- "templates/pcs_workflow_template/**"
51+
- "Makefile"
52+
- "scripts/pcs_bench_producer.*"
53+
- "scripts/generate_pcs_bench_ingest_fixture.py"
54+
- "tests/fixtures/pcs_bench_ingest/**"
4355
- ".github/workflows/pcs.yml"
4456

4557
jobs:
@@ -124,6 +136,8 @@ jobs:
124136
run: python examples/pcs_qc_release/scripts/ci_benchmark_reproducibility.py
125137
- name: Reproducibility ingest (PcsBenchIngest.v0)
126138
run: python examples/pcs_qc_release/scripts/ci_validate_benchmark_ingest_golden.py
139+
- name: Offline pcs-bench ingest fixture (producer gate)
140+
run: python examples/pcs_qc_release/scripts/ci_validate_pcs_bench_ingest_fixture.py
127141
- name: Benchmark packet fixtures
128142
run: python examples/pcs_qc_release/scripts/generate_benchmark_packet.py
129143
- name: Verify LabTrust release protocol package

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ audit_self_check/
133133
# Local reproducibility scratch (not committed)
134134
_tmp_repro/
135135
_tmp_repro2/
136+
_tmp_repro_*/
137+
_tmp_suite_test/
138+
benchmark_runs/
139+
reports/
136140

137141
# OS
138142
.DS_Store

Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# make verify: full verification battery (lint, typecheck, policy, tests, risk-register gate, docs).
44
# make paper OUT=<dir>: package-release paper_v0.1 then verify-release (requires OUT= output dir).
55

6-
.PHONY: test golden bench-smoke lint format typecheck policy-validate no-placeholders e2e-artifacts-chain verification-battery verify paper
6+
.PHONY: test golden bench-smoke lint format typecheck policy-validate no-placeholders e2e-artifacts-chain verification-battery verify paper pcs-bench-producer
77

88
# Default: run fast test suite (no env optional deps for golden/policy)
99
test:
@@ -57,3 +57,11 @@ paper:
5757
@if [ -z "$${OUT}" ]; then echo "OUT is required (e.g. make paper OUT=./paper_release)"; exit 1; fi; \
5858
labtrust package-release --profile paper_v0.1 --out "$$OUT" --seed-base 100 && \
5959
labtrust verify-release --release-dir "$$OUT" --strict-fingerprints
60+
61+
# Release-grade reproducibility producer + pcs-bench ingest validation (requires ../pcs-core).
62+
PCS_CORE ?= ../pcs-core
63+
PCS_BENCH ?= ../pcs-bench
64+
BENCH_RUN_DIR ?= benchmark_runs/labtrust_reproducibility
65+
66+
pcs-bench-producer:
67+
python scripts/pcs_bench_producer.py

docs/contracts/cli_contract.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ This document defines the contract for all LabTrust-Gym CLI commands: exit codes
7373
| regenerate-release-chain | alias for `regenerate-release-protocol` | 0 | same as above | examples/pcs_qc_release/RUNBOOK.md |
7474
| generate-failure-gallery | `--workflow <id\|property_id>`; `--out <dir>`; optional `--release-dir`; `--workflow-profile` | 0 | Per-case `failure_case_manifest.json`, `README.md`, `artifacts/`, legacy `expected_failure.json` / `repair_hint.json` | examples/pcs_qc_release/failures/ |
7575
| generate-benchmark-cases | `--workflow <id\|property_id>`; `--out <dir>`; `--seed` (default 42); optional `--release-dir`, `--workflow-profile`; `--pcs-bench-layout`; `--suite-fixture-root`; `--validate-pcs-core-output <pcs-core>`; `--pcs-core-registry <path>` | 0 | Flat: `benchmark_index.json` + per-case dirs. pcs-bench: `suite.yaml`, `benchmark_manifest.v0.json`, `valid/`, `invalid/`, `coverage_report.v0.json` | docs/labtrust-benchmark-profile.md |
76-
| benchmark-reproducibility | `--workflow` (default hospital_lab.qc_release); `--out <dir>`; `--runs` (default 5); `--seed`; `--mode full_regeneration` (default) or `hash_stability`; optional `--pcs-core`, `--release-dir`, `--certifyedge-bin`, `--validate-pcs-core-output <pcs-core>` | 0 | `benchmark_run.v0.json`, `coverage_report.v0.json`, `benchmark_manifest.v0.json`, `benchmark_report.v0.json`, `regeneration_reports/`, `hash_stability_report.v0.json`, `pcs_bench_ingest.v0.json` | docs/labtrust-benchmark-profile.md |
76+
| benchmark-reproducibility | `--workflow` (default hospital_lab.qc_release; aliases e.g. `qc-release` resolve to canonical `hospital_lab.qc_release`); `--out <dir>`; `--runs` (default 5); `--seed`; `--mode full_regeneration` (default) or `hash_stability`; optional `--pcs-core`, `--release-dir`, `--certifyedge-bin`, `--validate-pcs-core-output <pcs-core>`; `--release-grade` / `--no-release-grade` (default: release when mode is `full_regeneration`) | 0 | `benchmark_run.v0.json`, `coverage_report.v0.json`, `benchmark_manifest.v0.json` (`evidence_grade`, `certifyedge_live`, `pcs_core_validation`), `benchmark_report.v0.json`, `regeneration_reports/`, `hash_stability_report.v0.json`, `pcs_bench_ingest.v0.json` (embedded runs + `artifact_refs` for all sidecars) | docs/pcs-benchmark-producer.md |
7777
| verify-benchmark-cases | `--benchmark-dir <dir>`; optional `--pcs-core` (auto-detect); `--validate-pcs-core-output <pcs-core>`; `--json` | 0 | (stderr OK labels) | docs/labtrust-benchmark-profile.md |
7878

7979
**PCS CI scripts (not `labtrust` subcommands):** `run_pcs_ci_local.{sh,ps1}` runs `pytest tests/pcs`, `ci_validate_workflow_profile.py`, `ci_validate_formalization.py`, export/release validation, `check-status-policy`, failure gallery generate/verify, and optional `verify-release-protocol` (matches `.github/workflows/pcs.yml`). `materialize_workflow_profile.py` refreshes `workflow_profile.v0.json` digest. `materialize_formalization_artifacts.py` rebuilds formalization JSON from an existing `release/` tree. `generate_golden.py` regenerates `expected/` in deterministic mode.

docs/pcs-benchmark-producer.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ python examples/pcs_qc_release/scripts/generate_pcs_bench_suite.py \
3737

3838
## Reproducibility ingest (pcs-bench runs)
3939

40+
```bash
41+
make pcs-bench-producer
42+
# Windows: .\scripts\pcs_bench_producer.ps1
43+
# Cross-platform: python scripts/pcs_bench_producer.py
44+
```
45+
46+
Equivalent manual flow:
47+
4048
```bash
4149
labtrust benchmark-reproducibility \
4250
--workflow hospital_lab.qc_release \
@@ -45,15 +53,30 @@ labtrust benchmark-reproducibility \
4553
--certifyedge-bin certifyedge \
4654
--runs 5 \
4755
--out benchmark_runs/labtrust_reproducibility \
48-
--validate-pcs-core-output ../pcs-core
56+
--validate-pcs-core-output ../pcs-core \
57+
--release-grade
4958

59+
pcs-bench validate-ingest \
60+
--input benchmark_runs/labtrust_reproducibility/pcs_bench_ingest.v0.json \
61+
--pcs-core ../pcs-core
62+
```
63+
64+
Release-grade mode (`evidence_grade: release` on `benchmark_manifest.v0.json`) requires
65+
`full_regeneration`, certifyedge success rate 1.0, and per-run pcs-core / release-protocol /
66+
status-policy validation. `workflow_id` is always canonical (`hospital_lab.qc_release`), even
67+
when `--workflow` uses a short alias such as `qc-release`.
68+
69+
```bash
5070
python examples/pcs_qc_release/scripts/publish_reproducibility_ingest.py \
5171
--out benchmark_runs/labtrust_reproducibility \
5272
--pcs-core ../pcs-core
5373
```
5474

5575
`pcs_bench_ingest.v0.json` embeds pcs-core `BenchmarkRun.v0` (one per iteration) and
56-
`CoverageReport.v0` (`release_reproducibility_score`). Companion files:
76+
`CoverageReport.v0` (`release_reproducibility_score`), with pcs-core-compatible
77+
`artifact_refs` (one ref per embedded object under `artifact_refs/`). LabTrust-extended
78+
refs (aggregate run, report, manifest, hash stability, regeneration reports) are in
79+
`benchmark_artifact_refs.labtrust.v0.json`. Companion files:
5780

5881
- `benchmark_run.v0.json` — LabTrust aggregate multi-run summary
5982
- `benchmark_report.v0.json` — pcs-core suite report with `metric_summaries`
@@ -70,6 +93,7 @@ python examples/pcs_qc_release/scripts/publish_reproducibility_ingest.py \
7093
| Path | Purpose |
7194
|------|---------|
7295
| `examples/pcs_qc_release/benchmark_packet/` | Two-case smoke packet |
96+
| `tests/fixtures/pcs_bench_ingest/labtrust/pcs_bench_ingest.v0.json` | Offline pcs-bench producer gate (regenerate: `python scripts/generate_pcs_bench_ingest_fixture.py`) |
7397
| `examples/pcs_qc_release/benchmark_ingest/golden/` | Optional committed ingest golden (regenerate via `materialize_benchmark_ingest_golden.py`) |
7498

7599
See also [labtrust-benchmark-profile.md](labtrust-benchmark-profile.md).

examples/pcs_qc_release/RUNBOOK.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ python examples/pcs_qc_release/scripts/ci_validate_benchmark_registry_expected.p
331331
# Minimal two-case packet for pcs-bench smoke (no full gallery):
332332
python examples/pcs_qc_release/scripts/generate_benchmark_packet.py
333333

334+
# Release-grade producer + pcs-bench ingest validation (from repo root):
335+
make pcs-bench-producer
336+
337+
# Or manual equivalent:
334338
labtrust benchmark-reproducibility \
335339
--workflow hospital_lab.qc_release \
336340
--pcs-core ../pcs-core \
@@ -339,7 +343,11 @@ labtrust benchmark-reproducibility \
339343
--mode full_regeneration \
340344
--out benchmark_runs/labtrust_reproducibility \
341345
--seed 42 \
342-
--validate-pcs-core-output ../pcs-core
346+
--validate-pcs-core-output ../pcs-core \
347+
--release-grade
348+
349+
# Offline ingest fixture gate (no certifyedge):
350+
python examples/pcs_qc_release/scripts/ci_validate_pcs_bench_ingest_fixture.py
343351

344352
# Publish ingest for pcs-bench (sibling repo):
345353
python examples/pcs_qc_release/scripts/publish_reproducibility_ingest.py \

examples/pcs_qc_release/scripts/ci_benchmark_reproducibility.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from __future__ import annotations
55

6+
import json
67
import sys
78
import tempfile
89
from pathlib import Path
@@ -79,12 +80,17 @@ def main() -> int:
7980
):
8081
if not (out / name).is_file():
8182
raise FileNotFoundError(f"missing reproducibility output: {name}")
83+
manifest = json.loads((out / BENCHMARK_MANIFEST_NAME).read_text(encoding="utf-8"))
84+
if manifest.get("evidence_grade") != "developer":
85+
raise SystemExit("CI hash_stability run must emit developer evidence_grade")
8286
if pcs_core_root is not None:
8387
from labtrust_gym.pcs.bench_schemas import validate_pcs_core_reproducibility_outputs
8488

85-
validate_pcs_core_reproducibility_outputs(
89+
checks = validate_pcs_core_reproducibility_outputs(
8690
out, pcs_core_root=pcs_core_root, policy_root=ROOT
8791
)
92+
for label in checks:
93+
print(f" OK {label}")
8894
print("reproducibility benchmark CI OK")
8995
return 0
9096

examples/pcs_qc_release/scripts/ci_validate_benchmark_ingest_golden.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
validate_pcs_core_reproducibility_outputs,
1616
)
1717
from labtrust_gym.pcs.benchmark_reproducibility import benchmark_reproducibility
18+
from labtrust_gym.pcs.workflow_profile import CANONICAL_QC_RELEASE_WORKFLOW_ID
1819

1920
GOLDEN = ROOT / "examples" / "pcs_qc_release" / "benchmark_ingest" / "golden"
2021

@@ -43,6 +44,22 @@ def main() -> int:
4344
checks = validate_pcs_core_reproducibility_outputs(
4445
out, pcs_core_root=pcs_core, policy_root=ROOT
4546
)
47+
import json
48+
49+
from labtrust_gym.pcs.benchmark_pcs_bench_ingest import PCS_BENCH_INGEST_NAME
50+
51+
ingest = json.loads((out / PCS_BENCH_INGEST_NAME).read_text(encoding="utf-8"))
52+
if ingest["workflow_id"] != CANONICAL_QC_RELEASE_WORKFLOW_ID:
53+
raise SystemExit(
54+
f"ingest workflow_id must be {CANONICAL_QC_RELEASE_WORKFLOW_ID!r}"
55+
)
56+
n_runs = len(ingest.get("benchmark_runs") or [])
57+
n_cov = len(ingest.get("coverage_reports") or [])
58+
if len(ingest.get("artifact_refs") or []) < n_runs + n_cov:
59+
raise SystemExit("ingest must include pcs-core artifact_refs per embedded run/coverage")
60+
manifest = json.loads((out / "benchmark_manifest.v0.json").read_text(encoding="utf-8"))
61+
if manifest.get("evidence_grade") != "developer":
62+
raise SystemExit("hash_stability CI run must use developer evidence_grade")
4663
for label in checks:
4764
print(f" OK {label}")
4865

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env python3
2+
"""CI: validate committed offline PcsBenchIngest.v0 fixture (pcs-bench producer gate)."""
3+
4+
from __future__ import annotations
5+
6+
import json
7+
import sys
8+
from pathlib import Path
9+
10+
ROOT = Path(__file__).resolve().parents[3]
11+
sys.path.insert(0, str(ROOT / "src"))
12+
13+
from labtrust_gym.pcs.bench_schemas import (
14+
resolve_pcs_core_schema_root,
15+
validate_producer_ingest_contract,
16+
)
17+
18+
FIXTURE = ROOT / "tests" / "fixtures" / "pcs_bench_ingest" / "labtrust" / "pcs_bench_ingest.v0.json"
19+
20+
21+
def main() -> int:
22+
if not FIXTURE.is_file():
23+
print(f"FAIL missing fixture: {FIXTURE}", file=sys.stderr)
24+
return 1
25+
26+
doc = json.loads(FIXTURE.read_text(encoding="utf-8"))
27+
pcs_core = resolve_pcs_core_schema_root(ROOT.parent / "pcs-core")
28+
checks = validate_producer_ingest_contract(
29+
doc,
30+
ingest_path=FIXTURE,
31+
policy_root=ROOT,
32+
pcs_core_root=pcs_core,
33+
)
34+
for label in checks:
35+
print(f" OK {label}")
36+
print(f"pcs-bench ingest fixture OK ({FIXTURE.relative_to(ROOT)})")
37+
return 0
38+
39+
40+
if __name__ == "__main__":
41+
raise SystemExit(main())

examples/pcs_qc_release/scripts/run_pcs_ci_local.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
4444
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
4545
& $Python examples/pcs_qc_release/scripts/ci_validate_benchmark_ingest_golden.py
4646
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
47+
& $Python examples/pcs_qc_release/scripts/ci_validate_pcs_bench_ingest_fixture.py
48+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
4749

4850
& $Labtrust check-status-policy --release-dir examples/pcs_qc_release/release
4951
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

0 commit comments

Comments
 (0)