# DAEMON → CORE — the engine API `sched/` needs before it can be written Status: **resolved**. CORE answered in full: `core/docs/adr-0011-core-response.md` (`lane/core`, commit `7bf5cb5`). Kept as a record of what was asked and the shape of the answer; the API itself is now specified in `docs/adr/0011-admission-control-and-the-segment-budget.md` §"Engine API `sched/` is built against". `sched/` may be written against it. Ranking followed `contracts/README.md` rule 4 conventions even though this wasn't a `contracts/` change: new API surface = cheap, land anytime; a behavioural promise (min-1 fairness) = needed CORE's explicit sign-off before DAEMON built on the assumption. That sign-off is in. --- ## C1. Occupancy read-out, not inference — **resolved** Requested `budget()`, `segments_active(TaskId)`, `on_budget_changed`. CORE's answer adds `starved_tasks()` and `starved_since(TaskId)` (amendment A3) and pins two definitions: `segments_active(id)` counts a segment in `connecting` state as held (it is progress, not starvation), and `tasks_starved` counts only `segments_active == 0`. See ADR 0011 §3.6. ## C2. `set_max_active_segments(uint32_t)` live-apply — **resolved: drain, never kill** Confirmed DAEMON's assumption. Lowering runs in-flight segments to their next boundary; no new segment starts while over the new ceiling; nothing is aborted, no partial range lost. If the new ceiling is below the running-task count, CORE honours min-1 for the top-priority subset and reports the rest via `tasks_starved` — DAEMON's governor must reconcile and pause the lowest-priority excess itself (CORE does not auto-pause). See ADR 0011 §2. ## C3. `set_host_segment_cap(host, uint32_t)` — **resolved, confirmed as proposed** CORE keeps the `host → cap` map and derives a task's host from its URL/mirror set; DAEMON owns the table and pushes it. See ADR 0011 §4. ## C4. Contract gap: `connection.maxActiveSegments` — **resolved by PROTO** PROTO landed it (ADR 0012, `connection.maxActiveSegments` default 32, `connection.maxTotalBufferBytes` default 128 MiB, `TaskDetail.effectiveBufferBytes`) while this was in flight. No daemon-local stopgap needed — `sched/` reads the wire field directly. See ADR 0011 §6. ## C5. Fairness rule sign-off — **resolved, with two amendments** CORE confirmed min-1-before-seconds is implementable without a priority-inversion at slot release (two-pass allocator: guarantee pass over zero-slot tasks in DAEMON's priority order, then a growth pass; a released slot always re-enters the pool at pass 1, never handed back locally). Two amendments to what DAEMON assumed: - **A1** — "steal" (slot-neutral, unchanged) isn't the whole mechanism; **"yield"** is the slot-transfer operation that actually satisfies min-1 out of a full budget: an over-quota task releases one slot at its next segment boundary, bounded by that segment's remaining bytes. - **A2** — "admission implies progress" is **bounded-delay**, not immediate: `time_to_first_slot ≤ min(next yield boundary, low_speed_secs) + connect_timeout`, not "connect timeout + per-host cap" alone. DAEMON's starvation-invariant assertion window widened from the originally proposed 2 s to `low_speed_secs + connect_timeout` (~45 s) accordingly. Priority order (open item 4) is an ordered `TaskId` list pushed via `set_task_order` on change — not an integer, not per-tick. See ADR 0011 §3. ## C6. Probe pool sized outside the segment budget — **resolved, confirmed** Dedicated pool, default size 4, `set_probe_pool_size(uint32_t)`, independent of `maxActiveSegments`; probe cancellation is immediate. DAEMON still bounds probe *submission* on its own side. See ADR 0011 §5.