Skip to content

feat(arty_io_core): coordinate completion service - #768

Draft
martintmk wants to merge 12 commits into
user/martintomka/20260907-add-arty-io-driverfrom
user/martintomka/20260917-coordinated-io
Draft

martintmk wants to merge 12 commits into
user/martintomka/20260907-add-arty-io-driverfrom
user/martintomka/20260917-coordinated-io

Conversation

@martintmk

@martintmk martintmk commented Sep 17, 2026

Copy link
Copy Markdown
Member

Posted by an AI agent

Summary

Stacked on #737. This draft targets user/martintomka/20260907-add-arty-io-driver,
not main, and replaces the parent draft's combined blocking driver interface.

The public driver contracts are generic and support static dispatch. Providers
return the consumer context together with LocalDriver<Self::Driver>, and
consuming shutdown returns the concrete associated drain in LocalDrain<D::Drain>.
Both owners store their values inline and remain !Send/!Sync even when the
underlying implementation is thread-safe. They expose no inner extraction.
There is no mandatory driver allocation or public boxed driver/drain contract.
Correct context-instance pairing remains the provider's responsibility.

The runtime owns collection and the blocking wait; independent drivers retain
their own operations, buffers, queues, and completion decoding. One compatible
collector can deliver shared records or notify drivers to drain private queues.
Bounded service, notification preparation, immediate continuation, deadlines,
and consuming cooperative shutdown remain explicit.

take_completion_service transfers native clients, including unique local
clients, into creation. Providers obtain the selected strategy's clients before
native binding. Missing clients permit an explicit fallback; native registration
failure is not treated as a missing client.

The reference runtime uses one collector per owner worker and one shared
system-work thread, not a thread per driver. Registration retry cannot collide
with a still-live predecessor. A lost installation reply starts bounded cleanup
of its own registration without stopping healthy peers. Undelivered retirement
outcomes retain their classified native causes.

System-work submission remains fallible. Live owners and accepted jobs preserve
execution authority after a controller timeout; an inert handle alone does not.
Private heterogeneous runtime storage, opaque offload tasks, native clients,
and other backing resources may still allocate. The static driver contract is
not a claim that the entire runtime is allocation-free.

Ten additional refinement rounds

Completed ten new rounds, additional to the earlier design critiques rather than
a relabeling of them. Opus 5, GPT-6 Astra, and Sonnet 5 ran with high reasoning; each round
included multiple model contributions and a concrete change-or-retain decision.

Round Focus Result
1 Creation and ownership Paired creation, concrete associated drivers/drains, and inline local owners replace mandatory public driver boxing.
2 Client ownership Replace borrowed lookup with one owned typed take; preserve missing, duplicate, sibling, and before-binding behavior.
3 Budgets and progress Retain residual accounting and explicit statuses; nested completion work defers retirement when the shared allowance is exhausted.
4 Preparation and waking Retain separate service and arming; preparation remains valid when the runtime cancels the wait or prepares again.
5 Shutdown phases Retain two phase-specific owners, including when one concrete type implements both protocols; no extraction or repeated initiation.
6 System-work lifetime Retain opaque tasks and fallible admission; clarify synchronous execution authority and failed-worker behavior.
7 Errors Retain canonical classifications and native causes; expose all reference aggregate failures without adding a rejection category.
8 Static type exposure Retain nameable associated types for concrete running/draining state storage; private erasure remains optional.
9 Registration and rollback Bound orphan retirement, reject premature retry before native effects, and preserve response ownership and late outcomes.
10 Final convergence Two independent full passes retain the public API; clarify the opening inline-ownership wording without expanding the surface.

The current surface has 15 public types and traits. The two local phase owners
carry the required compile-time confinement rather than hiding boxed drivers.
Requirements, design documents, rustdoc, and the generated README describe this
implemented contract.
See the API design
and completion-coordination research.

Scope

This implements the core contracts and a safe in-memory reference runtime.
Production Windows IOCP/RIO and Linux io_uring backends are not included.
The reference control thread uses blocking result handles; it is not a complete
application-future executor.

There are no new dependencies, features, or unsafe core implementation. The
unreleased API is replaced without compatibility shims. Sharing the core does
not automatically make native clients compatible: their interfaces, concrete
types, and actual backing collectors must agree.

