Skip to content

feat(registry,api): harden warm-pool recovery, admission, and runtime shedding#525

Open
Gajesh2007 wants to merge 11 commits into
masterfrom
fix/warmpool-openpool-ops
Open

feat(registry,api): harden warm-pool recovery, admission, and runtime shedding#525
Gajesh2007 wants to merge 11 commits into
masterfrom
fix/warmpool-openpool-ops

Conversation

@Gajesh2007

@Gajesh2007 Gajesh2007 commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

This PR makes the coordinator-B/open-pool layer safe to merge after PRs #523 and #524. It restores warm-pool recovery on lightly busy providers, adds an optional box-wide admission cap, makes runtime model shedding atomic across admission and queued work, keeps permanently undersized pools on the 503 path, and adds the operational telemetry and runbook needed for the v0.7.5 release train.

All new routing behavior is either default-preserving or explicitly operator-gated:

  • EIGENINFERENCE_WARM_POOL_ALLOW_BUSY_LOAD_MAX defaults to 0 (the historical fully-idle rule).
  • EIGENINFERENCE_COMBINED_ADMISSION_CAP defaults to false.
  • Runtime shedding changes only when an operator calls PUT /v1/admin/reject-models or the startup reject set is non-empty.

Before

flowchart TB
  subgraph Behavior[Behavior]
    B1[Warm-pool tick] --> B2[Add coordinator pending and backend running/waiting]
    B2 --> B3[Mirrored work can exceed the busy limit]
    B3 --> B4[eligible_cold stays zero and MIN_WARM cannot recover]
    B5[Runtime shed PUT] --> B6[Admission set changes]
    B6 --> B7[Queued alias request can still dispatch]
    B8[Model A fills a box] --> B9[Independent per-model cap can still admit model B]
  end
  subgraph Code[Code]
    C1[warmPoolCandidateReasonLocked] --> C2[pendingCount plus backend slot load]
    C3[Server rejectModels map] --> C4[Queue sweep by concrete queue key]
    C5[hasConcurrencyHeadroomForModelCapResolvedLocked] --> C6[Per-model cap only]
    C7[HasCapableProviderForModel] --> C8[No cold hardware-fit check]
  end
Loading

After

flowchart TB
  subgraph Behavior[Behavior]
    B1[Warm-pool tick] --> B2[Use max of coordinator and backend occupancy]
    B2 --> B3{No-eviction memory and resident-slot headroom?}
    B3 -- yes --> B4[MIN_WARM can load a lightly busy provider]
    B3 -- no --> B5[Fail closed without sending a doomed load]
    B6[Runtime shed PUT] --> B7[Atomically replace admission and queue snapshots]
    B7 --> B8[Matching alias/build waiters return model_shed 429]
    B9[Model A fills a box] --> B10[Optional combined load over quality-concurrency cap rejects B]
  end
  subgraph Code[Code]
    C1[Server.replaceRejectModels] --> C2[Registry.RejectQueuedRequestsForShedModels]
    C2 --> C3[RequestQueue.ReplaceShedModels]
    C3 --> C4[Enqueue / requeue / assignment share one locked snapshot]
    C4 --> C5[Queue state linearizes assignment vs timeout/cancel]
    C6[ProviderLoop.updateAggregateCapacity] --> C7[max_model_slots plus occupied_model_slots]
    C7 --> C8[warmPoolModelSlotHeadroomLocked]
    C9[combinedAdmissionHeadroomLocked] --> C10[Per-model solo rates plus pending-only cold models]
    C11[HasCapableProviderForModel] --> C12[Nonresident modelFitsHardware gate]
  end
Loading

