FE-1225: Proxy detached Petrinaut optimization runs through NodeAPI#9067
FE-1225: Proxy detached Petrinaut optimization runs through NodeAPI#9067kube wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
PR SummaryMedium Risk Overview Shared streaming/validation logic is extracted into Reviewed by Cursor Bugbot for commit a39600b. Bugbot is set up for automated code reviews on this repo. Configure here. |
fec71b8 to
760a6f0
Compare
8763d1f to
a39600b
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a39600b. Configure here.
| error: "An optimization is already running for this account", | ||
| }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Detached routes skip global cap
Medium Severity
The legacy POST /optimize handler enforces MAX_CONCURRENT_OPTIMIZATIONS (4) via a module-local counter, but the new detached create and event-attach handlers never participate in that limit. Many accounts can run detached optimizations concurrently while a fifth legacy request gets 502-style “optimizer busy,” overloading the optimizer beyond the cap the legacy path was meant to enforce.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit a39600b. Configure here.
| // consumed run keeps its entry alive for the whole study. | ||
| owner.lastSeenAt = now(); | ||
| } | ||
| return owner; |
There was a problem hiding this comment.
Ownership TTL blocks late re-attach
Medium Severity
Run ownership entries expire after OPTIMIZATION_RUN_OWNER_TTL_MS of inactivity based on lastSeenAt, which is only refreshed on get() at attach/cancel time—not while a client is disconnected mid-run. If the user waits longer than that before re-attaching, NodeAPI returns 404 even when the optimizer still has the run and event log, so reconnect/resume fails at the proxy layer.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a39600b. Configure here.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## cf/fe-1224-petrinaut-detached-runs #9067 +/- ##
======================================================================
+ Coverage 59.24% 59.30% +0.05%
======================================================================
Files 1391 1400 +9
Lines 135208 135510 +302
Branches 6242 6315 +73
======================================================================
+ Hits 80100 80358 +258
- Misses 54143 54156 +13
- Partials 965 996 +31
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|


🌟 What is the purpose of this PR?
Proxies the detached, reconnectable optimization runs (#9064) through NodeAPI so the browser can create a run, attach/re-attach to its NDJSON event stream with a cursor, and cancel it explicitly — with per-user ownership enforced at NodeAPI. This is the middle layer of the fix for the staging
network error: a transport reset now costs one re-attach instead of the whole study.🔗 Related links
🚫 Blocked by
🔍 What does this change?
apps/hash-api: three new authenticated routes behind the existing rate limiter —POST /api/petrinaut-optimizer/optimize/runs→201 {runId}(manifest validation and error mapping identical to legacy; client-disconnect windows handled, including cancelling a run created for a client that already left),GET .../optimize/runs/:runId/events?cursor=N→ NDJSON attachment stream with the legacy backpressure/timeout machinery (each event carriesseq),DELETE .../optimize/runs/:runId→ idempotent cancel. The legacy streaming route is unchanged (deprecated in docs) and its 16 tests pass as-is.runId → account) withlastSeenAt-based TTL; 404-not-403 for other users' runs; ownership released only when a terminal event is delivered (an abort during the terminal write keeps the entry so one re-attach can replay it); a stale owned run (e.g. reaped upstream after the browser lost its id) is liveness-probed against the optimizer's/status/{run_id}on the next create and released if terminal — so an orphan can never 429-lock an account.@local/petrinaut-optimizer-client:createPetrinautOptimizationRun,attachPetrinautOptimizationRunStream,cancelPetrinautOptimizationRun(204+404 both resolve),getPetrinautOptimizationRunStatus; the SSE decoder threadsid:→seq(strict^\d{1,15}$), mapsevent: cancelledto a terminaloptimization_cancellederror event, and supports attachment mode (no syntheticstarted; best-so-far aggregation skipped whendirectionis unknown —best: null, the browser computes its own).@hashintel/petrinaut-core: optionalseqon the canonical event schema (changeset included).Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
completeevents carry counts observed since the cursor (the frontend keeps its own accumulated totals);requestedTrialsis the true total.seqschema addition also exists identically on the frontend branch — one-line dedupe at merge time.🛡 What tests cover this?
apps/hash-apipetrinaut-optimizer: 55 vitest — create happy path + both disconnect windows, cross-family single-flight both directions, ownership TTL touch/expiry, terminal-delivery-before-release (abort-during-terminal-write regression), cancelled-terminal release, stale-owner liveness probe (terminal / unknown / still-running / probe-unreachable), cursor validation, 404-not-403, route ordering.@local/petrinaut-optimizer-client: 40 vitest incl. the codegen drift gate — seq threading, junk-id rejection, cancelled-event adaptation, attachment mode, run status.@hashintel/petrinaut-core: 833;apps/petrinaut-opt: 129 pytest (consumed unchanged).❓ How to test this?
POST /api/petrinaut-optimizer/optimize/runswith a valid manifest (authenticated) → noterunId.GET .../optimize/runs/{runId}/events, kill the connection mid-run, re-attach with?cursor=<last seq>→ resumes without duplicates; the run never stopped.DELETEthe run → promptoptimization_cancelledterminal event on the next attach; account immediately free for a new create.🤖 Generated with Claude Code