[4/6] refactor(pg): row-based enqueue target, needs view and settle hand-off (no behaviour change)

BN
5d ago
+355-95

Stacked on #482, merge in order. Stack: 4/6, splitting #479.

Summary

Groundwork for the job-scoped promotion in #5 and #6. No behaviour change intended: promotion still runs the shared sweep over the loaded graph. The only new code that nothing calls yet is NeedsView's loader.

What changed

  • on_job_enqueued takes a row-based EnqueueTarget instead of a RunGraph (control/pg/dispatch.rs, lifecycle.rs). Callers that hold a graph build the target from the node; lifecycle.rs builds it from the job's own row, so enqueueing no longer needs a graph in hand.
  • dependency_decision, hydrate_needs_context and the needs context builders read the jobs a dependent names through a NeedsSource instead of a whole RunRecord. NeedsView loads exactly the rows one job's needs reach (matched by id or matrix base, following the matched rows' own needs transitively) with one recursive query (control/pg/needs.rs).
  • refresh_remaining_needs returns the blocked dependents with no needs outstanding instead of a bool, and the settle outcome carries them (and the cohorts a freed max-parallel slot can feed) by id to one promote_after_settle step, the single place the completion paths promote from. Existing refresh_remaining_needs tests assert the returned ids.
  • Docs: step 8 and step 11 of the settle flow in internal/settle-redesign.md.

Not included

Job-scoped admission (#5) and dependent and waiter promotion (#6) use these pieces.

Test plan

On this PR's tip, against a disposable Postgres 17 (max_connections=2000, PRELOOP_PG_WRITERS=3 PRELOOP_PG_READERS=3, PRELOOP_TEST_POSTGRES_URL set):

  • cargo fmt --all --check: clean
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo test --locked -p preloop-runner-server --lib -- control::: 351 passed, 0 failed, 0 skipping: lines (same count as #3: the refactor adds no tests)

just test-ci and the local engine CI run were not run on this PR's tip: the laptop engine's runner pool does not advertise the preloop-cpane label that ci.yml requires, so jobs fail at enqueue. The checks on this PR are the first full CI run for these commits.

Stack

  1. #480 fix(pg): max-parallel stall, cohort waiter retry, nested-caller fold, non-terminal guard
  2. #481 fix(pg): deferred reusable callers
  3. #482 refactor(pg): remove the in-memory completion path
  4. #483 refactor(pg): row-based enqueue target, needs view, settle hand-off (no behaviour change)
  5. #484 perf(control/pg): max-parallel admission over rows
  6. #485 perf(control/pg): dependents and job waiters over rows

Each PR's base is the previous PR's branch, so each shows only its own diff. Supersedes #479 (closed).