Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Evaluation budget and cancellation: a host cannot bound or interrupt a running step

Open
#1,817 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
20/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
c
Domain
backend

Research direction

This is a broad design and implementation effort, and the issue delegates delivery to sub-issues #1819–#1824; start with #1819 for the foundation contract and its named source paths and tests. The issue’s plan identifies #1824 as the owner of final documentation and qualification. Done means the sub-issue behaviors have merged and final-candidate evidence passes; the parent explicitly remains open until then.

Written by the indexing model from the issue text.

Description

enhancement track/api

Requirement

A host must be able to bound the work a single evaluation performs, and stop
one already in flight
. Today it can do neither: wl_session_step()
(wirelog/session.h:239) runs to completion, and the only cancellation the API
offers is of delta notifications, which the header is explicit about:

Cancellation does not cancel evaluation: a failed step still requires retry
— wirelog/session.h:252

So a host that hands wirelog a valid but expensive program — a deep recursion,
a product join over a large EDB — has no lever. It cannot cap the work, cannot
set a deadline, and cannot abandon the step. Its only recourse is to kill the
process.

This is a resource-bounding requirement, not a hostile-input one. The threat
model already names the program text as trusted (docs/SECURITY_MODEL.md §1)
and this request does not ask to change that. Trusted programs can still be
accidentally expensive, and an embedding host is the only party that knows how
much work it is willing to pay for.

Why the memory governor is not enough

