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
33 changes: 12 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read
Expand Down Expand Up @@ -66,7 +67,6 @@ jobs:
if-no-files-found: warn

build:
needs: preflight
timeout-minutes: 45
strategy:
fail-fast: false
Expand Down Expand Up @@ -121,7 +121,7 @@ jobs:
# ratio -- for a runner whose CPU accounting is unusable; record the
# measured reason here and in docs/ci.md before setting it.
PINEFORGE_RUNTIME_BUDGET_CANDIDATE_ONLY: '0'
run: python3 scripts/ci_verify.py ${{ matrix.build_type == 'Release' && 'release' || 'debug' }} --build-dir build --jobs 4 --ccache
run: python3 scripts/ci_verify.py ${{ matrix.build_type == 'Release' && 'release' || 'debug' }} --build-dir build --jobs 4 --ccache ${{ github.event_name == 'pull_request' && matrix.build_type == 'Debug' && '--exclude-label slow' || '' }}

- name: Stage and summarize diagnostics
if: always()
Expand All @@ -142,7 +142,6 @@ jobs:
# instrumentation propagates into every test binary (CMakeLists.txt option
# PINEFORGE_ENABLE_SANITIZERS).
sanitizers:
needs: preflight
runs-on: ubuntu-24.04
timeout-minutes: 90
env:
Expand Down Expand Up @@ -172,7 +171,7 @@ jobs:
ccache-${{ runner.os }}-${{ runner.arch }}-sanitizers-

- name: Verify (sanitizers)
run: python3 scripts/ci_verify.py sanitizers --build-dir build-asan --jobs 4 --ccache
run: python3 scripts/ci_verify.py sanitizers --build-dir build-asan --jobs 4 --ccache ${{ github.event_name == 'pull_request' && '--exclude-label slow' || '' }}

- name: Stage and summarize diagnostics
if: always()
Expand All @@ -193,7 +192,6 @@ jobs:
# modules are built from the kernel alone, and every examples/native host
# runs as an example_* row asserting both its exit code and its summary line.
kernel-only:
needs: preflight
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
Expand Down Expand Up @@ -234,30 +232,23 @@ jobs:
path: ci-diagnostics/
if-no-files-found: warn

# Reuse the full native proof once per CI run. The reusable workflow also
# retains manual dispatch for explicitly requested standalone verification.
# Reuse native proof once per CI run. Its manual dispatch runs every row.
native-live:
needs: preflight
uses: ./.github/workflows/native-live.yml
with:
exclude_slow: ${{ github.event_name == 'pull_request' }}

# TradingView parity, on the population a pull request can wait for. No
# ci_verify.py profile compiles a corpus strategy, so without this job
# nothing between a src/** change and a merge re-derives the engine's
# parity: the whole sweep is ~32 min and can only run nightly
# (corpus-parity.yml). This one re-runs the 30 probes
# scripts/corpus_parity_subset.txt names against the same pinned sha256
# rows, and feeds the required `build` context below — which is what makes
# a trade-moving change unmergeable rather than caught the next night.
# Deliberately unfiltered by path: `build` requires success, and a skipped
# dependency is not success.
# Advisory TradingView parity subset on every CI run. The maintainers post
# the required pineforge/parity status from their separate parity verdict.
# Keep this job unfiltered so build-gate reports its result on every run.
corpus-parity-subset:
needs: preflight
uses: ./.github/workflows/corpus-parity.yml
with:
mode: subset

# Keep the required `build` context while making it cover every proof lane.
# Failure, cancellation or skipping in any dependency must fail this gate.
# Advisory aggregate context covering every GitHub Actions lane. The merge
# ruleset requires pineforge/verify and pineforge/parity, posted by the
# maintainers, rather than this build context.
build-gate:
name: build
needs: [preflight, build, sanitizers, native-live, kernel-only, corpus-parity-subset]
Expand Down
17 changes: 8 additions & 9 deletions .github/workflows/corpus-parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ name: Corpus parity
# runs nightly and on demand, plus on any pull request that touches the pin or
# the parity tooling itself.
#
# AND HOW PARITY STILL BLOCKS A MERGE. The `corpus-parity-subset` job below
# PR EARLY SIGNAL. The `corpus-parity-subset` job below
# re-runs the 30 probes scripts/corpus_parity_subset.txt names -- in parallel,
# they write into disjoint directories -- and judges them against the same
# pinned sha256 rows the nightly uses. Measured on the same laptop at JOBS=8,
# under sibling load: derive 2 s, build the runtime + the 30 strategy .so 68 s
# from clean, run 45 s wall for 80 s of probe CPU, judge <1 s; 94 s end to end
# over an up-to-date build directory, under 3 min cold.
# .github/workflows/ci.yml calls it as a reusable workflow and
# makes it a dependency of the required `build` context, so a src/** change that
# moves one of those 30 probes by one byte cannot merge. Nothing about the
# nightly job changes.
# .github/workflows/ci.yml calls it as a reusable workflow and includes it in
# the advisory `build` aggregate. The maintainers' separate full parity
# verdict posts the required pineforge/parity commit status on the PR head.
# Nothing about the nightly job changes.

