Skip to content

perf(ci): add caching, path filtering, and nextest - #722

Closed
wpfleger96 wants to merge 1 commit into
mainfrom
ci-perf-optimization
Closed

perf(ci): add caching, path filtering, and nextest#722
wpfleger96 wants to merge 1 commit into
mainfrom
ci-perf-optimization

Conversation

@wpfleger96

Copy link
Copy Markdown
Member

Several CI jobs were compiling Rust from scratch on every push with no caching, and all jobs ran regardless of what changed — a frontend-only PR would still wait for two full musl cross-compile runs.

server-cross-compile (~7-8 min × 2 targets) and desktop-build-macos (~7-10 min) had zero cargo caching, making them the biggest uncached time sinks. Meanwhile, all 9 heavy jobs ran on every PR even when the relevant code hadn't changed.

  • Add dorny/paths-filter gating: server-cross-compile and mobile only run when their source trees change; all jobs still run on push to main/release
  • Add Swatinem/rust-cache to server-cross-compile (with per-target key to avoid matrix collisions) and desktop-build-macos
  • Move docker compose up -d earlier in desktop-e2e-integration so containers boot during pnpm/Playwright install instead of after, saving ~30-60s
  • Add Docker image caching for postgres/redis/typesense to avoid re-pulling on each E2E run
  • Add pnpm store cache to web, desktop, and desktop-e2e-integration
  • Switch unit-tests to cargo-nextest for parallel test execution; update just test-unit to auto-detect nextest with fallback to existing script for local dev

CI wall-clock time is bottlenecked by uncached Rust builds and jobs
running unnecessarily on unrelated changes. Server cross-compile
(~7-8 min × 2 targets) and Desktop Build macOS (~7-10 min) had zero
cargo caching; all jobs ran on every PR regardless of what changed.

- Add dorny/paths-filter gating so jobs only run when relevant files
  change (rust, desktop, web, mobile groups); push to main still runs
  everything
- Add Swatinem/rust-cache to server-cross-compile and
  desktop-build-macos (the two most expensive uncached jobs)
- Move docker compose up earlier in desktop-e2e-integration so
  containers boot during pnpm/Playwright install instead of after