Version 2 of wl_session_options_t (#1473) added a caller-retained memory
admission governor, and WIRELOG_MEMORY_BUDGET gives an explicit managed-memory
ceiling (docs/MEMORY.md §6). That bounds one axis. It does not bound time or
work: a program that stays inside its memory budget while iterating for an
unbounded period is admitted the whole way, and docs/MEMORY.md §6 records
that the governor's only consumer today is the join operator.

The memory governor is, however, the right precedent for the shape of this
feature: a caller-supplied, caller-owned limit passed through versioned session
options rather than resolved from the environment.

Suggested shape, not a decision

wl_session_options_t already carries size and version
(wirelog/session_options.h:35-41), so a version 3 adding one field is the
natural path. Candidates, in rough order of how much they constrain the engine:

  • A cancellation token the host sets from another thread; the evaluator
    checks it at an existing safe point (stratum boundary, semi-naive iteration
    boundary, join batch) and returns a distinct error.
  • A step/work budget — a cap on iterations or derived rows — that fails the
    step deterministically when exceeded. Deterministic, reproducible, and
    testable, unlike a wall-clock deadline.
  • A wall-clock deadline, which is the easiest for a host to reason about
    and the hardest for the engine to make reproducible.

The distinct error matters as much as the mechanism: a bounded-out step must be
distinguishable from an evaluation failure, because the host's response differs
(surface a budget refusal to the operator vs. retry).

The existing step contract says a failed step delivers nothing and must be
retried, with the baseline preserved. A bounded-out step should keep that same
contract — the session stays usable, and the host may retry with a larger
budget — rather than poisoning the session.

Acceptance criteria

  • A host can supply a per-session evaluation bound and/or a cancellation
    handle through versioned session options, without setting an environment
    variable.
  • An evaluation that exceeds the bound, or is cancelled, terminates within
    a documented granularity and returns an error distinct from an evaluation
    failure.
  • A bounded-out or cancelled session remains usable: the documented
    wl_session_step baseline/retry contract holds, and the host can retry
    with a larger bound.
  • The bound is observable — the host can tell how much of it was consumed —
    so it can be calibrated rather than guessed.
  • Behaviour is documented in docs/SEMANTICS.md alongside the existing
    evaluation contracts, and in docs/MEMORY.md where it interacts with the
    memory governor.
  • Tests cover: bound not reached, bound reached mid-evaluation, cancellation
    from another thread, retry after a bounded-out step, and interaction with
    a recursive program.
  • No behaviour change for a caller that sets neither, on either the easy or
    the advanced facade.

Discovery context

Raised from downstream wyrelog work (semantic-reasoning/wyrelog#1206) against
the released 0.70.0 pin (c353350). wyrelog rebuilds a fact graph's engine on
every fact mutation and swaps the result in atomically, so evaluation cost lands
on the write path. It wants to admit operator-registered derivation rules, but
cannot allow recursion in them: an unbounded recursive rule would stall writes
to that graph with nothing able to interrupt it. wyrelog recorded the same
precondition earlier in its own commit 960427ae — "keep query input as a parsed
allowlisted atom until cancellation and recursion budgets exist."

That is one host's motivation, not the scope of this issue. Any host embedding
wirelog behind a request boundary has the same gap.

Implementation plan — 2026-09-22

Analyzed current origin/main at e8e977ec6ffcc0bcc0b0a4aae56bb842da34b389. This plan preserves the original requirement and acceptance above. Independent Architect, Critic and Reviewer reviewed the six issue drafts; this is planning approval, not implementation or passing-test evidence.

Agreed first implementation scope

Deliver cooperative cancellation plus a per-evaluation-attempt work budget, observable consumption and retry with a larger allowance. Include evaluating STEP/SNAPSHOT calls, typed wrappers, both easy and advanced public facades, serial operators and active parallel paths. Defer engine-managed wall-clock deadlines; hosts can drive cancellation from their own timer. Cooperative cancellation does not preempt host callbacks or promise a wall-clock return deadline.

Iteration/output-row limits alone cannot bound a large acyclic JOIN or rejected-candidate scan. Charge input traversal, probes/candidates and bounded internal processing, including no-output work, under a documented metric and maximum polling quantum. One allowance covers coordinator and all workers; internal replay/fallback does not reset it. Different worker counts/optimizer choices need not report equal work.

Registered implementation issues

Every item below is an actual GitHub sub-issue and contains its own context, source paths, implementation scope, acceptance tests, dependencies and existing-issue boundaries.

Order Issue Deliverable Hard prerequisites
Foundation #1819 Retained control, versioned options, status/usage accounting and concurrency contract None
Recovery #1820 STEP/SNAPSHOT attempt lifecycle, original-baseline retry and publication safety #1819
Serial enforcement #1821 Operator, row/candidate and expensive-fallback metering #1819, #1820
Parallel enforcement #1822 Global allowance, cooperative worker stop, replay accounting and safe drain #1819, #1820, #1821
Public enablement #1823 Installed easy/advanced options, cancel/configure/report APIs and distinct errors #1819–#1822
Qualification #1824 Final-SHA conformance matrix, documentation and host examples #1819–#1823

Implementation can use smaller atomic commits within each issue. API design and test preparation can proceed against the foundation contract; exposing an enforceable public option waits for the serial and parallel coverage gates.

Contracts that the implementation must preserve
  • Options are borrowed only during creation; the control has independent retained lifetime. One control attaches to one logical session, including easy lazy-open ownership before inner session creation. Workers borrow the same attempt.
  • No control and zero/unlimited allowance preserve existing behavior. Decode accepted internal v2/v3 prefixes explicitly; retain legacy easy option prefixes and old advanced constructor signatures.
  • Cancellation is sticky; only requesting it is concurrently supported. Change the next attempt's allowance/reset the token while quiescent, including when pending retry forbids ordinary input mutation.
  • Each caller retry starts a new consumption report while the original logical-step notification baseline survives. One evaluating snapshot that internally resumes a step remains one attempt.
  • Failed evaluation publishes nothing. Once publication commits, finish the successful callback batch; late cancellation applies to the next attempt. Notification-only cancellation remains separate.
  • Callback-free retry requires explicit recovery proof: current observer state is conditional on a delta callback, and plain-step completion bookkeeping does not by itself prove arbitrary-stop safety.
  • Distinct exhaustion/cancellation statuses must survive expression errors, worker aggregation, cleanup and public facade mapping. Do not repurpose EOVERFLOW/EAGAIN, which existing paths can interpret as fallback.
  • Stop must preserve input/retraction/frontier/trace state and owned buffers/leases/reservations. Mandatory cleanup/drain is allowed after work exhaustion; no task context is freed before its submitted workers finish.
  • Document real polling units, uninterruptible library/host calls and cleanup costs. Do not call an arbitrary whole-input sort a bounded checkpoint.
Existing issue ownership

Reuse #1369/#1382 memory admission, #1370 bounded batches and #1777 pipeline integration. #1373/#1383 own bounded channels and memory backpressure; consume their stop/wake contract if present without making the entire memory project a prerequisite. Test real current transport behavior and do not claim a nonexistent blocked-producer scenario.

#1765/#1805 own existing cleanup defects. Audit current merged fixes and require any still-reachable defect resolved before corresponding cancellation acceptance; do not duplicate ownership mechanisms. Closed #1812 supplies lifetime evidence. #1375 retirement does not excuse missing coverage for currently enabled K-Fusion.

Parent completion mapping
Original acceptance Implementation/evidence owner
Versioned host-supplied bound/cancel handle #1819, #1823
Finite documented polling and distinct errors #1819, #1821, #1822, #1823
Usable session and baseline-preserving retry #1820, #1822, #1823
Observable consumed work #1819, #1823
Semantics and memory documentation #1824
Bound/cross-thread cancellation/recursion/retry tests Focused tests in #1819–#1823; final matrix in #1824
Unchanged easy/advanced defaults #1823, #1824

Keep #1817 open until those behaviors have merged and the final-candidate evidence passes. Publishing this plan and its sub-issues does not satisfy implementation acceptance.

Dominant language
C
Stars
18
Forks
2
Avg merge
6h 19m
Merged PRs (30d)
280

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from semantic-reasoning/wirelog

All issues in semantic-reasoning/wirelog

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.