on:
schedule:
Expand All @@ -44,7 +44,7 @@ on:
workflow_call:
inputs:
mode:
description: 'full (all 312 probes) or subset (the pull-request gate)'
description: 'full (all 312 probes) or subset (PR early signal)'
type: string
default: full
pull_request:
Expand Down Expand Up @@ -156,9 +156,8 @@ jobs:
path: corpus-parity-diagnostics/
if-no-files-found: warn

# The blocking half: the 30 probes scripts/corpus_parity_subset.txt names,
# judged against the same pinned sha256 rows. Reached only through
# .github/workflows/ci.yml, which needs it for the required `build` context.
# The advisory PR subset: the 30 probes scripts/corpus_parity_subset.txt
# names, judged against the same pinned sha256 rows. Reached through CI.
corpus-parity-subset:
if: inputs.mode == 'subset'
runs-on: ubuntu-24.04
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/native-live.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ name: Native live runner

on:
workflow_call:
inputs:
exclude_slow:
description: Exclude measured slow CTest rows in the caller's PR run
type: boolean
default: false
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -100,7 +105,7 @@ jobs:
cmake --install curl-build 2>&1 | tee curl-install.log

- name: Verify native live
run: python3 scripts/ci_verify.py native --build-dir build-live --jobs 4 --generator Ninja --curl-dir "${{ github.workspace }}/build-native-deps/curl-install/lib/cmake/CURL" --ccache --require-websocket
run: python3 scripts/ci_verify.py native --build-dir build-live --jobs 4 --generator Ninja --curl-dir "${{ github.workspace }}/build-native-deps/curl-install/lib/cmake/CURL" --ccache --require-websocket ${{ inputs.exclude_slow && '--exclude-label slow' || '' }}

- name: Stage and summarize diagnostics
if: always()
Expand Down
34 changes: 21 additions & 13 deletions .github/workflows/promote-baseline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
# On a merged PR it advances the campaign baseline one axis to the merged
# composite, reusing the snapshot from the pr-gate run that already cleared
# it. It promotes ONLY when the merge is exact-head (the gated commit's tree is
# now on the base branch), CI is green on that commit, and a PASS verdict binds
# now on the base branch), both maintainer verification statuses are green on
# that commit, and a PASS verdict binds
# the full (engine, codegen) pair. Otherwise it exits green without promoting.
#
# The gate logic (exact-head + CI-green + baseline-promote.mjs) is unchanged;
# only the transport to the campaign plane changed. The plane is now GCP: the
# The gate logic is exact-head + pineforge/verify + pineforge/parity +
# baseline-promote.mjs. The plane is now GCP: the
# promotion reads the active baseline and the gate ledger from the Postgres
# registry through a cloud-sql-proxy, streams the pinned documents from the
# GCS evidence bucket, and appends the new baseline row — authenticated by
Expand Down Expand Up @@ -72,10 +73,9 @@ on:

permissions:
contents: read
# "Confirm CI is green" reads check-runs, which is `checks`. The older
# combined-status API is not called, so no `statuses: read` is granted --
# this block is exactly what the steps below do, and nothing spare.
checks: read
# The two required merge-gate verdicts are commit statuses, not Actions
# check-runs. Inspect the PR head commit's statuses before promotion.
statuses: read
# Workload identity federation mints the GCP credential from the job's own
# OIDC token; nothing here can promote without this repo's identity.
id-token: write
Expand Down Expand Up @@ -121,18 +121,26 @@ jobs:
fi
echo "ok=true" >> "$GITHUB_OUTPUT"

- name: Confirm CI is green on the merged commit
- name: Confirm the merge gate on the merged PR head
id: ci
if: steps.exacthead.outputs.ok == 'true'
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.inputs.merge_commit }}
run: |
set -euo pipefail
bad=$(gh api "repos/${{ github.repository }}/commits/$HEAD_SHA/check-runs" \
--jq '[.check_runs[] | select(.conclusion=="failure" or .conclusion=="cancelled" or .conclusion=="timed_out" or .conclusion=="action_required")] | length')
if [ "$bad" != "0" ]; then
echo "::notice::$bad failing check-run(s) on $HEAD_SHA — not green; skipping"
# GitHub lists newest statuses first. Keep the first occurrence of
# each context across pages, so an older success cannot mask a
# newer failure or pending verdict on this exact commit.
statuses=$(gh api --paginate \
"repos/${{ github.repository }}/commits/$HEAD_SHA/statuses?per_page=100")
if ! jq -es '
reduce (.[][]) as $s ({};
if has($s.context) then . else .[$s.context] = $s.state end)
| .["pineforge/verify"] == "success"
and .["pineforge/parity"] == "success"
' <<< "$statuses" >/dev/null; then
echo "::notice::pineforge/verify and pineforge/parity must both be success on $HEAD_SHA — skipping"
echo "green=false" >> "$GITHUB_OUTPUT"; exit 0
fi
echo "green=true" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -188,7 +196,7 @@ jobs:
chmod +x /tmp/cloud-sql-proxy
/tmp/cloud-sql-proxy --port 5433 \
gen-lang-client-0864094636:asia-east1:pineforge-workflow-pg &
for i in $(seq 1 30); do
for _ in $(seq 1 30); do
if (exec 3<>/dev/tcp/127.0.0.1/5433) 2>/dev/null; then exit 0; fi
sleep 1
done
Expand Down
39 changes: 20 additions & 19 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