- Add Docker image caching for postgres/redis/typesense in E2E job
- Add pnpm store cache to web, desktop, and desktop-e2e-integration
- Switch unit tests to cargo-nextest for parallel test execution
- Update Justfile test-unit to auto-detect nextest with script fallback
@wpfleger96
wpfleger96 requested a review from a team May 22, 2026 15:40
Comment thread .github/workflows/ci.yml
patchelf \
wget
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
Comment thread .github/workflows/ci.yml
- name: Start integration services
run: docker compose up -d
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
Comment thread .github/workflows/ci.yml
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1
- name: Cache pnpm store
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
Comment thread .github/workflows/ci.yml Dismissed
Comment thread .github/workflows/ci.yml Dismissed
Comment thread .github/workflows/ci.yml
mobile: ${{ steps.filter.outputs.mobile }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
@wpfleger96
wpfleger96 marked this pull request as draft May 22, 2026 15:44
@wpfleger96 wpfleger96 closed this May 22, 2026
@wpfleger96
wpfleger96 deleted the ci-perf-optimization branch May 22, 2026 15:46
ALLiDoizCode pushed a commit to toon-protocol/buzz that referenced this pull request Aug 4, 2026
* RALPH: provider surface — advertise, see jobs, quote (buzz#84)

Task: #84, part of the agent-fleet-money epic
(toon-meta#262 decision 12 / Wave 5). PR #101 shipped this ticket's
freshness invariant (providerAvailability.ts) but stopped there, citing
three blockers on the "advertise"/"see jobs"/"quote" pieces. Re-checked
every one of those blockers against the current repo and live npm before
writing any code — two no longer hold, and this ships against what's left.

Key decisions:
- **Advertise (item 1) needs no wire event.** The prior blocker was "no
  NIP-89 kind:31990 allocation" — true, and toon-meta#263 (the closed spec
  ticket this whole epic builds against) still only specifies
  request/quote/offer/result/narration, nothing for discovery. But the
  pull model this ticket also needs (item 2) makes a discovery broadcast
  unnecessary: a provider finds jobs by reading the open kind:5097 feed
  directly, so "advertise" is a local, permissionless toggle
  (providerCapabilitySettings.ts) — consistent with decision 14's "no
  allowlist, no approval step."
- **The inbound job feed (item 2) was not actually blocked.** The prior
  note said "no open-relay subscription path exists" — but buzz#85's
  buyer surface (merged before PR #101, in the same epic) already built
  exactly that (ToonEventTransport.fetchEvents/subscribeLive against
  g.toon.relay). useInboundFactoryJobs.ts reuses it unscoped by author,
  filtered client-side by matchesProviderCapability.
- **Gift-wrapped briefs (also item 2) were not actually blocked either.**
  The prior note said the only unwrap precedent (channelKeyDelivery.ts)
  hardcodes its rumor kind — true, but nostr-tools/nip59 exports a fully
  generic unwrapEvent primitive underneath it. Verified that helper is
  insecure to use directly (it decrypts both layers and discards the
  seal, so a caller never learns whether the seal's signature verifies or
  whether rumor.pubkey matches it — channelKeyDelivery.ts's own stated
  reason for not using it). unwrapFactoryJobRequest.ts duplicates that
  ~10-line authenticity check for the factory-job rumor kind rather than
  sharing code with the channel-membership trust path, a different and
  more sensitive blast radius.
- **Quote (item 3) needed no getClaimState.** The prior note conflated
  "bump the client for getClaimState" (item 3's real ask was a schedule
  builder) with reading earned balance, which this ticket never needed —
  quoting is just signing and publishing a paid write, the same
  transport.publish path postFactoryJob.ts already uses.
  factoryJobQuote.ts builds the kind:7000 status:"quote" template to the
  exact shape factoryJobFeedback.ts's parser already reads (that module's
  own doc says "it never builds a request" — this is the missing
  builder), round-trip tested against that parser directly.
- **What is still genuinely blocked, re-confirmed today**:
  sessionLeaseTtlMs (providerAvailability.ts's freshness gate) has no
  live source — pulled @toon-protocol/client@0.26.1 from npm directly and
  diffed its .d.ts; ToonChannelAccept/ParsedX402Challenge still have no
  `extra` bag, so session_lease_ttl_ms is still silently dropped during
  parsing, exactly as the last two agents on this ticket found. Rather
  than block the whole surface on that, ProviderJobsPanel gates quoting
  on FactoryJobAvailability (the coarse "is the TOON transport ready"
  signal buzz#85 already uses) instead of the full time-windowed
  freshness model, which stays ready for its caller once block#722's TTL
  reaches the client library.

Files changed: desktop/src/features/providers/lib/{providerCapabilitySettings,
providerJobMatch,factoryJobQuote,postFactoryJobQuote,
unwrapFactoryJobRequest,useInboundFactoryJobs}.ts (new, +4 test files),
desktop/src/features/providers/ui/{ProviderCapabilityToggle,
InboundJobsList,QuoteForm,ProviderJobsPanel}.tsx (new),
desktop/src/features/factory-jobs/ui/FactoryJobsScreen.tsx (Buying/Providing
tab switch).

Verified: fmt-check, desktop-tauri-fmt-check, clippy (workspace,
-D warnings), test-unit (864 tests), desktop-check, desktop-test
(4345 passed, 3 pre-existing skips), desktop-build, web-check,
web-build — all green.

Blockers/notes for next iteration: sessionLeaseTtlMs remains unwireable
(see above) — no scope change needed here once it lands, just swap
ProviderJobsPanel's availability gate to canQuoteJobs(deriveProviderAvailability(...)).
Increment scheduling in QuoteForm is manual (owner types milestone names
and prices) rather than derived from any pricing policy — toon-meta#262
explicitly leaves that undecided ("a rate card cannot price 'understand
my weird codebase'"), so this is a deliberate scope stop, not a gap.
No increment-offer/narration/result publishing is built — out of scope
per the issue's own "What" list (delivery is rig#52's job). Gate-pass
rate and reputation display for a provider's own history are not
surfaced here (buyer-side only, from buzz#85).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>

* Fix unsafe verifyEvent cast in unwrapFactoryJobRequest

asLayer's validator and SealedLayer type never checked for id/sig, so
the seal-verification call papered over the gap with a blind
`as unknown as Parameters<typeof verifyEvent>[0]` cast instead of
validating what verifyEvent actually requires. Split into asRumor
(rumor fields only) and asSealedLayer (adds the id/sig fields the
seal must carry), so verifyEvent's argument is now honestly typed
and validated rather than force-cast.

Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

2 participants