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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ jobs:
- name: no new operator URL at a log line (WOR-2629)
run: bash scripts/check-log-url-ratchet.sh


- name: clippy
run: cargo clippy --workspace --all-targets -- -D warnings

Expand Down Expand Up @@ -588,6 +589,32 @@ jobs:
CHANGELOG_BASE_REF: ${{ github.event.pull_request.base.sha || github.sha }}
run: python3 scripts/changelog-fragments.py --check

# The fourth ratchet of the unwrap/pub-item/log-url family, but it
# lives here rather than in the lint lane with its three siblings,
# and the reason is worth writing down because the first version
# of this got it wrong.
#
# Those three count source sites, so a depth-1 checkout serves
# them. This one compares a committed number against the same
# number at the merge base, which needs history the lint lane's
# checkout does not fetch. Wired there, it resolved no base on
# every `pull_request` event, printed a note, and exited 0: a
# guard that ran on every pull request and checked nothing,
# shipped inside the change whose subject is guards that cannot
# fail. It now fails closed, and it is here because this job's
# checkout is full depth for precisely this reason.
- name: stack-budget ratchet fixtures
run: bash scripts/check-stack-budget-ratchet.sh --self-test

- name: AI dispatch path stack budget only falls (WOR-2699)
env:
# Same shape and same reason as CHANGELOG_BASE_REF above: the
# script takes the merge base of this ref with HEAD itself, so
# a base gone stale behind a direct push to main cannot
# attribute main's commits to this branch.
STACK_BUDGET_BASE_REF: ${{ github.event.pull_request.base.sha || github.sha }}
run: bash scripts/check-stack-budget-ratchet.sh

# A merge that commits its own conflict markers ships corrupted
# files. One reached main's CHANGELOG on 2026-08-19 through a gate
# that never looked, because this scan existed only in
Expand Down
38 changes: 0 additions & 38 deletions .github/workflows/context-compression-eval.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,41 +129,3 @@ jobs:
--provenance "$HARNESS/fixtures/provenance.json" \
--json-report "$HARNESS/reports/window-fit-smoke.json" \
--markdown-report "$HARNESS/reports/window-fit-smoke.md"

compression-request-path:
name: production request-path smoke
runs-on: ubuntu-latest
# Was 35 with a warm cache. The cache below is restore-only now, and a
# cold run compiles the sbproxy binary and the e2e test union from
# scratch; 60 covers that honestly.
timeout-minutes: 60
steps:
- uses: actions/checkout@v5

# Restore-only, same reasoning as the rust-cache step in the job
# above: this key held ~2.0 GB of the 10 GB budget that ci.yml's
# header arithmetic gives back to the per-PR keys.
- uses: ./.github/actions/rust-setup
with:
cache-key: context-compression-request-path
cache-save: 'false'

- name: build sbproxy binary
run: cargo build -p sbproxy --locked

- name: Phase 1 request path
run: >-
cargo test -p sbproxy-e2e --locked --test context_compression_phase1
-- --test-threads=1

- name: governed selector precedence
run: >-
cargo test -p sbproxy-e2e --locked --test governed_key_policy
dynamic_compression_profile_changes_context_and_header_overrides_it
-- --exact --test-threads=1

- name: Anthropic native compression
run: >-
cargo test -p sbproxy-e2e --locked --test ai_native_inbound
anthropic_native_upstream_receives_the_compressed_message_list
-- --exact --test-threads=1
111 changes: 111 additions & 0 deletions .github/workflows/request-path-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# The lane that runs a real sbproxy binary down the AI request path.
#
# It lives in its own workflow, with no `paths:` filter, because of how
# it failed. It used to be a job inside context-compression-eval.yml
# behind a twelve-path trigger, and the two commits that broke `main`
# touched none of those twelve paths. The lane did not go red; it did
# not run at all, and `main` sat overflowing its stack with nothing to
# say so.
#
# A stack budget cannot be gated on a path list. The stack is consumed
# by the whole call chain, so a frame added anywhere in the workspace,
# in a crate this file has never heard of, spends the same budget as one
# added in `ai_dispatch.rs`. Any filter narrower than "every pull
# request" is narrower than the thing being guarded, and a guard
# narrower than its claim is worse than none.
#
# This lane builds a DEBUG binary on purpose. Debug frames are far
# larger than release ones, so it reaches depths production never does,
# which is exactly what makes it the place a stack budget is worth
# checking.

name: request-path-smoke

on:
# No `paths:` filter, deliberately: see the header. Branches are named
# so a fork's pull request against a release branch does not silently
# skip the one lane that catches a stack overflow.
pull_request:
branches:
- main
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1

jobs:
request-path-smoke:
name: production request-path smoke
runs-on: ubuntu-latest
# Was 35 with a warm cache. The cache below is restore-only now, and a
# cold run compiles the sbproxy binary and the e2e test union from
# scratch; 60 covers that honestly.
timeout-minutes: 60
steps:
- uses: actions/checkout@v5

# Restore-only: this key held ~2.0 GB of the 10 GB budget that
# ci.yml's header arithmetic gives back to the per-PR keys. The
# key name is unchanged from when this job lived in
# context-compression-eval.yml, so the warm cache carries over.
- uses: ./.github/actions/rust-setup
with:
cache-key: context-compression-request-path
cache-save: 'false'

# First, and on its own: this is the check that says how close the
# request path is to the stack it runs on, and it is cheap next to
# the three e2e runs below. `--nocapture` because the number is
# the point; a reviewer comparing two branches reads it out of the
# log rather than editing the test. Linux frames are the ones that
# matter here: macOS numbers rank candidates and nothing else.
# `cargo test` exits 0 when a filter matches nothing, so a
# renamed test turns this step into a no-op that reports success.
# That already happened once on this branch, with `--exact` and a
# bare test name selecting none of 2857 tests and passing. Count
# the tests that ran and fail if it is not the two expected.
- name: request-path stack budget
run: |
set -euo pipefail
cargo test -p sbproxy-core --locked --lib \
dispatch_path_stays_inside_its_stack_budget -- --nocapture 2>&1 \
| tee /tmp/stack-budget.log
ran="$(sed -n 's/^test result: ok\. \([0-9]*\) passed.*/\1/p' /tmp/stack-budget.log \
| head -1)"
if [ "${ran:-0}" -ne 2 ]; then
echo "::error::expected 2 stack budget tests, ran ${ran:-0}. A renamed or removed"
echo "::error::test makes this step pass while checking nothing."
exit 1
fi
grep -E 'STACK_HIGH_WATER_BYTES' /tmp/stack-budget.log

- name: build sbproxy binary
run: cargo build -p sbproxy --locked

- name: Phase 1 request path
run: >-
cargo test -p sbproxy-e2e --locked --test context_compression_phase1
-- --test-threads=1

- name: governed selector precedence
run: >-
cargo test -p sbproxy-e2e --locked --test governed_key_policy
dynamic_compression_profile_changes_context_and_header_overrides_it
-- --exact --test-threads=1

- name: Anthropic native compression
run: >-
cargo test -p sbproxy-e2e --locked --test ai_native_inbound
anthropic_native_upstream_receives_the_compressed_message_list
-- --exact --test-threads=1
35 changes: 28 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ten-minute build.
| Tracker placeholders | `grep -rn 'WOR-XXX' crates/ --include='*.rs' --include='*.toml'` (any hit fails) |
| pub-item ratchet | `bash scripts/check-pub-item-ratchet.sh` |
| unwrap/expect/panic ratchet | `bash scripts/check-unwrap-ratchet.sh` |
| AI dispatch stack budget | `bash scripts/check-stack-budget-ratchet.sh` |
| Spec citations | `bash scripts/check-spec-citations.sh` |
| Env mutation | `bash scripts/check-env-mutation.sh` |
| NOTICE (Apache-2.0-only) | `bash scripts/check-notice.sh` |
Expand Down Expand Up @@ -638,14 +639,34 @@ ships without any upstream fixes landed since the last cut.
Re-sync it as the first step of every release:

1. In the `pingora` checkout, fetch upstream (`origin`,
`github.com/cloudflare/pingora`) and rebase `sbproxy-0.8.0` onto the
target upstream tag, keeping our patch commits on top. Resolve any
conflicts against the new upstream.
2. Push the rebased branch to the `sb` remote
`github.com/cloudflare/pingora`) and rebase `sbproxy-0.8.0` onto a
newer upstream **`main`**, keeping our patch commits on top. Resolve
any conflicts against the new upstream.