Both C++ unit tests and full corpus verification must pass.

The merge ruleset requires `pineforge/verify` and `pineforge/parity` commit
statuses on the PR head's exact tree. The maintainers' `lab verify` tooling
posts them after full verification on their x86-64 build hosts and a parity
verdict. GitHub Actions is advisory: PR Debug, sanitizers and native jobs
exclude the measured `slow` CTest rows; push-to-main and manual CI dispatch
run every row. The commands below run the full sets.

```bash
# Fast workflow/source checks first (requires actionlint 1.7.12 + ShellCheck).
# This does not replace either verification step below.
Expand Down Expand Up @@ -122,23 +129,17 @@ dispatches, reviews and measures; this section is what binds you here.
engine closer to an independent backtest + forward-execution state
machine, and Pine-parity behaviour kept in codegen with this kernel clean.

## Parity campaign gate (applies on EVERY harness)

Pushes and PRs from this repo are gated by the PineForge parity campaign: a
fresh (≤6h) PASS verdict must bind the exact (engine, codegen) HEADs, recorded
on the campaign registry. Under Claude Code a PreToolUse hook
(`.claude/settings.json`, calls `pineforge-workflow/campaign/hooks/pr-gate.mjs`)
enforces this on `git push` / `gh pr create|ready|merge`. Codex, OpenCode, and
other harnesses run NO hook — the discipline is exactly as binding there: before
any push, run the gate and record the verdict (see the `pr-gate` skill in
`pineforge-workflow/.claude/skills/` — plain markdown, readable anywhere):

```sh
gcloud run jobs execute pineforge-pr-gate --project gen-lang-client-0864094636 \
--region asia-east1 --args '^|^--pipeline|pr_gate|--conf|<conf-json>'
lab gate record --verdict <verdict.json> --engine <sha> --codegen <sha>
```
## Merge gate and parity campaign

The `PineForge strict CI base` ruleset requires `pineforge/verify` (full
`ci_verify.py` profiles on the PR head's exact tree) and `pineforge/parity`
(no parity regression, or no engine behaviour change). The maintainers'
`lab verify` tooling posts both commit statuses; GitHub Actions does not post them.
Its PR jobs provide faster advisory feedback, while push-to-main and manual
dispatch run the full CI profiles. A campaign PASS verdict still binds the
exact engine and codegen HEADs for baseline promotion.

Merged single-axis PRs advance the campaign baseline automatically
(`.github/workflows/promote-baseline.yml`); a squash/rebase that rewrites the
sha defers and must be re-gated.
Merged single-axis PRs advance the campaign baseline automatically through
`.github/workflows/promote-baseline.yml` only when the exact-head guard, both
required statuses on that head, and the campaign verdict pass. A squash or
rebase that rewrites the SHA defers promotion and needs new verification.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ Every script is exported from TradingView as-is (its own inputs, its own default
- **strong** — ≥ 95% matched, trade count within 6%, entries within 0.1% and exits within 0.5% at p90;
- **moderate / weak** — ≥ 75% coverage, or less.

Published parity results use a fixed population and reproducible Cloud Run measurements. The formal gate requires **no hard-surface regression** and strictly positive pooled movement across the target excellent and excellent+strong bands. A documented native-correctness exception permits exactly zero target-band movement with no individual regression, after full comparison, independent review and CI; its actual FAIL remains recorded and baseline promotion is deferred. Negative movement is outside this exception. Baseline promotion requires a recorded PASS and an exact-head merge with green CI.
Published parity results use a fixed population and reproducible Cloud Run measurements. The formal gate requires **no hard-surface regression** and strictly positive pooled movement across the target excellent and excellent+strong bands. A documented native-correctness exception permits exactly zero target-band movement with no individual regression, after full comparison and independent review; its actual FAIL remains recorded and baseline promotion is deferred. Negative movement is outside this exception. The merge ruleset requires the maintainers' `pineforge/verify` and `pineforge/parity` commit statuses on the exact PR head; GitHub Actions CI is advisory. Baseline promotion also requires a recorded PASS and an exact-head merge.

### What the closed test taught the engine

Expand Down
Loading
Loading