Repository navigation
CI: install the codegen docs-only gate, with a same-repository guard for forks - #175
Conversation
…for forks Installs the five templates of pineforge-workflow's codegen docs-only gate (workflow #102, with the guard and review fixes of workflow #106 at 8cbd5c6), byte for byte: campaign/ci/codegen-gate.yml -> .github/workflows/gate.yml campaign/ci/codegen-test.yml -> .github/workflows/test.yml campaign/ci/codegen-docs-check.mjs -> .github/pineforge/ci/codegen-docs-check.mjs campaign/src/codegen-docs-rule.mjs -> .github/pineforge/src/codegen-docs-rule.mjs campaign/ci/promote-baseline.yml -> .github/workflows/promote-baseline.yml gate.yml runs on pull_request_target from main's definition: a classifier reads the PR head as data and runs only main's checker, an accepted documentation/legal-only diff skips the Pyodide differential gate and the pytest matrix, and a reporter that checks out nothing posts the required `gate` commit status on the exact head. The root README.md and LICENSE, which the build reads, count as documentation only as edits. test.yml runs on pushes to main only, once per commit, and skips a docs-only push. promote-baseline.yml reads the `gate` status and judges each check-run name by its newest run; its actions are pinned to full commit SHAs and its campaign-tooling checkout keeps no credentials. Every job is capped (10 minutes for the classifiers and the reporter, 30 for the heavy jobs). This repository is public and takes fork pull requests, so `differential` and `pytest`, the two jobs that run the PR head's code, run only when the event is not pull_request_target or github.event.pull_request.head.repo.full_name == github.repository. The reporter clears the full gate only for a same-repository head. A fork's PR that does not pass gets a failing `gate` whose description says a maintainer must push the branch here and open a PR from it; when a run of this repository already posted a `gate` success on the same commit, the fork's run leaves it and exits 0 instead of turning that PR red. A fork's docs-only proof runs no fork code and clears as a same-repository one does. Nightly and manual runs stay full. Dependabot groups its GitHub Actions bumps into one monthly PR: the workflows are the templates', so every bump also goes to pineforge-workflow and needs a measured pr-gate here. CONTRIBUTING.md says how CI treats pull requests from forks. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
Record for the CI install, added 2026-10-05 after it landed. Required-status pin. The ruleset on One-time bootstrap (done). This PR could not be gated by its own workflow: no definition on Live runs since the merge. #174 (README only) and #177 (LICENSE and Markdown): Still open: |
* docs: changelog and version scoping for 1.3.0 1.3.0 pairs only with engine v1.3.0. The changelog section takes in the Unreleased note and covers #173, #178, #179, #177, #174 and #175: the confirmed-bar receipt and what engine v1.3.0's live runner does with it, the capability receipt's request entries now read from the lowering, the order-shapes receipt, which no engine reads yet, the checked settings fix for string dropdowns with built-in constants (now supported=false where 1.2.0 wrote an integer code or published the input as supported), the PineForge Source License 1.2, and the pairing and migration steps. The Python and JSON contract is unchanged; the engine's report harness is the same file as in v1.2.0, so no report key changes. The README, AGENTS.md / CLAUDE.md, CONTRIBUTING.md, LEGAL.md, docs/PUBLIC_CONTRACT.md, docs/pine-cap-activation.md and npm/README.md name 1.3.0 where they named 1.2.0 as the current release or pair, the pairing table gets a 1.3.0 row, the "unreleased (after 1.2.0)" markers name 1.3.0, and the license badge reads 1.2. The release scoreboard sentence and the pf markers are left to the release lane. Measured for 1.3.0: the engine's 325 corpus sources and the 277 gate fixtures, each in a fresh process, transpile to 1.2.0's C++ byte for byte once the confirmed-bar and order-shapes functions are removed (13 refusals, same messages), with transpile_full results and transpile_json envelopes without cpp equal to 1.2.0's, each in under 0.1 s on CPython 3.14.6 on an Apple M4 Max. The tutorial run of the quick-start C++ against engine main 7a1f01c0 (Linux x86_64, GCC 13) prints 9 trades and +738.20, and 13 trades with the old sizing declared, as 1.2.0 with engine v1.2.0 does. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> * docs: release 1.3.0's grades in the README and the changelog, from the facts tokens The README's release sentence moves to the releases[1.3.0] tokens and its main-scoreboard lines re-render, and the 1.3.0 changelog says which registry baseline grades the release and why that baseline is the release's code, all rendered from pineforge-release facts/facts.json at ff21de0 (#32). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Installs the codegen documentation/legal-only gate whose basis landed in pineforge-workflow #102 (
codegen-docs-only/v1, the owner's 2026-09-30 rule: never sweep for docs). It adds a same-repository guard so a fork's code never runs in the base-context workflow, along with the fixes from the installation review. pineforge-workflow#106 carries the guard and the fixes to the templates, so template and install stay byte-equal.This is a configuration PR. CI files are not docs-only, so it carries the ordinary measured pr-gate. Against engine
b7ed0ee7(engine main after #342; this head carries #173), the result is PASS (no-improve-no-regression), recorded as receipt0be891353152e0b5379aa05d770ea318fccc25895b9e2f5ce64d45255b3ec06b, eventverdict-b7ed0ee7920b-5b596a42f269-33525ec0a983.The five files
Each file was copied with
git show <sha>:<path>; modes stay 100644.8cbd5c6)campaign/ci/codegen-gate.yml.github/workflows/gate.yml90ed96ccde348a1a41b8a259e0073f5de1f78aa25b9542afa0d7ba78ef4973d6839b523deb5a9ab7f4c10612933a864d40474ad592eed323178a947c3b3b3c7bcampaign/ci/codegen-test.yml.github/workflows/test.ymle5b0e9827084bb912a8856bca85d7e272501ebcc7fa38a03ea264b31789b90acc4644af79e7efd3ca5613c67bcb630f753fe7fa99025531599c8dd53c982057ccampaign/ci/codegen-docs-check.mjs.github/pineforge/ci/codegen-docs-check.mjs436a05bad672812b3d7972328687e2c674972ba0d9b489715cd763af5d3751c1436a05bad672812b3d7972328687e2c674972ba0d9b489715cd763af5d3751c1(unchanged)campaign/src/codegen-docs-rule.mjs.github/pineforge/src/codegen-docs-rule.mjs010ea1b6cb24fe7638ed2f7003e38c69384c40210a7c5ecc345c37aa86f6affd2e31a997cbf5c2c15eaa5f9e43a34eeaa0df012af49f9b8e019a57955df870b6campaign/ci/promote-baseline.yml.github/workflows/promote-baseline.yml2bcb68a54dd0643b6dcae107046130a507b819a3ea43865a84015b15e68c3a131d86d78a5d27f7603997cf951bd5b95b8307c6c0c6dd5f19083e164f6f201ba7What differs from #102's templates, all mirrored in #106:
gate.yml, below).promote-baseline.yml). The old check counted every failed or cancelled check-run on the head. So a stale red run of the same SHA skipped promotion silently: a fork's PR, an edit or a ready/reopen mid-run, a second manual run. Now only the newest run of each (app, name) counts.id-token: writeand the App key. Each is pinned to the commit its tag names today: checkout v4.4.0, create-github-app-token v1.12.0, setup-node v4.4.0, google-github-actions/auth v2.1.13, setup-gcloud v2.2.1. Its campaign-tooling checkout keeps no credentials (persist-credentials: false), so the App token is not on disk whennpm ciruns install scripts.README.mdandLICENSEcount as documentation only as edits.pyproject.tomlnames both and the npm package copiesLICENSE. Deleting either, renaming it away or changing its type therefore needs the full gate, and a receipt claiming such a change is refused. Any otherREADME.mdis Markdown as before. The rule id stayscodegen-docs-only/v1: it only narrows, and the publish hook re-derives every diff.test.ymlruns once per pushed commit (concurrencykeyed by the SHA), so a docs-only push no longer cancels a code push's pytest.The same-repository guard
This repository is public and takes fork PRs.
differentialandpytestare the two jobs that run the PR head's code underpull_request_target, withcontents: read. Theirif:gains:&& (github.event_name != 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository)schedule) and manual runs still run both jobs.head.repois null, so it counts as a fork.preflight-docs) and the reporter (gate) stay unguarded. They run trusted code from main, so everypull_request_targetrun postsgate: no hang, no silent pass.The reporter takes the same expression as
SAME_REPO:Fork PR: a maintainer must push this branch to pineforge-4pass/pineforge-codegen-oss and open a PR from it for the full gate(124 characters, under GitHub's 140). The reporter job then fails.github-actions[bot]) already posted agatesuccess on the same commit, the fork's run leaves it and exits 0. That success means a maintainer pushed the fork's branch here and opened a PR from it. Without this rule, any lateredited/reopened/ready_for_reviewevent on the fork PR would turn the maintainer's PR red, and anyone could do it on purpose by opening a fork PR at a same-repository PR's head.mainmoves.A fork's accepted docs proof ran only main's checker on the PR head as data, so it clears as a same-repository one does.
How each event behaves
How this was checked: each
run:script was extracted from this branch's YAML and run with bash, in the checkout layout the job builds (.docs-gate= main's checker;.candidate= the PR head as data, or the pushed commit with the previous main). The inputs were synthetic commits on top of this head. GitHub's jobif:was applied as written, andghwas stubbed. The installation reviewer's independent reporter, push and promotion simulations were run on this head as well.preflight-docsdifferential+pytestgatepull_request_target: opened, synchronize, reopened, edited, ready_for_review)gate.ymlmainto the head: accepted,docs_only=true; proof kept ascodegen-docs-diffsuccess"docs-only preflight+docs; no sweep" on the exact headgate.ymlREADME.md/LICENSEdeleted or retyped):docs_only=falsenpm audit,gate:full; pytest 3.11–3.14success"Full codegen differential gate and pytest passed" only if both pass, elsefailure"Codegen gate did not pass"contents: readjobsgate.ymlsuccessdocs-onlygate.ymldocs_only=falsefailure"Fork PR: a maintainer must push this branch … and open a PR from it for the full gate"; if the same commit already passed in a repository run, that success is left and the job exits 0maintest.yml(gate.ymlhas no push trigger)github.event.before) judges before..aftertest.ymlposts none);promote-baseline.ymlruns from main's copy on the PR'sclosedeventgate.ymlgate.ymldocs_only=falseSimulated rows, all as designed:
.md.before.The installed checker, run from this codegen path, refuses this PR's own diff (
.github/**is outside the rule), so it takes the full gate.This PR's own checks
No workflow triggers on this PR by itself:
pull_requestreads the definitions in GitHub's test commit of this PR onmain, whosegate.ymlandtest.ymlno longer have that trigger.pull_request_targetreads main's oldgate.yml, which has none.test.yml, which runs onmainonly.The required
gatetherefore comes from one manual run of this branch'sgate.yml, dispatched once right after this PR opened: run 37317597451.gate.ymlexists on main withworkflow_dispatch, and the branch's copy has it too.gatejob check-run lands on it. The reporter posts no commit status on a manual run, and GitHub does not count aworkflow_dispatchcheck-run toward a PR's required context; see Bootstrap below.It runs this PR's own
gate.yml, not main's. That is acceptable for this bootstrap PR because:pull_requestdefinition anyway.{}at the workflow level,contents: readper job).Three paths run only once this is on
main: classifying a PR head in.candidate, the fork guard, and the status POST. The simulations above exercised them; a docs-only smoke PR afterwards proves them live.Bootstrap
gate.yml, run 37317597451 (workflow_dispatch, head5b596a4), passed:preflight-docs,differential, pytest 3.11–3.14 and thegatejob all succeeded.BLOCKED. GitHub does not count aworkflow_dispatchcheck-run toward a PR's required context.5b596a4: contextgate, statesuccess, target the run above, description "bootstrap: gate.yml run 37317597451 (workflow_dispatch) passed on this head" (status 55606203402, 2026-10-05 13:46:42Z). That is exactly what the installed reporter posts underpull_request_target.main, the reporter postsgateitself. No bypass actor or ruleset change was used.Trust audit
gate.ymlpreflight-docscontents: readpersist-credentials: false(both checkouts)gate.ymldifferentialcontents: readid-tokennot granted)persist-credentials: falsegate.ymlpytestcontents: readpersist-credentials: falsegate.ymlgate(reporter)statuses: writeGITHUB_TOKEN)test.yml(push tomain)contents: readpersist-credentials: falsepromote-baseline.ymlcontents/statuses/checks: read,id-token: writegit fetch);persist-credentials: falsefor the tooling checkoutOther properties:
gate.ymlsetspermissions: {}at the workflow level.${{ }}appears inside anyrun:ofgate.yml/test.yml; event data reaches scripts throughenv.actions/setup-pythonv5.6.0 andactions/setup-nodev4.4.0 restore a cache only whencache:is set, and nothing sets it.package.jsonhas nopackageManagerfield. No workflow here uses a cache.Verification
gate.yml: onlydangerous-triggers(pull_request_target, by design).test.yml: no findings.promote-baseline.yml: its trigger,artipackedon the base checkout (whosegit fetcha private repository needs), and an info about the trusted${{ steps.axis.outputs.repo }}.8cbd5c6, run on the same AWS box with Node 24.12: codegen-docs-gate, baseline-promote, pr-gate-hook, publish-reader, state-gcp-receipts-pg): 104 tests, 91 pass, 13 skipped (need Postgres), 0 fail. Each new test fails against the previous templates. The five files are byte-equal to build(deps): bump pypa/gh-action-pypi-publish from 1.14.0 to 1.14.2 #106's templates at this head.a0117d0(on an AWS spot box; a0117d0 changes only a Postgres test fixture, so the templates are as at8cbd5c6):npm test: 1,332 tests, 1,298 pass, 0 fail, 34 skipped.make test-pg: 60 tests and 39 pipeline tests passed on a throwaway postgres:16 container, none skipped.d9f4f22on the same box: 1,329 / 1,295 / 0 / 34, and test-pg all passed.lab remoteon CLOUD. Jobrj-20261005t130442-7b7aa5ran at this head5b596a4(treebe3dab55, clean) against engineb7ed0ee7(engine main), with--steps pytest,compile-corpus,gate,pyodide:5741 passed, 41 skippedin 1178.53 s;junit: tests=5782 failures=0 errors=0 skipped=41. The engine,version.h, Eigen,libpineforge.a, the 314-strategy corpus and the 1m feed all resolved. The skips: 35 compare against older commits the shipped bundle's history lacks, 2 need clang++ (the host has g++), 4 have empty parameter sets.314 passed;junit: tests=314 failures=0 errors=0 skipped=0.gate selftest: 8 comparator + 10 verdict cases OK;gate: PARITY OK over 277 fixtures (verdicts asserted: ok/* succeed, err/* rejected).npm pack: @pineforge/[email protected], 65 files, 1532754 B packed.v1.2.0(rj-20261005t112325-e8fd06).test_many_bindings_are_walked_once_eachstays at main's 60 s CPU budget. On spark2 (NVIDIA GB10: 10 Cortex-X925 + 10 Cortex-A725) it took 7.2 s of CPU alone on an X925 core and 13.7 s on an A725. Across 40 concurrent runs at up to 2.4× CPU oversubscription (1-min load 48.6 on 20 cores) it took 8.9–12.3 s of CPU and up to 49 s of wall clock, with 0 failures.exp-cgadopt-gate-20261005: 7,989 probes in 134 cases, attempt 1, 0 failed, engine treeed8ac522and codegen treebe3dab55(both clean).90fe216de76ac33929941b1a4aa09db32f0429ac1c20c1eb4b5b6bb8ffa0c2b0(1,521,054 bytes;pineforge-publish-snapshot-c9jk5).pineforge-parity-baseline-20261005-engine-b7ed0ee7, snapshotf1892e49b520778c0942bcd5313cdec59395d27be9f514088d44fc505c5c02cf.40c482c2…(3,731,436 bytes; contentcf857209…).pineforge-pr-gate-q5stx:verdict PASS (no-improve-no-regression),pineforge-pr-gate-verdict/v3. Changes: hard 0 improved / 0 worsened, target 0 / 0.lab gate record: receipt0be89135….Ruleset (unchanged here)
Read 2026-10-05:
PineForge strict CI base(21476387) requires contextgatewith no app pin, strict up-to-date,do_not_enforce_on_create: false. Bypass: one Integration (4105147).defaultadds deletion protection, linear history and a PR rule with 0 approvals.Pinning
gateto GitHub Actions (app id 15368):GITHUB_TOKENstatuses are created bygithub-actions[bot], and its job check-run comes from app 15368.lab gate docs --postwould no longer count. It posts with the operator's ownghlogin, and CI now posts the same docs-only verdict.Planned once this lands: one docs-only smoke PR, then
required_status_checks: [{"context": "gate", "integration_id": 15368}](strict and bypass unchanged).Notes, not changed here
if: always(). A run cancelled by a newer run on the same head (aneditedevent) still postsfailure. The newer run normally posts later and wins; a re-run fixes the rare reverse order. Promotion now ignores the superseded red run..github/dependabot.yml). The workflows are the templates', so each bump goes to pineforge-workflow too and needs a measured pr-gate here. npm and pip stay weekly. Dependabot PRs are same-repository branches and take the full gate. Onpull_request_target, GitHub makes the token read-only only when the PR's base ref was created by Dependabot. Here that ref ismain, so the reporter should be able to post; the first such PR will confirm it. A bumped action ingate.ymlruns only once it is onmain, because PRs run main's definition.CONTRIBUTING.mdgains one note on how CI treats fork PRs.🤖 Generated with Claude Code