Skip to content

ci: routing, lint and parity gates, and upgrade-harness gaps #2070

Description

@hmzakhalid

Summary

This issue lists gaps in CI routing, lint and parity gates, and the upgrade test harness. Each item was checked against main at 29fbd7152 on 2026-09-30.

CI routing and gates

  • The cross-version upgrade scenarios never run in CI (medium).
    • What happens: tests/integration/upgrade.sh supports the all, mixed, late, rollback and mixed-dkg scenarios (:38). CI runs only base and persist (.github/workflows/ci.yml:795), and no workflow provides the old and new binaries.
    • Fix: add a release-candidate job that builds the previous release and the candidate, then runs the upgrade scenarios.
  • Circuit CI routing misses the builder, the artifact store and some verifier inputs (medium).
    • What happens: the circuit path filter (ci.yml:101) omits scripts/build-circuits.ts, scripts/circuit-artifacts.ts, the Honk verifier paths and scripts/protocol/constants.ts. build_circuits is also computed without the contracts filter (:251). A pull request that changes only one of these files skips the circuit and verifier-sync checks. Release-candidate runs force them (:213).
  • Both end-to-end jobs are conditional (low).
    • What happens: crisp_e2e (ci.yml:1079) and template_integration (:1654) run only when their path filters match. If they are required checks, a skipped job does not show whether the covered code still works.
    • Fix: add one result job that always runs and fails when a covering job fails or is skipped unexpectedly.
  • No Clippy in CI (low).
    • What happens: rust:lint is cargo fmt -- --check (package.json:43, ci.yml:321). Nothing runs Clippy.
  • Root ESLint runs only in the pre-push hook (low).
    • What happens: pnpm lint (package.json:25) runs from .husky/pre-push:3. CI runs only the Solidity linter (ci.yml:647).
  • The address-consumer check runs only in the pre-push hook (low).
    • What happens: check:addresses (package.json:33, scripts/check-addresses.ts:22) finds stale addresses in docs, dashboards and examples. CI checks only that the manifest is fresh (ci.yml:663).
  • The agent-harness job skips the committee parity matrix (low).
    • What happens: .github/workflows/agent-harness.yml:34 runs scripts/check-committee.sh without Nargo or the generator. The script then prints a skip note and passes (check-committee.sh:433-460). The circuit job's rebuild covers only the default committee (ci.yml:1279-1283).
  • Solhint allows 10 warnings (info).
    • What happens: packages/interfold-contracts/package.json:192 sets --max-warnings 10. Lower it to zero if a zero-warning policy is wanted.
  • The doc-sync check accepts any harness document change (low).
    • What happens:
      • scripts/check-doc-sync.sh:86 passes as soon as any file in the harness document set changed. That file need not be the one that describes the changed code.
      • The watch list (:32) omits crates that the harness documents, including data-availability, support and sdk.
      • agent/invariants/00_INDEX.md:39 routes data-availability work to the protocol and flow-trace documents.
  • The invariant-reviewer agent has unrestricted shell access (low).
    • What happens: .claude/agents/invariant-reviewer.md:4 grants Bash with no command allowlist, although the prompt says the reviewer is read-only (agent/prompts/invariant-reviewer.md:8).
    • Fix: allow only git and file reads.
  • Cross-layer constants have no complete parity gate (low). This is tracked in Centralize protocol constants and enforce cross-layer drift checks #1835. The current values agree:
    • E3 stage and failure ordinals (Solidity, Rust, SDK, dashboard);
    • the message width of 100 (Noir, Rust, the verifiers, the CRISP SDK and the program);
    • the dashboard's committee thresholds (packages/interfold-dashboard/src/lib/e3.ts:69, which check-committee.sh does not cover);
    • the proof-layout offsets. test/BfvVkBindingIntegration.spec.ts:340 skips verification for stale fixture lengths instead of failing.