What Changed

  • Bounded-busy warming: occupancy is max(coordinator pending, sum backend running+waiting), avoiding mirrored-request double counting. Busy candidates additionally require no-eviction memory fit and a v0.7.5 provider heartbeat proving a free model slot.
  • Authoritative slot commitments: Swift reports max_model_slots and occupied_model_slots. Occupancy includes resident, unloading, actively loading, and distinct globally queued model loads. Legacy/unknown busy providers fail closed; fully idle warming remains backward compatible.
  • Combined admission: when enabled, box-wide normalized load includes every resident slot plus coordinator-pending cold models absent from the latest heartbeat. PR fix(registry): per-model solo-TPS quality caps + pooled-KV admission (postmortem layer 6) #524's per-model solo-TPS resolver supplies each model's quality concurrency.
  • Runtime shedding: the public reject set and queue snapshot update under one serialized transition. Enqueue, requeue, and final assignment consult that snapshot; alias/build identity and self-route exemptions are preserved. Typed ErrModelShed produces a prompt 429 instead of a false queue timeout.
  • Queue lifecycle: provider assignment and timeout/cancellation now commit through one queue state transition, so a canceled waiter cannot retain a provider reservation.
  • Capability classification: a nonresident model must fit the provider's real total memory before the pool is classified as transiently capable (429); permanently undersized pools remain 503.
  • Operations: cold-disqualification gauges and the runtime admin endpoint remain documented. The release runbook now forbids an intermediate coordinator deploy and treats either a production tag or manual environment=prod dispatch as immediate active rollout.

Validation

Candidate head: 5176e66a

  • cd coordinator && go test ./... -race -count=1 - all 21 packages passed.
  • cd coordinator && go vet ./... - passed.
  • cd coordinator && go build ./cmd/coordinator - passed.
  • cd provider-swift && swift test - 1,343 tests passed in 115 suites.
  • git diff --check - passed.
  • Focused queue/runtime-shed HTTP tests, slot-commitment wire tests, warm-pool regressions, cold-fit classification, and combined pending-only admission all passed under their relevant race/Swift lanes.
  • Independent final queue, warm-pool/protocol, operations, and holistic rereviews found no actionable issues.

Fresh GitHub checks are required on this pushed head before merge.

Release And Deploy Sequence

  1. Merge PR feat(registry,api): harden warm-pool recovery, admission, and runtime shedding #525.
  2. Bring PR feat(registry): v2 version floors + tripwire, prefill-honest TTFT (absorbs #453), satisficing utilization band [stacked on per-model caps] #526 onto the resulting master, close its review/CI gates, and merge it.
  3. Run the combined coordinator merge-train gates on final master. Do not deploy an intermediate coordinator after either PR.
  4. Bring PR release(provider): v0.7.5 — one engine: full legacy deletion, vision+video+prefix on CBv2, KV re-slicing, fail-loud #522 onto that final master, close all one-engine v0.7.5 release blockers, and run the complete provider release gates.
  5. Use a dev tag or manual release with environment=dev to validate the signed/notarized bundle, installer, update path, and owned-provider canary in pre-production.
  6. Only after that signoff, create the production tag (or manually dispatch environment=prod). Either path registers an active release and begins fleet rollout immediately; monitor and stop/roll back on failed gates.
  7. Deploy the coordinator once only after the complete one-engine provider release is healthy.

No coordinator, provider release, tag, or production rollout is performed by this PR.

…it for busy boxes

Replace the fully-idle cold-candidate requirement (any pending request or
busy backend slot disqualified with not_idle) with a bounded-busy gate:
eligible when pendingCount + sum over all slots of running+waiting <=
EIGENINFERENCE_WARM_POOL_ALLOW_BUSY_LOAD_MAX (default 0 = exact historical
behavior). A non-idle candidate must additionally fit the model weights
WITHOUT evicting co-resident models (new busy_no_evict_headroom reason),
mirroring the dispatch path's no-eviction rule in freeMemoryAdmits.

Fixes postmortem 2026-07-06 layer 7: during recovery every box carrying a
single request read as ineligible, eligible_cold hit 0, and MIN_WARM floors
were clamped into a no-op.
…ightly-busy leg)

Pins the postmortem §3.3 recovery path: after a provider idle-unloads, the
floor alone (zero demand pressure) re-warms the box on the next tick, and
with ALLOW_BUSY_LOAD_MAX=2 the floor also acts on a box still serving one
request of a co-resident model — the case the historical fully-idle gate
clamped to eligible_cold=0.
…_COMBINED_ADMISSION_CAP

Per-model quality caps are checked independently, so a box saturated on
model A still admits model B (the cross-model hard edge blocking the
open-pool flip). When the flag is on (default OFF — byte-identical
behavior while dormant), hasConcurrencyHeadroomForModelCapLocked also
requires the normalized box budget:

  sum over slots(load_s/qc_s) + 1/qc_candidate <= overcommit(candidate)

qc uses the same qualityConcurrency the per-model cap uses (static rates
only, never observed-under-load EWMAs); unresolvable slot qc fails open
per slot; an unloaded box always admits; the no-benchmark non-dedicated
trust rule mirrors effectiveMaxConcurrencyForModelLocked.
…embership

The uptime-neutral 429 on the no-eligible-provider preflight branch was
triggered by IsDedicatedModel && HasProviderForModel, so a NON-dedicated
model whose providers were all gate-excluded (cooldowns, thermal, floors)
hard-503'd even though the fleet serves it — and clearing
EIGENINFERENCE_DEDICATED_MODELS would silently flip sheds to 503s. Re-key
the trigger on HasProviderForModel alone: provider exists => 429 +
Retry-After; model truly absent => 503. DEDICATED_MODELS becomes a pure
routing-policy env with instant rollback.

Retry-After computation and the historical outcome tag
(outcome:dedicated_capacity_429) are preserved for existing dashboards; a
new reason tag (dedicated | provider_exists) splits the old trigger from
the re-keyed one. IsDedicatedModel is consulted for that tag only.
The controller already tallied per-reason cold-candidate disqualifications
into its snapshots, but they were only visible in warm_pool_tick logs — the
Jul 6 recovery stalled for hours on eligible_cold=0 before anyone could see
WHY (postmortem mistake #4: knobs raised blind, restart to grep). Emit
warm_pool.eligible_cold + warm_pool.cold_ineligible per model and
warm_pool.cold_disqualified per (model, reason) from the existing 15s gauge
loop (body extracted to pushDDGauges so tests can drive one emission
against a live UDP collector).
GET/PUT /v1/admin/reject-models reads/replaces the per-model shed set at
runtime; EIGENINFERENCE_REJECT_MODELS still seeds the initial set at
startup. Every shed flip previously required a coordinator restart, and
each restart wipes the in-memory calibrator/TPS/breaker/warm-pool state
(1,589 provider sessions died to coordinator restarts in one 48h window).

- Auth mirrors /v1/admin/drain: requireAuth wrapper (admin key accepted as
  a pseudo-account, no credentials = 401) + isAdminAuthorized in-handler
  (admin key OR Privy admin; otherwise 403).
- PUT is a FULL replacement ({"models": [...]}; empty list = shed
  nothing) and logs old -> new loudly.
- rejectModels is now guarded by an RWMutex (it was startup-write-only
  before; the endpoint makes it runtime-mutable on the admission hot path).
- scripts/admin.sh grows reject-models get|set|clear following the
  existing subcommand pattern.
…SY_LOAD_MAX, and the runtime reject-models endpoint
@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
d-inference Ready Ready Preview Jul 9, 2026 9:56pm
d-inference-console-ui-dev Ready Ready Preview Jul 9, 2026 9:56pm
d-inference-landing Ready Ready Preview Jul 9, 2026 9:56pm

Request Review

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

This PR introduces a runtime-mutable model-shed facility and minor scheduling bookkeeping; it is neutral-to-positive on security posture but adds one new admin-only attack surface that warrants a brief note.


Trust boundaries touched

Boundary Why
TB-001 (Consumer → Coordinator) New 429 path in consumer.go; shed applied to queued requests
TB-002 (Coordinator → Provider WebSocket) Registry changes to RejectQueuedRequestsForShedModels; queueShedModels snapshot
TB-006 (Admin → Coordinator) Two new admin endpoints GET/PUT /v1/admin/reject-models registered in server.go

Per-threat assessment

T-002 — Unbounded request body causes coordinator OOM

ℹ️ Neutral. The changes in consumer.go (lines 146–184, 3507–3585) add new early-return paths on ErrModelShed but do not touch io.ReadAll or add/remove a MaxBytesReader. The open finding (SEC-006) is unaffected.

T-004 — Consumer API key used for admin-scoped operations

ℹ️ Neutral. The two new routes (GET/PUT /v1/admin/reject-models) are registered with s.requireAuth(…) and, per the comment at server.go:1935, the handlers call isAdminAuthorized (same pattern as /v1/admin/drain). No new scope confusion is introduced. Worth confirming the handler implementations in admin_reject_models.go (not in diff) do call isAdminAuthorized before mutating state.

T-005 — Consumer denies inference requests (billing repudiation)

ℹ️ Neutral. New ErrModelShed early-exit paths call refundReservation() before returning 429 (consumer.go:3511, 3577). Billing is correctly unwound; no new repudiation surface.

T-006 — Unauthenticated WebSocket floods provider registry

ℹ️ Neutral. RejectQueuedRequestsForShedModels only touches the queue, not WebSocket admission or the pre-auth path. No change to provider.go WebSocket upgrade.

T-007 / T-012 / T-027 — Weight hash / model tampering

ℹ️ Neutral. Registry changes are limited to the shed list and MarkModelWarm slot accounting. Neither weight hash enforcement nor the fail-open behaviour (SEC-007) is changed.

T-009 / T-015 / T-034 / T-036 — Attestation chain / trust escalation

ℹ️ Neutral. The registry.go diff touches only queue-shed and BackendCapacity bookkeeping. The trust state machine, MDM/MDA paths, and APNs code-identity challenge are untouched.

T-024 — Admin API key compromise / release manipulation

⚠️ Minor surface increase (mitigated by existing auth). Two new admin-gated endpoints (server.go:1935–1941) allow a live replace of the model-shed set. A compromised admin key or Privy admin JWT can now flip the shed list without a coordinator restart. This is a narrower capability than the existing release-key powers (binary hash injection), but it is a new runtime-mutable control plane action. The existing finding SEC-009 (release key compared with == rather than subtle.ConstantTimeCompare) remains relevant: a timing attack that recovers the release key also inherits the ability to call these endpoints, but the admin key (used here) is already constant-time compared. Verify that admin_reject_models.go gating is indeed isAdminAuthorized and not just requireAuth.

T-025 — Stale release cache (SEC-021)

ℹ️ Neutral. Cache invalidation logic not touched.

T-038 — Slow-header / HTTP server config

ℹ️ Neutral. main.go diff only adds EIGENINFERENCE_COMBINED_ADMISSION_CAP env-var handling. HTTP server timeouts are unchanged.

T-040 — Host header injection in URL generation

ℹ️ Neutral. No URL generation touched.

T-041 — Profile signing identity exfiltration

ℹ️ Neutral. main.go change is unrelated to signing identity handling.

T-010 — Cancellation not propagated to inference engine

ℹ️ Neutral. ProviderLoop.swift change adds loadGateWaitingModels: [String: Int] bookkeeping only. The cancellation wiring is unchanged.

T-008 — Provider sends plaintext SSE on encryption failure

ℹ️ Neutral. No changes to the encryption/SSE path in ProviderLoop.swift.


New attack surface not covered by existing threats

Runtime shed list as a denial-of-service lever (new, low severity)

PUT /v1/admin/reject-models calls RejectQueuedRequestsForShedModels which, under rejectModelsMu, iterates the queue and fails all in-flight queued requests for the named models (registry.go:3466–3497). A compromised admin credential can now instantly drain queued consumer requests for any model set without a coordinator restart. This is operationally intentional but the blast radius (silent 429 storm to all queued consumers) is higher than a restart would be, since a restart gives providers time to reconnect. No audit log is emitted for this action per the existing open finding on TB-006 ("No alerting or audit log on release registration events"). Recommend adding a structured log entry in replaceRejectModels that records previous → current transition and the failedQueued count, so the change is detectable in log-based alerting.


Lock ordering note (registry.go:3466–3497)

RejectQueuedRequestsForShedModels acquires r.mu and then calls queue.ReplaceShedModels(models). If RequestQueue internally acquires its own lock, verify that no other code path holds queue's lock while acquiring r.mu — a deadlock would silently wedge the coordinator's shed logic on the next admin PUT. This is not a threat-model finding but is a correctness risk with security impact (shed not taking effect).


Open findings resolved by this PR

None — no SEC-* findings are closed by this change.


🔐 Threat model: docs/threat-model.yaml · Updates on each push to this PR

@blacksmith-sh

blacksmith-sh Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
github.com/eigeninference/d-inference/e2e/TestIntegration_ConcurrentRequests View Logs

Fix with Codesmith
Need help on this PR? Tag /codesmith with what you need.

@Gajesh2007
Gajesh2007 marked this pull request as ready for review July 8, 2026 06:28

@ethenotethan ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review — Layr-Labs/d-inference#

Verdict: REQUEST_CHANGES

Security — 1 finding(s) (1 blocking)

  • 🟡 [MEDIUM] coordinator/api/admin_reject_models.go:52-56 — Model names not validated for injection or path traversal
    • Suggestion: Add validation to reject model names containing special characters, path separators, or control characters that could be used in injection attacks

Performance — 1 finding(s) (1 blocking)

  • 🟡 [MEDIUM] coordinator/api/admin_reject_models_test.go:176-195 — Concurrent test spawns 8 goroutines performing 200 operations each without context cancellation or timeout
    • Suggestion: Add context with timeout and use errgroup to properly manage goroutine lifecycle and error handling

Type_diligence — 2 finding(s)

  • 🔵 [INFO] coordinator/api/admin_reject_models.go:33 — map[string]any used for JSON response where typed struct would be clearer
    • Suggestion: Define a typed response struct like type RejectModelsResponse struct { Models []string \json:"models"` }` instead of map[string]any
  • 🔵 [INFO] coordinator/api/admin_reject_models.go:62-65 — map[string]any used for JSON response where typed struct would be clearer
    • Suggestion: Define a typed response struct like type PutRejectModelsResponse struct { Models []string \json:"models"`; Previous []string `json:"previous"` }` instead of map[string]any

Additive_complexity — ✅ No issues found

4 finding(s) total, 2 blocking. Verdict: REQUEST_CHANGES.

🤖 Automated review by Centaur · DAR-186

Comment thread coordinator/api/admin_reject_models.go Outdated
Comment thread coordinator/api/admin_reject_models_test.go
Comment thread coordinator/api/admin_reject_models.go
Comment thread coordinator/api/admin_reject_models.go Outdated
Review round for PR #525 (findings F1, F3, F4):

- PUT /v1/admin/reject-models now bounds the payload (max 128 entries,
  max 256 bytes per name) and rejects invalid UTF-8 and control
  characters with a 400 that names the offending index. '/' stays legal
  since real model IDs contain it (mlx-community/...). Validation runs
  before ReplaceRejectModels so a rejected payload never half-applies.
  The strings only ever flow into map lookups, slog, and JSON — never
  filesystem paths or commands — so this is defense-in-depth for the
  admin surface, not an injection fix.
- Both handlers now return typed response structs
  (rejectModelsResponse, rejectModelsReplaceResponse) instead of
  map[string]any; wire shape unchanged and pinned by
  TestAdminRejectModelsWireShape.
- Table-driven validation tests through the real HTTP mux, plus a
  direct test for the invalid-UTF-8 leg (unreachable via JSON decode,
  guards the env-seeding path).

@ethenotethan ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review — Layr-Labs/d-inference#

Verdict: REQUEST_CHANGES

Security — 1 finding(s)

  • 🔵 [INFO] coordinator/api/admin_reject_models.go:58-70 — validateRejectModelName allows forward slashes which could enable path traversal if model names are used in file operations
    • Suggestion: Consider restricting forward slashes or ensure model names are never used in file path operations without proper sanitization

Performance — 1 finding(s)

  • 🔵 [INFO] coordinator/api/admin_reject_models.go:102-115 — Map allocation without pre-sizing in validation loop
    • Suggestion: Pre-allocate the map with capacity len(req.Models): set := make(map[string]bool, len(req.Models))

Type_diligence — 1 finding(s) (1 blocking)

  • 🟡 [MEDIUM] coordinator/api/admin_reject_models.go:103-115 — map[string]bool creation without validating duplicate model names could silently overwrite entries
    • Suggestion: Check for duplicate model names in the request slice before creating the map, or document that later entries overwrite earlier ones

Additive_complexity — ✅ No issues found

3 finding(s) total, 1 blocking. Verdict: REQUEST_CHANGES.

🤖 Automated review by Centaur · DAR-186

Comment thread coordinator/api/admin_reject_models.go
Comment thread coordinator/api/admin_reject_models.go
Comment thread coordinator/api/admin_reject_models.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b906978e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coordinator/api/inference_admission.go Outdated
Comment thread coordinator/registry/warm_pool_controller.go Outdated
Comment thread coordinator/api/admin_reject_models.go Outdated
Comment thread coordinator/api/admin_reject_models.go
@Gajesh2007

Copy link
Copy Markdown
Member Author

Review round addressed @ethenotethan — ready for re-review.

  • Fixed (5b90697): PUT /v1/admin/reject-models input hardening (≤128 entries, ≤256 bytes/name, UTF-8 + control-char rejection, validate-before-apply; '/' stays legal — real model IDs contain it) and typed response structs for both handlers with the wire shape pinned by test.
  • Declined with evidence (per-thread replies): the concurrent test is WaitGroup-joined, in-process, non-blocking — nothing for errgroup/context to cancel or collect; the round-2 findings (slash/path-traversal premise, map pre-sizing, duplicate overwrite) are answered in-thread — the strings never touch the filesystem, the map is already pre-sized, and set semantics are intended.
  • CI: E2E TestIntegration_ConcurrentRequests = known master-reproducing MLX broadcast_shapes flake; the Provider Tests failure is the known StartupPreloadTests timing flake — this branch is coordinator-only (14 coordinator files + docs + scripts).

Finding 1 (503 for structural no-provider): the no-eligible-provider shed
preflight keyed the 429-vs-503 split on the bare HasProviderForModel, which
ignores the public routing gates — so a non-dedicated model advertised only by
private-only, runtime-unverified, below-trust, or render-broken providers (which
can NEVER serve a public request) was 429'd as transient capacity instead of
503'd. Add registry.HasCapableProviderForModel, the capability-aware sibling
that applies the structural liveness/trust/privacy + trait gates while ignoring
transient exclusions (dedicated rule, cooldowns, breaker, thermal). Status stays
independent of IsDedicatedModel; the dedicated-on-non-dedicated-box case still
reads as a transient 429.

Finding 2 (busy no-evict units): the bounded-busy warm-pool no-evict fit
compared the RAW catalog decimal-GB size against freeGB (reported in GiB),
under-counting the model's in-memory footprint. Normalize with
coldLoadCatalogGBToMemGiB (×1.2 overhead + decimal-GB→GiB) so it mirrors the
provider's ModelLoadAdmission gate — a near-threshold model no longer passes
here and then fails load_model.

Finding 3 (require models key): PUT /v1/admin/reject-models with {}, a typoed
key, or {"models":null} yielded nil and silently CLEARED the shed list. Decode
into *[]string so an absent/null key is a 400; only an explicit {"models":[]}
clears.

Finding 4 (reject queued on shed): adding a model to the runtime shed set only
gated future admission — already-queued requests kept dispatching to the pulled
model. The PUT handler now fails the queued public waiters for newly-shed models
(self-route preserved, mirroring the shed's admission exemption) via
registry.RejectQueuedRequestsForShedModels.

Each fix ships a fails-without-fix regression test (httptest for the admin
endpoints).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aba4931bb2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coordinator/api/admin_reject_models.go Outdated
Comment thread coordinator/registry/dedicated_models.go
Comment thread coordinator/registry/warm_pool_controller.go Outdated

@ethenotethan ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review — Layr-Labs/d-inference#

Verdict: COMMENT

Security — ⚠️ SKIPPED

Error: OpenRouter HTTP 402: {"error":{"message":"Insufficient credits. Add more using https://openrouter.ai/settings/credits","code":402}}

Performance — ⚠️ SKIPPED

Error: OpenRouter HTTP 402: {"error":{"message":"Insufficient credits. Add more using https://openrouter.ai/settings/credits","code":402}}

Type_diligence — ⚠️ SKIPPED

Error: OpenRouter HTTP 402: {"error":{"message":"Insufficient credits. Add more using https://openrouter.ai/settings/credits","code":402}}

Additive_complexity — ⚠️ SKIPPED

Error: OpenRouter HTTP 402: {"error":{"message":"Insufficient credits. Add more using https://openrouter.ai/settings/credits","code":402}}

✅ All four passes clean. No issues found.

🤖 Automated review by Centaur · DAR-186

@Gajesh2007 Gajesh2007 changed the title feat(registry+api): warm-pool eligibility, shed re-key, combined admission cap, runtime shed endpoint (postmortem layer 7 + open-pool enablers) feat(registry,api): harden warm-pool recovery, admission, and runtime shedding Jul 9, 2026

@ethenotethan ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review — Layr-Labs/d-inference#

Verdict: REQUEST_CHANGES

Security — 1 finding(s)

  • 🔵 [INFO] coordinator/api/admin_reject_models.go:58-72 — validateRejectModelName allows forward slashes which could enable path traversal if model names are used in file operations
    • Suggestion: Consider restricting forward slashes in model names or ensure model names are never used directly in file paths without proper sanitization

Performance — 2 finding(s) (2 blocking)

  • 🟡 [MEDIUM] coordinator/registry/concurrency_cap_combined_test.go:95 — Unbounded slice allocation in test loop without pre-allocation
    • Suggestion: Pre-allocate the slice with known capacity: slots := make([]combinedSlotLoad, 0, len(p.BackendCapacity.Slots)+len(pendingByModel))
  • 🟡 [MEDIUM] coordinator/registry/concurrency_cap.go:105 — Unbounded slice allocation in hot path without pre-allocation
    • Suggestion: Pre-allocate the slice with known capacity: slots := make([]combinedSlotLoad, 0, len(p.BackendCapacity.Slots)+len(pendingByModel))

Type_diligence — 1 finding(s) (1 blocking)

  • 🟡 [MEDIUM] coordinator/api/admin_reject_models.go:99 — Missing nil check for req.Models pointer before dereferencing
    • Suggestion: The code checks if req.Models == nil but then immediately dereferences *req.Models without verifying the pointer is valid. Add explicit validation: if req.Models == nil { return 400 }; models := *req.Models

Additive_complexity — ✅ No issues found

4 finding(s) total, 3 blocking. Verdict: REQUEST_CHANGES.

🤖 Automated review by Centaur · DAR-186

Comment thread coordinator/api/admin_reject_models.go
Comment thread coordinator/registry/concurrency_cap_combined_test.go
Comment thread coordinator/api/admin_reject_models.go
@Gajesh2007
Gajesh2007 dismissed stale reviews from ethenotethan, ethenotethan, and ethenotethan July 9, 2026 21:57

Superseded by head 5176e66. The actionable input-hardening feedback was implemented, all associated threads are resolved, and full Go/Swift gates plus independent rereviews pass.

@Gajesh2007

Copy link
Copy Markdown
Member Author

CI triage on 5176e66a: all four required checks pass. The optional E2E job failed TestIntegration_ConcurrentRequests because the current Qwen3.5 provider hit the known MLX broadcast_shapes crash (Shapes (4,1) and (12,1), then (3,1) and (6,1)). The same failure is present on recent master runs 29045836408 and 28983297154, while a later master run passed, so this is not attributable to #525's coordinator diff. It remains a hard integrated release gate for #522/v0.7.5: rebase and validate the one-engine provider, complete signed/notarized dev and owned-provider canaries, and only then activate v0.7.5 and deploy the coordinator once.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5176e66a6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if !warmPoolModelSlotHeadroomLocked(p, model) {
return warmPoolCandidate{}, warmColdBusyNoEvict
}
if size := r.catalogSizeGBLocked(model); size > 0 && size*coldLoadCatalogGBToMemGiB > freeGB {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include load headroom in busy warm-pool fit

When EIGENINFERENCE_WARM_POOL_ALLOW_BUSY_LOAD_MAX admits a busy cold candidate, this check only compares padded weights against totalMemoryGB - gpuActiveGB. The provider load path requires requiredToLoadGb(weightsGb: ..., headroomGb: Self.loadHeadroomGb) and compares it to availableMemoryGb(), which also subtracts the unified-memory reserve and outstanding KV (provider-swift/Sources/ProviderCore/ProviderLoop+ModelLoading.swift:225-229 and :543-556). On a near-threshold busy box, especially when free_for_load_gb passed by assuming resident MLX memory is reclaimable, this can still issue load_model that the provider rejects, causing failed warms and pending-load cooldowns instead of selecting a truly loadable candidate.

Useful? React with 👍 / 👎.

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