Verification

  • cargo +1.95 test -p arty_io_core --locked:
    42 public-contract cases, 53 reference-runtime cases, and 4 doctests pass through
    default target discovery. Three doctests check rejected ownership operations.
  • cargo +1.95 run -p arty_io_core --example two_thread_runtime --locked:
    both models complete on both owner threads, then all drivers drain.
  • Strict scoped Clippy, crate formatting, spelling, generated README, and
    compiled public API inspection completed.
  • A fresh package-only coverage build reports 100% source-line coverage.
  • The compiled public API contains no public Box<dyn> signatures.

Existing CI limitations outside this change

The previous remote head reported an inherited rustls advisory in cargo deny
and a full-workspace Windows LLVM export command-length failure (os error 206).
Dependency versions and workspace coverage tooling are unchanged. The separate
core coverage gap from that head is covered by the current package result above;
this does not claim that the new full-workspace CI run has already passed.

Separate native collection and waiting from bounded driver service. Add scoped capability negotiation, fallible initialization, local ownership, budgeted cooperative draining, and source-preserving classified errors. Align the documentation and replace the registration-only example with a coordinated in-memory reference runtime and focused contract coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6

@martintmk martintmk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent

Warning: Incomplete review coverage

Some review areas could not be executed:

  • Public API documentation (review-public-docs): exact repository-configured ms-prod-1.95 is not installed, so matching base/head rustdoc JSON could not be generated; the installed 1.95 toolchain was not substituted.

No combined verdict is issued. Findings below come only from completed areas.

Completed areas identified one blocking and five non-blocking findings. Targeted package tests passed, and pinned CI diagnostics were inspected where relevant; the failed fast-check advisory, Windows coverage export failure, and equivalent mutation were not treated as defects in this change.

/// A waiter is used only on its configured owning thread. It has no `Send` or `Sync`
/// requirement. Configuring extra domains, dedicated hosts, or external observers is explicit
/// runtime policy; this trait does not create threads.
pub trait CompletionWaiter: 'static {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent

CompletionWaiter leaves its owning-thread contract unenforced

Problem
CompletionWaiter says a waiter is used only on its configured owning thread, but the public trait and its return/storage contract add no !Send guard. A concrete waiter whose fields happen to be Send can therefore be moved after configuration while still satisfying every trait bound; unlike LocalDriver and Shutdown, no core wrapper makes that misuse unrepresentable.

Why this matters
Native collectors commonly bind registrations, handles, or callback state to one owner thread, so a generic runtime can service or drop a valid implementation on the wrong thread despite using the API exactly as the type system permits. Every implementer/runtime must independently remember a foundational invariant that this crate otherwise claims to enforce through local ownership.

Suggested fix
Introduce a core-owned local waiter handle (analogous to LocalDriver) with an infectious PhantomData<Rc<()>>, and have runtimes own and drive CompletionWaiter implementations only through that handle; alternatively make the coordinator/owner type carrying the waiter part of this public contract and enforce !Send there.

Comment on lines +207 to +216
while self.state.registration.has_records() {
if !budget.try_consume() {
return Ok(ServiceStatus::runnable());
}
let record = self
.state
.registration
.pop()
.expect("only this owner removes records from the nonempty mailbox");
let mut requests = self

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

Record-delivery service locks the mailbox twice per completion

Problem
SampleDriver::service calls RecordRegistration::has_records() and then pop() for every record, and both methods independently acquire the same mailbox mutex (native.rs:205-224). This newly introduced per-completion path therefore performs two lock acquisitions just to dequeue one record.

Why this matters
The reference runtime presents this as the record-delivery hot path, so the redundant synchronization cost compounds with completion rate and obscures the intended cost of coordinated collection; its wall-clock magnitude is unmeasured.

Suggested fix
Expose one dequeue operation that checks availability, charges the completion budget, and pops while holding the mailbox lock once (returning empty versus budget-exhausted distinctly), then drive this loop through that operation.

/// The client's own ownership rules determine its thread safety and native-resource lifetime.
/// Cloning a service preserves its domain and clones its client handle.
#[derive(Clone)]
pub struct CompletionService<T> {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

CompletionService names a passive client capability like a service turn

Problem
The type's own documentation calls it a typed native client capability, and the reference adapter names the concrete values RecordClient and ReadinessClient. Service is already the operation vocabulary for Driver::service, Drain::service, CompletionBudget, and ServiceStatus, so the new noun makes a stored capability look like an active service participant.

Why this matters
This name propagates through negotiation, context construction, error classifiers, docs, examples, tests, and the PR title; consumers must otherwise distinguish two unrelated meanings of completion service, and correcting the public family later is breaking.

Suggested fix
Rename the passive family to CompletionClient<T> and use client consistently (CompletionDomain::client, with_completion_client, completion_client, duplicate-client classifiers/messages). Update requirements/design docs, tests, examples, generated README inputs, and the PR title to reserve service for bounded progress turns.


/// Returns the collection domain whose waiter the runtime will drive.
#[must_use]
pub const fn completion_domain(&self) -> &CompletionDomain {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

DriverContext::completion_domain diverges from sibling domain accessors

Problem
CompletionService::domain (completion_domain.rs:79) and CompletionWaiter::domain (completion_waiter.rs:26) already establish domain() for returning the same &CompletionDomain property, while DriverContext adds the unexplained synonym completion_domain().

Why this matters
The divergence is entering the public API and forces adapter authors to remember which of three closely related values uses the redundant prefix; a later normalization would be breaking.

Suggested fix
Rename this accessor to domain() and update its public-surface tests, examples, and documentation references.

Comment on lines +117 to +120
pub(super) fn report(message: &str) {
// Unlike eprintln!, a failed stderr write must not cause a second panic during unwinding.
let _ = writeln!(io::stderr().lock(), "{message}");
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

The reference runtime emits unstructured failure lines outside the tracing pipeline

Problem
report writes each dynamic message directly to stderr, and callers encode distinct events such as abandoned shutdown, initialization panic, rollback failure, and driver-creation failure only in free-form text. These new emissions therefore have no stable event name, severity, structured error field, or repository tracing subscriber integration.

Why this matters
Hosts and operators cannot reliably filter, correlate, redact, or alert on these failures through the existing tracing pipeline; parsing stderr text also turns wording changes into an accidental query contract.

Suggested fix
Emit these conditions through the workspace tracing front end with stable event names and structured fields (for example tracing::error!(event = "arty_io.runtime.shutdown_abandoned", error = %error)), leaving subscriber/export configuration to the host.

Comment on lines +38 to +41
Self::from_message(cause.to_string()).with_cause(cause)
}

/// Attaches an underlying cause without changing the classification or message.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

DriverError drops recoverability for native service failures

Problem
DriverError::from_cause assigns every native cause the opaque Failure kind, and the public API exposes only the special unsupported/shutdown booleans. Consequently an attached std::io::ErrorKind::TimedOut, ConnectionReset, or WouldBlock (or an inner error already implementing Recovery) cannot reach the runtime's source/domain failure policy, even though Driver::service explicitly delegates that policy to the runtime. The resolved recoverable 0.2.0 recipes require inner recovery propagation and ErrorKind/source-chain heuristics for foreign I/O errors.

Why this matters
A runtime cannot reliably distinguish a transient native collection/service fault from a permanent failure, so it may retire an otherwise recoverable domain or retry a permanent error; it also cannot compose this public error with the repository's standard recovery-aware middleware.

Suggested fix
Store RecoveryInfo in DriverError and implement recoverable::Recovery. Preserve explicit outer classifications where context changes recoverability, propagate recovery from recoverable native causes, derive foreign I/O recovery by walking Error::source(), and add focused tests for retryable, unavailable/unsupported, permanent, and wrapped causes.

Reduce the shared API from 19 to 13 named exports using direct local boxed traits, actual-client selection, and one collector-owned attachment seam. Preserve owned budgeted draining and explicit readiness states. Make system-work admission fallible, retain execution authority after controller timeout, and retire native registrations through RAII. Align documentation and enable the reference scenarios in default test discovery.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6

@martintmk martintmk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent

Complete Review Lens coverage at commit 3a63069 found two blocking test gaps and three new non-blocking design/performance issues. Five earlier findings remain applicable and were not duplicated; one earlier naming finding was resolved by this revision. Because this PR is authored by the posting identity, this review is COMMENT-only with no vote.

}

#[test]
fn shutdown_created_by_driver_failure_also_gets_an_initial_turn() {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent

Collector failures have no contract test for their distinct shutdown policy

Problem
The new contract requires collection-domain failures to remain distinct from one-driver failures, and Coordinator::collect now permanently marks the collector failed, records a domain error, forces zero-duration scheduling, and causes WorkerLoop to begin shutdown. The coordinator tests inject driver service, driver preparation, drain service, and drain preparation failures, but no waiter ever returns Err, so this separate observable policy is untested.

Why this matters
A regression could lose the collector error, treat it as idle, repeatedly call the failed collector, or stop independent drains incorrectly while all 76 tests still pass; runtimes adopting the reference coordination loop would then mishandle a domain-wide native failure.

Suggested fix
Add a scripted waiter failure case that asserts the collector is called once, the domain error is retained and reported, shutdown begins for installed drivers, independent drains receive their required turns, and the terminal runtime result contains the collector failure rather than an idle/success result.

}

#[test]
fn later_worker_creation_failure_rolls_back_before_publishing_or_retrying() {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent

Registration abandonment and rollback-error preservation are not exercised

Problem
The new requirements say an abandoned acquisition must not abandon an installation already in progress and that cleanup failures remain visible alongside the original registration error. WorkerLoop::command has a dedicated failed-reply path that records registration caller disappeared before publication and starts draining, while Runtime::get_context_on aggregates rollback errors, but the runtime tests cover only immediate creation failure followed by successful rollback; none drops or times out the acquisition reply after installation, or makes rollback itself fail.

Why this matters
Those untested branches own the driver, native registrations, callbacks, and error reporting after the caller no longer receives a context. A regression could leak an installed participant, publish partial cache state, or discard cleanup failure evidence without affecting the current success and immediate-failure tests.

Suggested fix
Add focused runtime cases that pause/delay installation until the acquisition receiver times out or is dropped, then prove the unpublished driver is drained and not cached; also inject a terminal rollback failure and assert the returned registration error retains both the original creation failure and the cleanup failure.

if !state.open {
return Err(DriverError::from_message("the in-memory collector has stopped"));
}
state.events.push_back(event);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

The native ingress queue grows without a bound or producer backpressure

Problem
EventQueue::publish unconditionally pushes every record/readiness event into a VecDeque that starts at capacity 64 but has no maximum, and producer-facing post methods return success as long as the collector remains open. Producers can therefore outpace the single collector indefinitely, retaining one event and route Arc per submission while the queue repeatedly reallocates.

Why this matters
This is a per-item path driven by callers, so sustained completion bursts or a stalled owner can turn the reference adapter into unbounded memory growth rather than applying a defined capacity policy; the allocation rate and limit are unmeasured.

Suggested fix
Give the ingress queue an explicit maximum and make publication apply the adapter's chosen backpressure policy, such as rejecting with a classified capacity error or using a bounded channel. Add a focused test that fills the configured capacity and verifies deterministic rejection or bounded draining without growing beyond it.

if self.stopping.is_some() && self.coordinator.is_empty() {
return outcome(self.errors);
}
self.coordinator.service(Instant::now());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

The reference scheduler hard-wires real monotonic time into deadline decisions

Problem
WorkerLoop::run, finish_drains, deadline_after, worker joins, rollback waits, and system-pool shutdown read Instant::now() directly. Although the workspace provides tick::SimpleClock and controlled clocks, the scheduler cannot substitute one, and its tests consequently exercise deadline behavior against the host clock.

Why this matters
Deadline checks run every scheduler turn, while timeout, rollback, and drain-expiry races are foundational behavior of this reference runtime. Hard-wired time makes those paths slower and less deterministic to test and prevents controlled coverage of exact boundary ordering.

Suggested fix
Construct the runtime with a tick::SimpleClock (system-backed by default), pass it to worker/system-pool state, and use clock.instant() for deadline creation and comparison. Enable tick test utilities for focused frozen/advanced-clock tests of expiry boundaries without sleeping.

/// A collector is used only on its configured owning thread. It has no `Send` or `Sync`
/// requirement. Configuring extra collectors, dedicated hosts, or external observers is explicit
/// runtime policy; this trait does not create threads.
pub trait CompletionWaiter: 'static {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

CompletionWaiter names its secondary wait behavior instead of its collection role

Problem
The trait documentation calls this value a collector, its primary operation is collect, and the coordinator stores collection state and collector_failed; waiting is optional because Duration::ZERO explicitly performs non-blocking collection. CompletionWaiter and the example's NativeWaiter therefore name a secondary mode instead of the role established by the surrounding family.

Why this matters
Adapter and runtime authors see a public type whose name suggests a blocking primitive even though it also routes activity, assembles clients, and is invoked on every non-blocking service turn. Correcting that vocabulary after release would require a breaking rename across implementations, trait objects, docs, and examples.

Suggested fix
Rename the trait to CompletionCollector and the reference implementation to NativeCollector, then update the waiter fields/parameters and user-facing docs to use collector; keep waker and max_wait for the actual waiting-specific members.

martintmk and others added 10 commits September 17, 2026 20:52
Pair consumer contexts with allocation-free inline local driver owners. Return concrete associated drains without public boxing, keep heterogeneous erasure private to the runtime, and make offload tasks opaque. Preserve same-instance pairing and thread-local consuming ownership, and exercise SystemTasks diagnostics to restore package coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Replace borrowed client lookup with owned typed extraction and preserve before-binding strategy selection. Cover local move-only leases, repeated takes, sibling isolation, reinsertion, and native registration failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Exercise real record and readiness drains with a one-unit budget and completed offload cleanup. Operation completion exhausts the inner allowance; terminal retirement must continue on a fresh turn without another event.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Keep explicit service and notification preparation phases. Clarify that a successful arm can be followed by service or repeated preparation, and exercise peer readiness aborting the shared wait.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Clarify synchronous execution authority and distinguish later external callbacks from accepted follow-up work. Verify that a failed offload worker rejects subsequent tasks, safely releases queued captures, and reports failure on join.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Keep the canonical error classifications and native source chains. Provide typed access to every reference-runtime failure while preserving the primary Error::source and clarify stage-specific retry policy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Compile a generic concrete participant state using Driver::Drain, with no trait object or heap-backed driver storage. Retain a draining participant without restarting shutdown.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Link owned client extraction from the crate guide, document placement metadata use, and avoid implying extraction proves a client has never been registered. Regenerate the README from rustdoc.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Reject duplicate creation before native effects while a preceding instance drains. Retire lost installation replies individually under a deadline, preserve rollback response ownership and unclaimed native failures, and permit fresh creation only after retirement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6
Keep the opening construction description focused on inline ownership; the detailed cost note separately explains the allocation-free wrappers and private runtime costs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9fcc1db7-eda6-4c33-bbbe-77ff8b5e5ae6

@martintmk martintmk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent

Complete Review Lens coverage at commit c27a8e5 found two new non-blocking public-contract/performance issues. Seven earlier findings remain applicable and were not duplicated; the two earlier test-gap findings are resolved by the expanded focused coverage. Because this PR is authored by the posting identity, this review is COMMENT-only with no vote.

///
/// Returns a duplicate-client error if a client type was already supplied to this context,
/// or an adapter failure raised while producing its clients.
fn attach_clients(&self, context: DriverContext) -> Result<DriverContext, DriverError> {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

CompletionWaiter::attach_clients cannot directly transfer collector-owned move-only clients

Problem
The method takes &self even though this seam is documented as attaching the collector's own client capabilities. A collector cannot move a non-Clone client into DriverContext through immutable access without adding hidden interior mutability, and the current non-clone test bypasses this seam by inserting the client directly.

Why this matters
Native adapters that own unique thread-local handles must add RefCell<Option<T>>-style state and potential borrow failures solely to satisfy this public contract.

Suggested fix
Take &mut self for attach_clients and add a test that transfers a non-Clone, non-Send client through the collector seam.

/// A false result leaves the budget unchanged. Do not perform the charged operation
/// unless this method returns true.
#[must_use]
pub const fn try_consume(&mut self) -> bool {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Posted by an AI agent · Non-blocking

CompletionBudget::try_consume omits the cross-crate hot-path inline hint

Problem
try_consume is an exported non-generic operation called for every bounded completion step, but it lacks #[inline] despite the repository performance guidance requiring the hint for small cross-crate hot-path functions.

Why this matters
Downstream drivers cannot inline this budget check without LTO, so every completion may retain an avoidable call boundary.

Suggested fix
Add #[inline] to try_consume; apply the same rule to remaining if it is used on the per-completion path.

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.

1 participant