Integration harness

  • fns.sh kills every process that matches anvil (medium).
    • What happens: tests/integration/fns.sh:62 and :269 run pkill -9 -f "anvil", and :341 calls kill_em_all when the file is sourced. Any Anvil on the machine dies, including one in another checkout. The helper also matches broad interfold and hardhat patterns.
    • Fix: record child PIDs, or use a dedicated process group, and stop only those.
  • Upgrade verdicts ignore unexpected ERROR lines (low).
    • What happens: tests/integration/upgrade.sh:351 fails only on a few crash patterns. A run with an unexpected error, a rejected share or an accusation can still pass.
    • Fix: classify errors per phase, with a narrow allowlist of expected transient ones.
  • Rollback is tested before the ciphertext exists, not after a share was sent (low).
    • What happens: the rollback happens 30 seconds after effects are enabled (upgrade.sh:275), and the ciphertext is published later (:315). No scenario rolls back a node that has already sent its decryption share.
  • No backup-and-restore scenario (low).
    • What happens: upgrade.sh:110 records a file inventory (tests/integration/lib/state_inventory.py:60), not a restorable backup. Nothing restores a stopped node from a backup and checks that it completes.
  • The upgrade scenarios fix the committee (low).
    • What happens: the local randomness word has no seed override (packages/interfold-contracts/tasks/interfold.ts:153), so every run selects the same members and aggregator.
    • Fix: add a local-only seed input, and run a small seed matrix.
  • No scale profile for the upgrade harness (low).
    • What happens: upgrade.sh uses one fixed configuration (:47) and candidate assignment (:51). It has no production-sized, secure-parameter profile, and no variant that starts from v0.17.0.
    • Fix: add scale and old-version options to upgrade.sh, not separate scripts.
  • Local test chains disable the clock-drift fence (low).
    • What happens: crates/ciphernode-builder/src/ciphernode_builder.rs:1534 removes the limit when every chain is 1337 or 31337, so the integration suite (tests/integration/interfold.config.yaml:4) never runs with the production drift policy. The public-chain case has a unit test (:2286).
  • No tool shows the per-E3 state of a stopped node (low).
    • What happens: crates/entrypoint/src/validate.rs checks schema, cursors and open loops. Nothing reports, per E3, the stage, whether the keyshare snapshot loads, or the recovery status. That is what an operator needs before an upgrade.

Test coverage

  • Released event, wire and snapshot layouts have few fixed-byte fixtures (medium).
    • What exists: fixed-byte fixtures cover bond-owner state (crates/sortition/src/sortition/bond_owners.rs:111), one CommitteeFinalized variant, node-role bytes, and a sync envelope with a u64 payload (crates/net/src/network_sync/wire.rs:297).
    • What is missing: fixtures for the other InterfoldEventData variants, the real sync payloads, the gossip envelope (wire.rs:36), and the keyshare and aggregation recovery snapshots.
    • Why it matters: v0.19 must keep all of these byte-compatible, and a field or variant reordering would not fail any test.
  • The DHT prune tests check a copied closure (low).
    • What happens: crates/net/src/net_interface.rs:2060, :2094 and :2147 build a standalone store and run a copy of the retain logic. Removing the production call (prune_expired_dht_records, :1652, used at :1710 and in the periodic loop) would not fail them.
  • DKG-start paths in the keyshare actor lack failure-driven tests (low). All the tests below are in crates/keyshare/src/threshold_keyshare/tests.rs.
    • Pending timing read: no test holds the timing read pending while peer inputs arrive. The early-selection helper's reader resolves at once (:532).
    • Timing-read error: nothing injects a failed read to cover the retry (handlers.rs:153-163).
    • Initialization: nothing injects a failure in either path, the cached-timing one (handlers.rs:136) or the fresh-read one (:161-165). So idempotency is untested: one keypair, no duplicate collectors or key intents.
    • Disk recovery: the fix(keyshare): recover early and restarted DKG inputs [skip-line-limit] #2058 recovery tests use in-memory stores (:652-686), not disk hydration through ThresholdKeyshareExtension::hydrate (crates/keyshare/src/ext.rs:132).
    • Replay: effects-disabled replay of the early inputs through ReplaySpool and the production effect gates is not covered (:749-800 seed state instead).

Release tooling

  • The dashboard's update check ignores pre-release ordering (low).
    • What happens: crates/dashboard/src/updates.rs:168-176 keeps only three numeric parts. 0.19.0-dev.N and 0.19.0 then compare as equal, and dev.2 against dev.10 is not compared correctly.
  • Release-channel tests stop short of the full wiring (low).
    • What happens: scripts/release.test.mjs covers publication gating. Nothing exercises prepareRelease and prepareReleaseAssets end to end for a stable candidate and a dev candidate, including the channel outputs, the npm tags and the pre-release install commands.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions