ci(harness): radar layers + anti-false-green gates + cross-SDK protocol conformance#219
ci(harness): radar layers + anti-false-green gates + cross-SDK protocol conformance#219EfeDurmaz16 wants to merge 37 commits into
Conversation
Radar L4: line/region coverage proves a line ran, not that a test would fail if it were wrong. Add cargo-mutants as an objective 'guard coverage meter' scoped to the x402 exact structural verifier (verify.rs). - rust/.cargo/mutants.toml: scope to the exact verifier, x402 feature only - .github/workflows/mutants.yml: nightly/on-demand advisory sweep, uploads the survivor list and warns on surviving mutants - harness/mutants/run.sh: local driver Baseline sweep: 103 mutants, 69 caught, 30 missed, 4 unviable => mutation score 69.7%. The single-transaction transfer guards (mint/recipient/amount/authority mismatch) are killed; survivors cluster in the on-chain Raw/compiled transfer matcher and the versioned-tx path, which have no negative tests. Tooling/CI only; no production logic changed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ol conformance Harden the pay-kit CI so the bug classes that shipped before (fabricated deposit, fail-open store, compute-budget drain, canonical-JSON divergence, dead tests) cannot recur silently. Language-agnostic infra only (workflows + harness/); the SDK behaviour fixes these gates enforce land in the per-language PRs, so some gates go RED until those merge (documented per leg). Radar layers L1-L8: metamorphic, concurrency/race, property, mutation (guard meter), fuzz, on-chain differential, semgrep, reference-oracle differential. Anti-false-green meta-gates (the audit's #1 finding: ~21 harness tests were dead in CI): - ci-coverage-gate: every harness/test/*.test.ts must run in a workflow or be documented-exempt; strips full-line AND inline YAML comments so a commented reference is not miscounted as wired. - matrix-coverage-gate: 118-cell path x outcome matrix; an undocumented uncovered cell is RED. - vector-accounting: every vectors/*.json maps to an executing consumer. Cross-SDK protocol conformance (mpp-tools canonical vectors): - protocol-conformance now drives the FULL adversarial case set (not a success-only smoke slice) through every SDK runner; confirmed per-SDK divergences are tracked in KNOWN_RUNNER_DIVERGENCES and wired into 6 per-language legs (go/rust/python/php/lua/ts). The spawned runner gets a hard timeout so a hung runner fails RED, never a silent pass. ruby is deferred pending ruby-3.3 verification + a runner stdin-encoding fix. False-green fixes: - compute-budget-caps: correct the Python cap path (was a nonexistent path with optional:true, a silent skip) so a Python CU-cap drift now REDs. - go.yml: assert-run-count on the Go e2e leg so a selector/env drift that runs zero payment pairs fails instead of passing green. - value-binding-verify: consume the authored value-binding vectors (expect-reject invariant + pinned roster) so a deleted or flipped vector REDs. - boot-policy: fail-closed store probes (go/ts/python) + a git-grep roster-honesty check that REDs if a skipped SDK starts wiring the opt-in. Also: shared canonical vectors, harness servers + session clients, the spawned protocol-runner ABI + manifests, semgrep L7 rules + fixtures.
Resolve the one semantic conflict in harness/python-server/server.py by keeping main's client-submit session model (main #218 adopted the epoch-addressed payment-channels program; the client derives the channel PDA from the challenge recentSlot). The branch's earlier server-broadcast open variant targeted the pre-openSlot program and is superseded. All other files (workflows, harness drivers, vectors) auto-merged.
…n checkout Independent second-model review of the pushed PR found #219 red in CI before the gates could run. Fixes: - Rust CI needs a committed lockfile: ci.yml runs cargo with --locked but rust/Cargo.lock was never committed, so a fresh runner exits "cannot create the lock file ... --locked was passed". Commit the resolved lockfile. (F3) - Go coverage gate was broken + weaker than advertised: go.yml called a missing scripts/check_coverage_test.sh, and check_coverage.sh accepted a per-file floor argument but only enforced the aggregate. Rewrite check_coverage.sh (pure awk over the raw profile) to enforce BOTH an aggregate and a per-file floor, and add the self-test go.yml invokes. A 20%-coverage file can no longer hide behind a 91% aggregate. (F2) - Conformance could pass with zero SDK runners: conformance.test.ts filtered runners by MPP_CONFORMANCE_LANGUAGES without asserting the filter matched, so a typo or a deleted manifest ran zero SDKs green. Add a runner-selection guard. Also add rust to EXACT_VERIFIER_LANGUAGES (it has a server verifier + is wired), so a Rust exact runner that stops executing the fund-safety vectors REDs. (F4) - Harness typecheck referenced unexported symbols: session-voucher-verify.test.ts imports verifyVoucherForChannel / VoucherRejectReason, which are defined in session/voucher.ts but were not re-exported from @solana/mpp/server, so the harness typecheck failed and blocked every downstream gate. Re-export the behaviour-free voucher verifier surface. (F1)
…round 2 Harness-scope hardening (the per-language-dependent findings — TS client Receipt re-export, rust conformance_runner example, Python client-submit settle, and the Go x402 conformance driver + verifier guard — are left to the per-language PRs by the agreed merge-order; not patched here): - protocol-conformance: add the runner-selection guard so a MPP_CONFORMANCE_LANGUAGES typo (e.g. rustt) that matches zero spawned SDK runners REDs instead of passing on the in-process TS block alone. (F7) - go.yml: pin the Go e2e harness run-count to --exact 18 (was --min 12), so dropping the x402-upto slice can no longer pass on the older 12 pairs. (F4) - x402 accept-oracle: bind resource / maxTimeoutSeconds / extra / decimals / tokenProgram (was only network/amount/payTo/asset), matching the production Rust structural backstop; a credential drifting only extra now rejects. (F6) - matrix-coverage-gate: enumerate the applicable (path,outcome) cells and assert each stays covered / documented-gap / NA, so deleting one covered cell REDs instead of hiding behind the path+outcome accounted elsewhere. (F8) - semgrep: add a Python fail-open default-store rule + bad/good fixtures (the scan already ran python but had only go + ts/js rules). (F9) Verified: protocol-conformance runner-selection guard REDs on a bogus allowlist; x402-v1-exact + matrix-coverage-gate green (154); semgrep self-test green (bad fixture hits, good fixture clean).
…nd-rolled HMAC Maintainer note: the harness should pull protocol logic from the libs, not re-implement it. The TS reference runner was the one place that hand-rolled the challenge-id HMAC while every other per-language runner calls its SDK's real compute-id. Rewire generateChallengeId to a thin adapter over mppx's public `Challenge.from(...)` (reading `.id`), used for 24/25 canonical challenge-id vectors. The one remaining case (a raw, non base64url-JSON `opaque` string) keeps the canonical HMAC as a documented, deterministic fallback: mppx's public `Challenge.from` takes structured `meta` and re-serializes it, so it cannot reproduce a raw pre-serialized opaque id. TODO(mppx): drop the fallback once mppx exposes a public `computeId` that accepts the canonical pre-serialized opaque — then all 25 come from the lib. base64url stays Node Buffer (a platform primitive). Verified: standalone probe over challenge-id.json = 25/25 match (lib 24, fallback 1).
|
@greptile-apps please review |
Greptile SummaryThis PR installs a comprehensive CI harness enforcement layer — Radar L1–L8 (metamorphic, concurrency, property, mutation, fuzz, on-chain differential, semgrep, oracle-differential), three language-agnostic meta-gates that prevent dead tests from silently passing, and cross-SDK protocol conformance runners that exposed the same codec-bug family (empty realm/intent, CRLF-in-description, unescaped escapes) across Go, Rust, Python, and Ruby. No SDK runtime behaviour changes are included; all changes are in the harness, CI workflows, scripts, and supporting probe fixtures.
Confidence Score: 5/5Safe to merge as a CI/harness-infrastructure PR; no SDK runtime behaviour is changed, and all three language-agnostic meta-gates are confirmed green independently. The change is purely additive harness infrastructure with no SDK runtime changes. Prior-thread issues are all addressed. The remaining observations are maintenance-quality nits with no effect on runtime correctness or security. harness/test/protocol-conformance.test.ts — KNOWN_RUNNER_DIVERGENCES.typescript spread copy could silently diverge from KNOWN_TS_DIVERGENCES as SDK fixes land. harness/test/value-binding-verify.test.ts — hardcoded program address and discriminator should stay in sync with generated SDK constants. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph Meta[Meta-gates: language-agnostic, always GREEN]
A[ci-coverage-gate]
B[matrix-coverage-gate]
C[vector-accounting]
end
subgraph Conformance[Cross-SDK Protocol Conformance]
D[canonical vectors]
E[in-process TS runner]
F[spawned runners: go/rust/python/php/lua/ts]
D --> E
D --> F
end
subgraph FG[False-green Fixes]
G[assert-run-count]
H[compute-budget-caps]
I[value-binding-verify]
J[boot-policy]
end
subgraph Radar[Radar L1-L8]
K[L1-L3: metamorphic/concurrency/property]
L[L4-L6: mutation/fuzz/on-chain diff]
M[L7: semgrep fail-open + nonconstant-compare]
N[L8: oracle differential]
end
A --> CI{CI gate}
B --> CI
C --> CI
E --> CI
F --> CI
G --> CI
H --> CI
I --> CI
J --> CI
K --> CI
L --> CI
M --> CI
N --> CI
%%{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
subgraph Meta[Meta-gates: language-agnostic, always GREEN]
A[ci-coverage-gate]
B[matrix-coverage-gate]
C[vector-accounting]
end
subgraph Conformance[Cross-SDK Protocol Conformance]
D[canonical vectors]
E[in-process TS runner]
F[spawned runners: go/rust/python/php/lua/ts]
D --> E
D --> F
end
subgraph FG[False-green Fixes]
G[assert-run-count]
H[compute-budget-caps]
I[value-binding-verify]
J[boot-policy]
end
subgraph Radar[Radar L1-L8]
K[L1-L3: metamorphic/concurrency/property]
L[L4-L6: mutation/fuzz/on-chain diff]
M[L7: semgrep fail-open + nonconstant-compare]
N[L8: oracle differential]
end
A --> CI{CI gate}
B --> CI
C --> CI
E --> CI
F --> CI
G --> CI
H --> CI
I --> CI
J --> CI
K --> CI
L --> CI
M --> CI
N --> CI
Reviews (3): Last reviewed commit: "ci(harness): close R1/R6/R7/R9/R11/R12/R..." | Re-trigger Greptile |
…tate field Greptile P2 findings (all harness-scope): - boot-policy commandExists: pass cmd via the arg vector, not interpolated into `sh -c` (injection-safe pattern even though callers pass literals). - boot-policy tryBuildGoServer: a `go build` failure when Go IS installed is now a hard error, not a caught->null silent skip (the false-green class this suite fights). Genuine Go-absence still skips. - ci-coverage-gate: assert every CI_EXEMPT entry still names a real test file, so a stale exemption for a renamed/deleted test REDs. - assert-run-count: reject a non-positive floor (--min 0 / --exact 0 / negatives) with a clear diagnostic; a 0 floor would let a zero-execution leg pass. - protocol-conformance: the runner-selection guard now asserts >= 1 spawned runner in BOTH allowlist and all-runner modes (was a no-op when the env var was unset, so deleting every manifest could run zero spawned SDKs green). - python-server: note that _handle_with_fresh_rpc reaches into the session's private _rpc (no public per-request rpc-injection API); harness-only, not for production. Also fix a stale field the current ChannelState surfaced: session-voucher-verify and value-binding-verify used `finalized`, but ChannelState was renamed to `sealed` (and the voucher reject reason to `channel-sealed`). This unblocks the harness typecheck.
|
@greptile-apps please re-review — all 6 P2 findings addressed in 983d51e (replies inline). |
The min NaN check (min = exact when --exact is set) already catches a non-numeric --exact and throws first, so the later dedicated --exact NaN branch was dead code. Greptile 5/5 review flagged it as stylistic; removed.
|
Thanks — 5/5 confirmed. Addressed the one remaining stylistic note in c474862: removed the unreachable |
… gaps Bedrock harness audit (codex, verified vs this branch) found these still open; each fix ships with a planted-regression test proven to fail if the guard is removed. - R1: add top-level 'permissions: contents: read' to every non-publish workflow (a hygiene guard test fails if one is missing). pypi-publish.yml is left for a separate release-workflow review. - R6: per-runner nonzero-executed-vector guard so a non-exact runner can no longer unsupported-mode-skip every vector and pass green. - R7: validate spawned-runner identity. Each conformance/protocol runner now emits its language/implementation and the harness rejects a missing or mismatched identity (a manifest pointed at the wrong runner fails). The strict guard is why the per-language conformance runners (go/python/php/ruby/lua/ swift/rust) each gain a one-line identity emission -- test-driver glue, no SDK behavior change. - R9: resource budgets on vector loading and constructed-wire materialization (file bytes, JSON depth/width, scenario count, repeat count + materialized length) so a hostile vector fails deterministically before any runner spawns. - R11: spawned protocol smoke now covers all nine declared ABI operations (adds credential.format / receipt.format) with a one-case-per-operation assert. - R12: REQUIRED_REJECT_CODES accounting for the shared/non-exact RejectCode union. - R13: canonical-code coverage accounting diffing CANONICAL_CODES against behavioral scenarios, with owned/dated exceptions for the three codes not yet behaviorally covered. Verified: MPP_CONFORMANCE_LANGUAGES=typescript vitest (conformance + protocol-conformance + ci-coverage-gate) 312 passed / 2 skipped; tsc --noEmit clean.
Re-target the Go idiomatic + solana-go/v2 work onto solana-foundation#219 (harness base) per the per-language merge model. Conflict resolution: - go/cmd/protocol-runner/main.go: keep solana-foundation#219's Track-A `Language` field (conformance identity emission) and the idiomatic `any` over `interface{}`. - harness go.mod/go.sum: keep the solana-go/v2 migration, drop the gagliardetto v1.22.0 line solana-foundation#219 carried from main; go mod tidy cleans the stale go.sum entry. Verified: go build/vet green across go SDK + both harness modules. Conformance TestSeededVectorsConform shows exactly solana-foundation#219's inherited 5-vector red (F2 verify-x402-transaction driver gap + 3 canonical-JSON residuals), unchanged by this merge; those are fix(go)'s scope.
|
@lgalabru ready for review — this is the harness/CI base for the per-language re-delivery. The SDK fix PRs stack into it and turn its inherited red green as they land:
One base note for your call: this branch trails |
|
@greptile-apps please review |
The solana-foundation#219 per-file coverage gate (75% floor) flags the pre-existing protocols/mpp/core/mpp.go re-export facade at 0% (untested since main). Add mpp_test.go exercising every facade function via delegation / round-trip so the Go tests coverage gate passes on this branch.
EfeDurmaz16
left a comment
There was a problem hiding this comment.
Verdict: please fix before merge. Thanks for pushing this harness base; the overall direction is useful, but I found two small blockers before I would be comfortable merging it. One runner manifest currently cannot start, and the x402 conformance dispatch looks broader than the mode capabilities declared for some SDKs. The current GitHub check state is also not diagnostic because the root jobs were cancelled and downstream legs skipped, so I would rerun after these fixes rather than treating that red as the intended merge-order signal.
| @@ -0,0 +1,6 @@ | |||
| { | |||
| "language": "rust", | |||
| "command": ["cargo", "run", "-q", "-p", "solana-pay-kit", "--example", "conformance_runner", "--features", "x402,client"], | |||
There was a problem hiding this comment.
This points at --example conformance_runner, but the Rust crate only declares protocol_runner; the Rust conformance lane fails before exercising any vectors. Please either point this manifest at the existing runner or add the missing example.
| const expectedIdentity = reportsAs ?? language; | ||
| const executedByGroup = new Map<string, number>(); | ||
| for (const vector of vectors) { | ||
| if (intents.includes(vector.intent)) { |
There was a problem hiding this comment.
This filters only by intent, so runners like Go/Python still receive verify-x402-transaction vectors even though the capability registry only advertises verify-transaction for them. Please make the manifest/capability model agree or filter by mode so the gate fails on real coverage, not dispatch drift.
EfeDurmaz16
left a comment
There was a problem hiding this comment.
Verdict: please fix before merge. I rechecked the current head and the two unresolved runner/dispatch blockers remain reproducible. I also found two additional anti-false-green gaps: the Python boot-policy probe is skipped in CI, and an empty Go coverage profile scores 100%. The live Actions state is still non-diagnostic because root jobs were cancelled and dependent jobs skipped, so please rerun after these are addressed.
| test/x402-upto-verify-open.test.ts \ | ||
| test/x402-upto-over-ceiling.test.ts \ | ||
| test/value-binding-verify.test.ts \ | ||
| test/boot-policy.test.ts \ |
There was a problem hiding this comment.
boot-policy.test.ts skips Python when uv is absent, but this job does not install uv and no Python-provisioned workflow runs this file, so the Python fail-closed probe remains dead in CI. Please install uv here or run this suite in a Python-provisioned job.
| } | ||
| END { | ||
| rc = 0 | ||
| agg = g_total > 0 ? 100 * g_cov / g_total : 100 |
There was a problem hiding this comment.
An empty coverage profile is treated as 100%, so a package-selection or instrumentation failure can pass both coverage gates without measuring any code. Please fail when g_total == 0 and add an empty-profile self-test.
There was a problem hiding this comment.
disclaimer: i'm not arguing with myself. GPT-5.6 is reviewing the PR.
…216 equivalence ledger Those five redelivery PRs landed on main rather than the #219 line, so their open_pr followUps are corrected from 'Land PR #X' to note the merge and the revalidation that follows when #219 rebases onto main. Validator + self-test still pass (113 commits, 237 paths accounted for).
Makes CI catch the bug classes that shipped green before — fabricated-deposit,
fail-open store, compute-budget drain, canonical-JSON divergence, and ~21 harness
tests that were dead in CI. Infra only: no SDK behaviour changes here — the
fixes these gates enforce ship in the per-language PRs.
Gates that encode fixed SDK behaviour go RED on
mainuntil the per-languagefix PRs merge (each such leg says so in a comment). Merge the per-language PRs
first, then this. The three language-agnostic meta-gates are green regardless.
What's in it
ci-coverage-gate— every harness test runs in a workflow or is documented-exempt ·matrix-coverage-gate— 118-cell path×outcome matrix ·vector-accounting— every vector has a live consumerKNOWN_RUNNER_DIVERGENCES; hung-runner hard timeoutassert-run-count(was passable with 0 payment pairs) · value-binding vectors now actually executed · fail-closed store boot probesCross-SDK bug matrix this surfaced
Running the canonical vectors against every SDK (not just TS, as before) exposed
the same bug family across SDKs — each fixed in that SDK's per-language PR:
Where to look
harness/test/{ci-coverage-gate,matrix-coverage-gate,vector-accounting}.test.tsharness/test/protocol-conformance.test.ts+harness/src/protocol/runners/spawn.tsVerify
→ 410 passed / 6 skipped. Surfaced by a multi-agent audit + two independent
second-model review rounds (the second caught 3 issues in the first remediation,
all fixed + re-verified).