Never onto a release tag, even though the branch is named for one.
Cloudflare cuts releases on a release branch, so a tag is not an
ancestor of `main`: as of 2026-08-28, `0.8.1` holds 8 commits `main`
does not have while `main` holds 190 that `0.8.1` does not. Rebasing
onto a tag moves the fork to a different line rather than to an older
point on the same one, and strands the fixes we have upstreamed,
which land on `main`.

The crates declare `0.8.0` and the branch is named `sbproxy-0.8.0`,
but neither is a release we track. Read the version as an API
generation.
2. Run `scripts/divergence.sh` in the fork before and after. It prints
the merge base, how far ahead and behind the fork is, and which files
we carry, which is the only honest before-and-after for a rebase that
touches upstream history. The fork's CI prints the same report on
every PR. Copy the after numbers into the comment above
`[patch.crates-io]` in `Cargo.toml`.
3. Push the rebased branch to the `sb` remote
(`git@github.com:soapbucket/pingora.git`).
3. Back in this workspace, refresh `Cargo.lock` so the build resolves to
the new fork commit, then run the full gate. Only cut the tag once it
is green against the updated fork.
4. Back in this workspace, refresh `Cargo.lock` so the build resolves to
the new fork commit, then run the full gate. Diff the lockfile and
revert anything that is not the `pingora-*` rev bumps: a `cargo update
-p` on this workspace has silently downgraded unrelated dependencies
before. Only cut the tag once it is green against the updated fork.

## License + attribution

Expand Down
35 changes: 28 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ten-minute build.
| pub-item ratchet | `bash scripts/check-pub-item-ratchet.sh` |
| unwrap/expect/panic ratchet | `bash scripts/check-unwrap-ratchet.sh` |
| Operator URLs at log lines | `bash scripts/check-log-url-ratchet.sh` |
| AI dispatch stack budget | `bash scripts/check-stack-budget-ratchet.sh` |
| Spec citations | `bash scripts/check-spec-citations.sh` |
| Env mutation | `bash scripts/check-env-mutation.sh` |
| Durable file modes | `bash scripts/check-durable-file-modes.sh` |
Expand Down Expand Up @@ -689,14 +690,34 @@ ships without any upstream fixes landed since the last cut.
Re-sync it as the first step of every release:

1. In the `pingora` checkout, fetch upstream (`origin`,
`github.com/cloudflare/pingora`) and rebase `sbproxy-0.8.0` onto the
target upstream tag, keeping our patch commits on top. Resolve any
conflicts against the new upstream.
2. Push the rebased branch to the `sb` remote
`github.com/cloudflare/pingora`) and rebase `sbproxy-0.8.0` onto a
newer upstream **`main`**, keeping our patch commits on top. Resolve
any conflicts against the new upstream.

Never onto a release tag, even though the branch is named for one.
Cloudflare cuts releases on a release branch, so a tag is not an
ancestor of `main`: as of 2026-08-28, `0.8.1` holds 8 commits `main`
does not have while `main` holds 190 that `0.8.1` does not. Rebasing
onto a tag moves the fork to a different line rather than to an older
point on the same one, and strands the fixes we have upstreamed,
which land on `main`.

The crates declare `0.8.0` and the branch is named `sbproxy-0.8.0`,
but neither is a release we track. Read the version as an API
generation.
2. Run `scripts/divergence.sh` in the fork before and after. It prints
the merge base, how far ahead and behind the fork is, and which files
we carry, which is the only honest before-and-after for a rebase that
touches upstream history. The fork's CI prints the same report on
every PR. Copy the after numbers into the comment above
`[patch.crates-io]` in `Cargo.toml`.
3. Push the rebased branch to the `sb` remote
(`git@github.com:soapbucket/pingora.git`).
3. Back in this workspace, refresh `Cargo.lock` so the build resolves to
the new fork commit, then run the full gate. Only cut the tag once it
is green against the updated fork.
4. Back in this workspace, refresh `Cargo.lock` so the build resolves to
the new fork commit, then run the full gate. Diff the lockfile and
revert anything that is not the `pingora-*` rev bumps: a `cargo update
-p` on this workspace has silently downgraded unrelated dependencies
before. Only cut the tag once it is green against the updated fork.

## License + attribution

Expand Down
23 changes: 12 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading