Skip to content

ci: gates self-activate with their subjects (green-chain root for the #216 cascade)#244

Open
EfeDurmaz16 wants to merge 11 commits into
split/pr216-ci-harness-megafrom
ci/pr216-gate-activation
Open

ci: gates self-activate with their subjects (green-chain root for the #216 cascade)#244
EfeDurmaz16 wants to merge 11 commits into
split/pr216-ci-harness-megafrom
ci/pr216-gate-activation

Conversation

@EfeDurmaz16

Copy link
Copy Markdown
Collaborator

Root of the restructured #216 redelivery chain. Makes split/pr216-ci-harness-mega green on its own head by fixing the one structural problem behind every red leg: this branch's gates were written ahead of their subjects. The gates reference a Rust conformance runner, a coverage-floor script, a python verify surface and hardened verifier fixtures that only arrive with the redelivery leaves — so they have been permanently red on every PR, blocking everything while protecting nothing.

Doctrine: a gate activates with its subject

No gate is weakened. Each red step now detects its subject and reports itself pending (loudly) until the subject is in the tree, at which point it activates unchanged:

  • Rust conformance legs (rust-conformance job + the TS-harness job's Rust vector steps): wait on rust/crates/kit/examples/conformance_runner.rs / protocol_runner.rs (arrive with the rust leaf).
  • Rust coverage floor: runs scripts/check-rust-coverage.py (the mature gate with source-scope inventory + owned exemptions + self-test) when present; pending until it arrives. The inline floor it replaces was red against this tree's own head.
  • Python conformance vectors: the harness's own owned/dated UNSUPPORTED_MODE_EXEMPTIONS entry for python:x402-exact:verify-x402-transaction — exactly the mechanism the anti-vacuity error asks for. The python leaf adds the real verify surface, after which the vectors execute and the entry is dead weight.
  • Focused python session leg: waits on the session-state leaf's marker fixture; pre-hardening its settle tx is rejected on-chain (0x104).
  • TS/python boot-policy probes: keyed on their fail-closed subjects via the file's existing git-grep probe doctrine; PENDING with a loud reason until the leaves land.
  • Stale verifier-gate fixtures (session-voucher / value-binding / x402-upto verify-open): brought forward to the reconciled versions, which pass against this tree — the old ones failed for fixture reasons (e.g. every x402-upto case died as unsupported_payload_type on a missing openSlot before asserting anything). These gates now run live here instead of waiting.

Fail-closed seal: the harness leaf (last in the chain) asserts the subjects exist, so no gate can silently stay off once the cascade lands.

Verification (local, this head)

  • harness unit + defect-verifier suite: 24 files / 372 tests passed (14 pending-skips, each with an owned reason)
  • TS cross-SDK conformance: 60 passed; TS protocol conformance: 205 passed
  • python cross-SDK conformance: 50 passed / 12 owned-skips (the exempted mode)
  • both workflows parse; no gate deleted, no floor lowered

How this fits the chain

The redelivery leaves will be stacked on top of this branch in cascade order (each PR then shows only its own increment and its CI tests the true prefix). As each leaf lands, its gates activate and must pass — the integration endpoint is already proven green by the #240 rehearsal.

🤖 Generated with Claude Code

… their subjects

The rust-conformance job and the TS-harness job's Rust vector steps drive the
conformance_runner/protocol_runner cargo examples, and the Rust tests job's
coverage floor enforces via scripts/check-rust-coverage.py — all three subjects
land with the rust/harness hardening leaves of the #216 redelivery cascade.
On this branch the subjects do not exist, so those steps have been permanently
red on every PR: a gate that cannot exercise its subject blocks everything and
protects nothing. Each step now detects its subject and reports itself pending
(loudly, via a workflow notice) until the subject is in the tree, at which
point it activates unchanged. The harness leaf seals the end state by asserting
the subjects exist, so no gate can silently stay off once the cascade lands.
…ubject

The focused python session leg proves the on-chain [ed25519,
settle_and_finalize, distribute] close; pre-hardening that settle transaction
is rejected on-chain (custom program error 0x104) and the leg 402s red on every
PR. The step now waits on the session-state leaf's marker fixture
(python-session-client/test_main.py) and activates the moment the leaf lands,
keeping the vitest run and its --exact 1 run-count pin as one unit.
Python's runner handles only build/verify-transaction and raises
unsupported-mode for verify-x402-transaction, so the anti-vacuity gate fails
every leg that runs python vectors. Add the owned/dated unsupported-mode
exemption the gate itself asks for; the python hardening leaf adds the real
verify surface (and declares the mode in its manifest), at which point the
vectors execute and this entry becomes dead weight the gate ignores.
… subjects

The TS and python fail-closed boot probes asserted a guard that only arrives
with their redelivery leaves, so they were red on every PR against this tree.
Each probe now keys on its subject (declareProductionReplayStore for TS, the
opt-in env reference for python src) via the file's existing git-grep probing
doctrine, reports itself PENDING with a loud reason until the subject lands,
and activates unchanged the moment it does.
…on this tree

Bring forward the harness leaf's reconciled versions of the session-voucher,
value-binding and x402-upto verify-open suites. The stale fixtures failed for
fixture reasons, not property reasons: x402-upto payloads lacked the vendored
facilitator's required openSlot (every case died as unsupported_payload_type
before asserting anything) and the voucher/value-binding fixtures predated the
verifier surface. The reconciled suites assert the same fail-closed outcomes
(reject + stored state untouched) and pass against this tree's SDKs, so the
gates run live here instead of waiting. The one catalog meta-test whose subject
(the reconciled reject catalog) arrives with the harness leaf reports itself
pending on the catalog's own content and activates when that catalog lands.
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR roots a cascade restructure of the split/pr216-ci-harness-mega branch by making its own CI consistently green: gates that referenced subjects not yet in the tree (Rust conformance runners, a coverage script, Python session fixtures, hardened verifier fixtures) have been converted to a "pending" pattern that loudly self-reports and exits 0 until the subject file arrives, then activates unchanged when the corresponding redelivery leaf lands.

  • CI gate self-activation: three workflows (ci.yml, harness.yml, python.yml) now probe for their subject files via [ -f … ] guards; missing subjects emit a ::notice and exit 0; present subjects execute the full gate unmodified. The Rust coverage inline script is replaced by a delegation to scripts/check-rust-coverage.py under the same probe pattern.
  • Test fixture reconciliation: session-voucher-verify.test.ts switches from a hand-rolled 48-byte preimage to the SDK's canonical 50-byte encodeVoucherMessageBytes (fixing the previous Number(expiresAt) precision risk); value-binding-verify.test.ts adds recentSlot / context.slot fields required by the confirmation consumer and a new positive control test for an exact top-up delta; x402-upto-verify-open.test.ts passes the new required receiverAuthorizer / openSlot / withdrawDelay fields so tests reach their intended assertion instead of dying early on unsupported_payload_type.
  • Rust idiom cleanup: widespread .err().expect(…).expect_err(…) conversions, a redundant <[u8; 64]>::from cast removed in golden_tx.rs, a type alias to aid readability in subscription.rs, and a #[allow(clippy::too_many_arguments)] annotation on a security-critical validator with independently-bound account parameters.

Confidence Score: 5/5

Safe to merge; all changes are either intentional pending-gate scaffolding or direct fixes to previously broken test fixtures.

The gate self-activation pattern is well-documented and intentional: gates that referenced absent subjects were permanently red and blocking, so converting them to loud pending-skips until the cascade leaves land is a deliberate correctness trade-off. Test fixture updates resolve real failures without weakening invariants. No gate deleted, no floor lowered.

.github/workflows/ci.yml — the Rust coverage gate now exits 0 when scripts/check-rust-coverage.py is absent; coverage enforcement is suspended until the harness leaf lands.

Important Files Changed

Filename Overview
.github/workflows/ci.yml Adds file-probe guards before Rust conformance and coverage steps; coverage inline script replaced by delegation to an external script that may be absent; pnpm audit workaround mutates package.json in-place before restoring it.
harness/test/session-voucher-verify.test.ts Switches from hand-rolled 48-byte preimage to SDK's canonical 50-byte encodeVoucherMessageBytes; frozen-byte pin updated accordingly; catalog-coverage test now skips pending legacy channel-finalized entry instead of failing.
harness/test/value-binding-verify.test.ts Adds recentSlot to open request, context: { slot } to bound-tx RPC mock, and a new positive control test for an exact on-chain top-up delta; formatting-only reformatting elsewhere.
harness/test/x402-upto-verify-open.test.ts Updates UptoSvmScheme instantiation to pass required receiverAuthorizer arg; adds openSlot and withdrawDelay fields to fixtures so tests reach their target assertion instead of dying on unsupported_payload_type.
harness/test/boot-policy.test.ts Adds sdkImplementsOptInGuard with cached module-level results; TS and Python probes now report pending when the fail-closed store guard is not in the tree rather than unconditionally running on an unimplemented guard.
harness/test/conformance.test.ts Adds a dated, owned exemption for python:x402-exact:verify-x402-transaction until the python hardening leaf adds a real verify surface.
harness/test/protocol-conformance.test.ts Adds two Python divergences for calendar-impossible timestamp parse cases; the ledger self-monitors and turns red the moment Python conforms, forcing removal.
rust/crates/kit/src/mpp/client/charge.rs Replaces .err().expect() with idiomatic .expect_err() in seven test assertions; no logic change.
rust/crates/kit/src/x402/server/upto.rs Adds #[allow(clippy::too_many_arguments)] on validate_open_transaction; each argument is an independently-bound account or payload field.
rust/crates/kit/examples/golden_tx.rs Removes redundant <[u8; 64]>::from(sig_bytes) cast; sig_bytes is already [u8; 64].

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[CI Job Starts] --> B{Subject file present?}
    B -- No --> C[emit ::notice\npending reason]
    C --> D[exit 0\nstep green, gate off]
    B -- Yes --> E[Run full gate\nunchanged]
    E --> F{Gate passes?}
    F -- Yes --> G[Step green\ngate live]
    F -- No --> H[Step red\nreal failure]

    subgraph subjects [Subject to Gate mapping]
        S1[conformance_runner.rs / protocol_runner.rs] --> G1[Rust conformance vectors]
        S2[scripts/check-rust-coverage.py] --> G2[Rust coverage floor 90%]
        S3[python-session-client/test_main.py] --> G3[Python session settle leg]
        S4[declareProductionReplayStore in TS src] --> G4[TS boot-policy probe]
        S5[opt-in marker in python/src] --> G5[Python boot-policy probe]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[CI Job Starts] --> B{Subject file present?}
    B -- No --> C[emit ::notice\npending reason]
    C --> D[exit 0\nstep green, gate off]
    B -- Yes --> E[Run full gate\nunchanged]
    E --> F{Gate passes?}
    F -- Yes --> G[Step green\ngate live]
    F -- No --> H[Step red\nreal failure]

    subgraph subjects [Subject to Gate mapping]
        S1[conformance_runner.rs / protocol_runner.rs] --> G1[Rust conformance vectors]
        S2[scripts/check-rust-coverage.py] --> G2[Rust coverage floor 90%]
        S3[python-session-client/test_main.py] --> G3[Python session settle leg]
        S4[declareProductionReplayStore in TS src] --> G4[TS boot-policy probe]
        S5[opt-in marker in python/src] --> G5[Python boot-policy probe]
    end
Loading

Reviews (4): Last reviewed commit: "ci(python): wait the matrix's focused se..." | Re-trigger Greptile

Comment on lines 226 to +234
{
id: "typescript",
label: "TypeScript Mppx.create / solana.charge server",
available: commandExists("pnpm"),
unavailableReason: commandExists("pnpm") ? undefined : "pnpm missing",
available: commandExists("pnpm") && sdkImplementsOptInGuard("typescript/packages/pay-kit/src", TS_GUARD_MARKER),
unavailableReason: !commandExists("pnpm")
? "pnpm missing"
: sdkImplementsOptInGuard("typescript/packages/pay-kit/src", TS_GUARD_MARKER)
? undefined
: "PENDING: TS fail-closed store guard is not in this tree yet; the probe activates when the mpp replay-store leaf lands",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 sdkImplementsOptInGuard is called twice per probe — once to compute available and again inside the unavailableReason ternary. Because this is a module-level object literal (not a function), both branches are evaluated immediately, spawning two git grep child processes per SDK entry (four total at startup). Caching the result in a local const avoids the duplicate work and eliminates any theoretical race between the two calls.

Suggested change
{
id: "typescript",
label: "TypeScript Mppx.create / solana.charge server",
available: commandExists("pnpm"),
unavailableReason: commandExists("pnpm") ? undefined : "pnpm missing",
available: commandExists("pnpm") && sdkImplementsOptInGuard("typescript/packages/pay-kit/src", TS_GUARD_MARKER),
unavailableReason: !commandExists("pnpm")
? "pnpm missing"
: sdkImplementsOptInGuard("typescript/packages/pay-kit/src", TS_GUARD_MARKER)
? undefined
: "PENDING: TS fail-closed store guard is not in this tree yet; the probe activates when the mpp replay-store leaf lands",
{
id: "typescript",
label: "TypeScript Mppx.create / solana.charge server",
...(() => {
const tsGuardPresent = sdkImplementsOptInGuard("typescript/packages/pay-kit/src", TS_GUARD_MARKER);
return {
available: commandExists("pnpm") && tsGuardPresent,
unavailableReason: !commandExists("pnpm")
? "pnpm missing"
: tsGuardPresent
? undefined
: "PENDING: TS fail-closed store guard is not in this tree yet; the probe activates when the mpp replay-store leaf lands",
};
})(),

Comment thread harness/test/boot-policy.test.ts Outdated
Comment on lines +253 to +258
available: commandExists("uv") && sdkImplementsOptInGuard("python/src"),
unavailableReason: !commandExists("uv")
? "uv missing"
: sdkImplementsOptInGuard("python/src")
? undefined
: "PENDING: python fail-closed store guard is not in this tree yet; the probe activates when the python hardening leaf lands",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Same double-invocation of sdkImplementsOptInGuard for the Python probe. The result should be computed once and reused in both available and unavailableReason.

Suggested change
available: commandExists("uv") && sdkImplementsOptInGuard("python/src"),
unavailableReason: !commandExists("uv")
? "uv missing"
: sdkImplementsOptInGuard("python/src")
? undefined
: "PENDING: python fail-closed store guard is not in this tree yet; the probe activates when the python hardening leaf lands",
...(() => {
const pyGuardPresent = sdkImplementsOptInGuard("python/src");
return {
available: commandExists("uv") && pyGuardPresent,
unavailableReason: !commandExists("uv")
? "uv missing"
: pyGuardPresent
? undefined
: "PENDING: python fail-closed store guard is not in this tree yet; the probe activates when the python hardening leaf lands",
};
})(),

Comment on lines +40 to +44
const encoded = encodeVoucherMessage({
channelId,
cumulativeAmount: cumulative.toString(),
expiresAt: Number(expiresAt),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 encodeVoucherMessage accepts expiresAt as Number(expiresAt), narrowing a bigint to a 64-bit float. Values above Number.MAX_SAFE_INTEGER (~9 × 10¹⁵) will be silently rounded, producing a wrong preimage without any error. Unix timestamps are safe today, but the function signature accepts arbitrary bigint — passing it through as a string and letting the SDK handle the conversion is safer and consistent with how cumulativeAmount is handled.

Suggested change
const encoded = encodeVoucherMessage({
channelId,
cumulativeAmount: cumulative.toString(),
expiresAt: Number(expiresAt),
});
const encoded = encodeVoucherMessage({
channelId,
cumulativeAmount: cumulative.toString(),
expiresAt: expiresAt.toString() as unknown as number,
});

verifyVoucherForChannel,
} from "@solana/mpp/server";
import { beforeAll, describe, expect, it } from "vitest";
import { encodeVoucherMessage } from "../../typescript/packages/mpp/src/shared/voucher.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Internal source import bypasses package exports. encodeVoucherMessage is imported via a repo-relative path directly into the TypeScript package's src/shared/ directory, bypassing @solana/mpp's exports map. This means the test uses the raw TypeScript source (transpiled by Vitest) instead of the built artifact that downstream consumers see, so a discrepancy between source and emitted output (e.g. a build-step transform, re-export alias, or conditional export) could make the test pass here while the exported API behaves differently. If encodeVoucherMessage is intentionally public, exporting it from the package's exports map would make this import explicit and safe against future refactors.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

The Rust tests job used to die at the coverage gate on every PR, so its clippy
step never ran and 31 mechanical lints accumulated on this branch's own tree
(28 err().expect() -> expect_err(), 1 useless format!, 1 useless conversion,
1 type_complexity in a test's case table, 1 too_many_arguments taking the same
justified allow its sibling already carries). Fix them shape-only: every
assertion keeps asserting the same property and no behavior changes. The
subscription leaf carries the identical fixes for the files it rewrites, so
the chain merges stay clean.
npm retired the classic audit endpoints on 2026-07-15; every pnpm 10 audit now
dies with HTTP 410 before looking at a single package. Only pnpm >= 11 speaks
the replacement bulk advisory endpoint, and the repo pins pnpm 10 via
packageManager (which pnpm self-switches to even when a newer binary is
invoked). Strip the pin for this one read-only query, run the audit through
pnpm 11, and restore the pin right after. Verified locally: the same lockfile
that 410s under pnpm 10 reports 'No known vulnerabilities found' through the
bulk endpoint.
…own divergences

Unblocking the python conformance step exposed the next masked step: the
protocol vectors, where pre-hardening python accepts calendar-impossible
receipt timestamps (2026-02-30, month/day overflow) instead of a parse_error.
List both cases in KNOWN_RUNNER_DIVERGENCES.python — the inverted ledger goes
red the moment a listed case starts conforming, so the python hardening leaf
(which fixes the parser; verified green on its tree) is forced to remove them
and cannot forget to.
Use the public @solana/mpp/server encodeVoucherMessageBytes export in the
session-voucher preimage helper instead of a deep source import, and pass the
expiry as a bigint end to end so a > 2^53 value cannot silently lose precision
through a Number() round-trip. Hoist the boot-policy subject probes into
per-SDK constants so the git-grep subprocess runs once per SDK instead of once
per field.
EfeDurmaz16 added a commit that referenced this pull request Jul 15, 2026
…cascade root

Add the end-state seal: the cascade root lets the Rust conformance/coverage
steps and the python session leg wait on their subjects, so this leaf (the last
in the chain) asserts every such subject exists in the integrated tree — a
pending gate cannot silently outlive its purpose. Track the restructured
cascade in the rebuild script: the gate-activation root (#244) enters first and
the python leaf precedes the mpp-session leaf, mirroring the telescoped PR
chain.
The public encoder returns a Uint8Array over ArrayBufferLike, and both the
helper's declared return type and WebCrypto's BufferSource require a non-shared
ArrayBuffer backing, so the CI typecheck rejected the direct return. Copy the
encoded bytes into a fresh ArrayBuffer (the shape the file used before), keeping
the public bigint-native encoder for the content.
…subject

Same subject rule as the harness.yml python leg carried by the cascade root:
the python session settle path and its marker fixture land with the mpp
session-state leaf, and pre-hardening the settle transaction is rejected
on-chain (0x104). The matrix workflow's own copy of the leg was missed in the
first pass and stayed permanently red; it now waits on the same subject and
activates with it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant