dinah 0.1.0

Rust-first modular runtime and high-performance compute foundation
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
# dinah Runtime — initial execution model

This document records the Runtime v0.1 design contract and implemented behavior.
The implementation intentionally remains small and synchronous at submission time;
parallel execution is an explicit queue-draining operation, not background work.
The contract distinguishes implemented guarantees from future worker-lifecycle
features that are not part of v0.1.

## Runtime v0.1 contract (point 060)

The public surface is a set of explicit primitives, not a fully managed task
service. `Executor` invokes one closure on the caller's thread. `Scheduler::schedule`
also runs immediately; `dispatch_next` and `run_until_empty` drain a caller-owned
bounded FIFO queue sequentially, while `run_parallel` uses a bounded set of scoped
workers reused across batches during that call. Results retain dispatch order and
task-level errors remain distinct from scheduler submission errors.

The queue is single-owner and not internally synchronized. `CancellationToken` is
cooperative and does not interrupt a closure. `RuntimeMetrics` and `TraceBuffer`
are opt-in recording primitives; they are not automatically instrumented by the
scheduler. `TaskState` validates transitions but is not an automatically maintained
task registry. `Shared<T>` provides explicit mutex-backed sharing. Task panics are
not converted into `TaskFailure`; sequential execution unwinds directly, while
`run_parallel` joins the current batch and scoped workers before resuming the panic.

Not included in v0.1: a background scheduler, workers persistent across calls,
retries, automatic cancellation propagation, automatic metrics/tracing, or a
scheduler-wide close/shutdown lifecycle. These omissions are deliberate API limits,
not implicit guarantees.

## Runnable example

`examples/runtime_basics.rs` demonstrates a bounded FIFO `TaskQueue`, explicit sequential draining with `Scheduler::run_until_empty`, and opt-in bounded parallel batches with `Scheduler::run_parallel`. Results remain in dispatch order; the example sorts only the separately collected side effects before printing, because worker completion order is not guaranteed. Run it with `cargo run --example runtime_basics`. The scheduler does not create persistent background workers.

## Responsibilities

- **Task** represents one submitted unit of work and its observable lifecycle. It
  must not own scheduler policy or worker-management responsibilities.
- **Executor** performs a task that has already been selected for execution. It
  reports the outcome; it does not decide global priority or queue ordering.
- **Scheduler** chooses eligible work and coordinates dispatch. It must not run
  user work while holding an internal scheduling lock.
- **Queue** stores pending work and provides the ordering contract selected by the
  scheduler. Queue mechanics must remain an implementation detail unless a public
  queue API proves necessary.
- **Worker** is an execution resource, not a task and not a scheduling policy.
  Worker identity and lifecycle should be added only with the first concrete
  worker implementation.

## Task contract (point 037)

A `Task` is the logical, one-shot unit of work accepted by the Runtime. It has
identity, observable lifecycle state, and eventually one terminal outcome. It is
not a worker, queue entry, scheduler, or executor. The same logical task must
not begin execution more than once, even if dispatch or worker operations fail.

The contract is deliberately split into stable semantics and a not-yet-selected
Rust representation:

- **Ownership:** once submission is accepted, the Runtime owns responsibility
  for either executing the task or reporting that it was cancelled/rejected
  according to the submission contract. An unaccepted task remains the caller's
  responsibility.
- **Single execution:** a task is started at most once. Retries, if ever added,
  must be explicit and must define whether they create a new task identity.
- **Lifecycle:** the state transitions in the next section are authoritative;
  a cancellation request is not itself proof that a running task stopped.
- **Outcome:** every terminal task distinguishes success, failure, and
  cancellation. Failure information should remain task-specific rather than be
  flattened into `CoreError`. A task cannot have two terminal outcomes.
- **Isolation:** task logic must execute outside scheduler/queue internal locks.
  Runtime bookkeeping must not imply that arbitrary user work is deterministic
  or side-effect-free.
- **Identity scope:** identity is assigned by the Runtime-facing API once the
  `TaskId` contract is defined; this section does not imply global uniqueness.
- **Representation not yet frozen:** closure type erasure, output/result handles,
  `Send + 'static` bounds, panic capture, and concrete error storage remain open
  until the executor and queue API are designed. These are implementation/API
  decisions, not permission to weaken the semantic guarantees above.

Acceptance criteria for point 037: the meaning, ownership boundary, one-shot
rule, lifecycle relationship, terminal outcome, and isolation requirement are
written down. The concrete Rust type and executable payload are separate follow-up
decisions; this point does not claim that a `Task` type exists in code.

## Task identity contract (point 038)

`TaskId` is a distinct Runtime-domain identifier for one logical task. It must
not be interchangeable with `CoreId` or a future `WorkerId`, even if those types
use the same numeric representation. This prevents accidentally passing an ID
from one resource domain into another API.

The initial contract is:

- **Representation:** a non-zero `u64`, wrapped in a dedicated `TaskId` newtype.
  Zero is reserved as invalid/uninitialized, matching the Core's sentinel policy.
- **Construction:** the initial type validates caller-supplied numeric values;
  it does not allocate IDs. Construction with zero returns a specific
  `TaskIdError`, rather than a generic `CoreError`.
- **Uniqueness:** the value type alone does not guarantee uniqueness. The
  component that creates/submits tasks must ensure no duplicate live task IDs
  within the relevant Runtime instance. The allocator and reuse policy are not
  defined by this value-type contract. No cross-process or global uniqueness is
  promised.
- **Stability:** a task's ID does not change during its logical lifetime. A retry
  must explicitly define whether it is the same logical task or a new task.
- **Value semantics:** equality, ordering, hashing, copying, display, and access
  to the numeric value are supported; serialization and persistence formats are
  deferred until a real storage/API requirement exists.
- **Scope:** `TaskId` identifies a task; it does not represent task state, prove
  that a task is registered, or authorize access to a task result.

The newtype intentionally mirrors `CoreId`'s simple non-zero value semantics
without aliasing it. It does not imply that `CoreId` must be used for Runtime
tasks, nor that `HasCoreId` should be implemented by `Task`.

Acceptance criteria for point 038: the domain, numeric representation, invalid
value, construction responsibility, uniqueness limits, stability, and public
value semantics are documented. This is a design decision; implementation and
tests remain separate work.

## Task lifecycle

The initial conceptual states are:

1. `Pending` — accepted and eligible for dispatch, but not yet started.
2. `Running` — execution has begun.
3. `Completed` — execution returned successfully.
4. `Failed` — execution returned an error or panicked and the runtime captured it.
5. `Cancelled` — the runtime prevented execution from starting, or cooperative
   cancellation was acknowledged by the running task.

Allowed transitions for the initial model:

- `Pending -> Running`
- `Pending -> Cancelled`
- `Running -> Completed`
- `Running -> Failed`
- `Running -> Cancelled` only when cooperative cancellation has actually been
  observed; a cancellation request alone is not a completed cancellation.

Terminal states (`Completed`, `Failed`, `Cancelled`) do not transition again.
A task outcome must distinguish successful completion, failure, and cancellation;
these must not be inferred solely from a generic runtime lifecycle state.

## Task state contract (point 039)

`TaskState` is a Runtime-specific enum representing the observed lifecycle of one
task. It is separate from Core's `LifecycleState`: a task's execution lifecycle
is not the lifecycle of a long-lived Runtime resource. The initial variants are
`Pending`, `Running`, `Completed`, `Failed`, and `Cancelled`.

The transition relation is deliberately closed and pure:

| Current state | Allowed next state | Meaning |
| --- | --- | --- |
| `Pending` | `Running` | The executor has claimed the task and execution is beginning. |
| `Pending` | `Cancelled` | The task was prevented from starting. |
| `Running` | `Completed` | The task returned successfully. |
| `Running` | `Failed` | Execution returned an error or a panic was captured under the selected panic policy. |
| `Running` | `Cancelled` | Running work observed and acknowledged cooperative cancellation. |
| `Completed`, `Failed`, `Cancelled` | none | Terminal states never transition again. |

Every transition not listed above is forbidden, including transitions to the same
state. A cancellation request is a request, not a state transition; a running
task remains `Running` until it acknowledges cancellation or reaches another
terminal outcome. State-checking methods must be pure: they do not mutate state,
provide synchronization, or claim that a transition has happened. The later
Task implementation must enforce transitions atomically wherever multiple
workers could race to claim or finish a task.

The state records lifecycle only, not the result payload or error details. Those
belong to a separate outcome/result contract. `TaskState` must be copyable,
comparable, debuggable, and hashable; no timestamps, worker IDs, retry counters,
or cancellation-request flag are embedded in this enum.

Acceptance criteria for point 039: the variants, allowed transition matrix,
terminal-state rule, cancellation semantics, separation from outcome data, and
concurrency enforcement requirement are documented. This point defines the
contract and does not claim that the enum or transition implementation exists.

## Worker contract (point 040)

A `Worker` is an execution resource responsible for running work that an executor or scheduler has dispatched to it. It is not a `Task`, does not choose global scheduling policy, and is not itself the queue. This is a role contract; no worker type, thread pool, or background thread is implemented by documenting it.

The initial responsibilities and boundaries are:

- **Execution boundary:** accept or claim work only through the executor's defined dispatch contract, run the selected task at most once, and make its execution outcome available to Runtime bookkeeping. A worker must not silently discard work that the Runtime has already accepted.
- **Isolation:** execute user task logic outside internal scheduler and queue locks. Internal locks may protect dispatch bookkeeping, but must not be held while arbitrary user code runs.
- **Identity:** a worker's identity belongs to the worker domain and must not be represented by `TaskId` or `CoreId`. A dedicated `WorkerId` contract is the next design point (041); this section does not imply that such a type exists.
- **Lifecycle:** a worker is a Runtime execution resource, distinct from a task's `TaskState` and Core's `LifecycleState`. Concrete states and legal transitions are deferred until the worker implementation and shutdown protocol are designed.
- **Failure and shutdown:** panic handling, worker replacement, draining or cancelling dispatched/pending work, and worker termination guarantees remain explicit open decisions. No failure-recovery or shutdown guarantee is claimed before it is implemented and tested.
- **Configuration:** `CoreConfig::max_workers` is Core configuration data; it does not establish that a worker pool exists, that this value is currently consumed by Runtime, or that dynamic resizing is supported.

The first worker implementation should remain in safe Rust and keep task execution outside scheduler/queue critical sections. Thread creation, synchronization primitives, worker count semantics, and dispatch ownership must be introduced in implementation work with tests rather than inferred from this contract.

Acceptance criteria for point 040: the worker's role, separation from tasks and scheduling policy, execution/locking boundary, identity domain, and deferred shutdown/failure semantics are documented. This point defines the contract only; it does not claim a concrete `Worker` type or worker threads exist in code.

## Worker identity contract (point 041)

`WorkerId` is a Runtime-domain identifier for one worker resource. It is a
different type from `TaskId` and `CoreId`, even if all three use the same
numeric representation. The type distinction prevents passing a task identifier
where a worker identifier is expected and keeps Runtime resource identity out of
Core's more general identity domain.

The initial contract is:

- **Representation:** a dedicated `WorkerId` newtype wrapping a non-zero `u64`.
  Zero is invalid/reserved, consistent with the existing `CoreId` and planned
  `TaskId` value contracts.
- **Construction:** the value type validates a caller-supplied numeric value and
  returns a specific `WorkerIdError` for zero. It does not allocate IDs or
  register workers as a side effect.
- **Uniqueness:** the newtype alone does not guarantee uniqueness. The component
  creating/owning workers must avoid duplicate IDs among live workers in the
  relevant Runtime instance. No process-wide, cross-process, or global
  uniqueness is promised. Allocation, reuse, and persistence policies remain
  deferred until required by a concrete worker manager.
- **Stability and scope:** a worker's ID remains stable for that worker resource's
  lifetime. It identifies neither a task nor a worker's lifecycle state, thread
  handle, liveness, permissions, or task result. Replacing a failed worker must
  explicitly define whether the replacement receives a new identity.
- **Value semantics:** support equality, ordering, hashing, copying, display, and
  access to the numeric value. Serialization and persistence formats are not
  specified without a real API or storage requirement.
- **Separation from registration:** constructing a valid `WorkerId` does not prove
  that a worker exists, is running, is registered, or is owned by a particular
  Runtime instance. Those facts belong to the future worker-management API.

The intended implementation is a small safe-Rust value type with exhaustive tests
for zero rejection, valid boundary values, display, equality/order, and hashing.
This is a contract only: no `WorkerId` or `WorkerIdError` type is claimed to exist
in code until a separate implementation point adds it.

Acceptance criteria for point 041: the domain, newtype representation, invalid
value behavior, construction and allocation boundary, uniqueness limits,
stability, and value semantics are documented.

## Bounded task queue (point 044)

The first queue implementation is a generic, single-owner `TaskQueue<T>` backed
by `VecDeque<T>` and configured with a non-zero capacity. It provides FIFO
`try_push`/`pop`, length/empty/full inspection, and exposes its configured
capacity. It is deliberately not a concurrent queue and does not execute tasks.

When the queue is full, `try_push` returns `QueuePushError::Full(item)` with the
original item, preserving caller ownership and preventing silent work loss.
`Scheduler::dispatch_next`, `run_until_empty`, and `run_parallel` consume this
queue by removing tasks before executing user closures. Queue insertion remains a
caller-owned operation; a full queue returns the rejected task without losing it.
Tests cover empty state, FIFO order, full-queue rejection without item loss, and
reuse after a pop.

## Queue-backed dispatch (point 045)

`Scheduler::dispatch_next` integrates the bounded queue and synchronous executor:
it removes at most one oldest closure from `TaskQueue`, then executes it after
the queue borrow has ended. An empty queue returns `None`; a task result or task
error is returned as `Some(result)` without erasing the task error. This is a
sequential dispatch primitive, not a worker pool, background queue consumer, or
concurrent scheduler. Queue-full rejection remains the responsibility of the
caller when inserting work.

## Initial delivery sequence

1. Define task identity, task state, and outcome contracts with exhaustive tests.
2. Implement a sequential executor first, making ordering and failure behavior
   deterministic and easy to verify.
3. Add a scheduler and bounded queue with explicit capacity and submission-error
   behavior; do not silently drop accepted tasks.
4. Introduce bounded scoped workers after sequential semantics are stable. Keep
   user callbacks outside queue locks and preserve FIFO result order.
5. Keep cancellation cooperative and document that tokens do not interrupt work;
   parallel worker shutdown is scoped to each `run_parallel` call.
6. Maintain public-API integration tests and repeatable benchmarks. A background
   persistent pool and scheduler-wide close/shutdown lifecycle are not part of the
   current v0.1 contract.

## Design constraints

- Keep the first Runtime implementation in safe Rust; no `unsafe`, FFI, or new
  dependency is justified by this design alone.
- Do not promise FIFO, fairness, work-stealing, or hard real-time behavior until
  the selected scheduler implements and tests that guarantee.
- Do not promise preemption. Cancellation of running work is cooperative unless
  a later design proves a safe alternative.
- A task ID identifies a task within the documented runtime scope; global or
  cross-process uniqueness is not implied without an explicit allocator contract.
- A failed task should retain a useful task-level error/panic outcome without
  forcing every detailed error into the coarse `CoreError` type.
- Shutdown must define whether pending tasks are drained or cancelled; the API
  must make the selected policy explicit rather than silently discarding work.

## Decisions deliberately deferred

- Closure representation and whether submitted tasks are one-shot `Send` jobs.
- Public versus private task handles and result retrieval API.
- Queue ordering, bounded-capacity defaults, and backpressure policy.
- Worker count defaults and resizing behavior.
- Panic policy and whether a panicked worker is replaced.
- Whether `HasCoreId` is useful for actual Runtime resource types; no Runtime type
  should implement it merely to justify the abstraction.

These decisions require the concrete Task and executor API plus tests. This
model is the boundary for point 036; the subsequent points cover implementation
of the individual types and mechanisms.


## Sequential queue drain (point 046)

`Scheduler::run_until_empty` repeatedly dispatches the oldest queued closure
until no work remains, returning each task result in FIFO order. Task-level
errors are retained in the returned vector and do not prevent later queued
tasks from executing. An empty queue returns an empty vector. This is a
synchronous drain on the caller's thread; it does not imply parallelism,
background progress, panic recovery, or a persistent scheduler lifecycle.


## Determinism regression coverage (point 089)

`tests/runtime_determinism.rs` exercises the public `Scheduler::run_parallel` contract with 64 queued tasks, five worker limits (1, 2, 3, 7, 64), and 16 repetitions per limit. Tasks have different deterministic amounts of work and a subset returns errors. Each run must return the same FIFO-indexed `Result` vector and drain the queue. A second repeated contract test checks cancellation request idempotence across cloned tokens, stable task-state transition errors, independent metric counters, and trace-buffer FIFO snapshots, full-buffer rejection, draining, and reuse. The tests deliberately avoid wall-clock assertions: they check stable observable results, not completion order, fairness, or timing determinism.

## Bounded parallel execution (point 047)

`Scheduler::run_parallel` executes queued closures in batches of at most the
caller-supplied non-zero worker limit. Each batch is removed from the queue
before its tasks run on a bounded set of scoped workers, reused across batches
within that call. Results are returned in FIFO dispatch order, not completion
order; task-level errors are retained and later batches continue. The queue is
empty after a normal return. Tasks and result/error values must satisfy `Send`.
Task panics are captured only to finish the current batch and shut workers down,
then the panic is resumed on the caller; later batches remain queued. Workers do
not persist across calls, and there is no background worker lifecycle or shutdown
API.


## Shared-state synchronization (point 048)

`Shared<T>` is an opt-in, cloneable owner around `Arc<Mutex<T>>`. Clones point
to the same value, and `lock()` gives exclusive access through a guard. It
returns `SynchronizationError::Poisoned` if a previous thread panicked while
holding the mutex; the wrapper does not guess whether the protected value can
be safely recovered. No extra dependency or unsafe code is needed.

The lock protects only access to the wrapped value; it does not make arbitrary
operations atomic unless callers perform them while holding the guard. Keep
critical sections short and do not execute arbitrary task callbacks while holding
a guard. This primitive does not yet make `TaskQueue` or `Scheduler` themselves
concurrently mutable, and it does not promise fairness, lock-free progress, task
cancellation, or panic recovery. Tests cover shared clone visibility, contended
increments across threads, and explicit poison reporting.


## Task error classification (point 049)

`TaskFailure<E>` wraps the original task error and labels it as
`TaskFailureKind::ReturnedError`. It preserves the concrete error type and
provides borrowing, consuming, display, and error-mapping operations without
flattening details into `CoreError`. Mapping an error retains the classification.

This is deliberately a task-error value, not a scheduler rejection, lock error,
cancellation signal, or panic wrapper. The executor and scheduler continue to
return their generic `Result<T, E>` values unchanged; callers can opt into
`TaskFailure::returned(error)` when they need a stable task-level category.
Panics still unwind and are not caught or mislabeled as returned errors. This
point does not yet implement a task registry or aggregate multiple task outcomes.
Tests verify preservation of the original error, classification, display, and
error mapping.


## Cooperative cancellation token (point 050)

`CancellationToken` is a cloneable, dependency-free signal backed by an atomic
boolean. Any clone can request cancellation, and all clones can observe the
request. Repeated requests are idempotent; `request_cancellation()` returns
`true` only to the caller that first changed the flag. Acquire/release ordering
provides visibility for the cancellation flag itself, not synchronization for
unrelated task data.

Cancellation is cooperative: this token cannot interrupt a closure, kill a
thread, roll back side effects, or guarantee that work has stopped. Task code
must poll at safe points and return or otherwise complete its own cleanup. A
request must not be confused with a confirmed `Cancelled` terminal task state;
that requires task lifecycle bookkeeping and observation of the task's actual
completion, which are not implemented by this primitive. Tests cover shared
visibility across clones and idempotent concurrent requests.


## Task finalization state transitions (point 051)

`TaskState` implements the documented task lifecycle with a checked transition
method. Only `Pending -> Running`, `Pending -> Cancelled`, and `Running ->
Completed`, `Failed`, or `Cancelled` are permitted. Self-transitions and all
transitions out of terminal states are rejected. `InvalidTaskTransition` keeps
the source and requested destination for diagnostics, and rejected transitions
leave the state unchanged. `is_terminal()` identifies `Completed`, `Failed`,
and `Cancelled`.

This value type is not synchronized; callers sharing state across threads must
provide synchronization and serialize updates. It does not provide atomic task
claiming across workers. A transition to `Cancelled` from `Running` is valid
only after cooperative cancellation is actually acknowledged, not merely
requested. Task result payloads remain separate from lifecycle state, and panic
capture remains outside this implementation.


## Runtime metrics (point 052)

`RuntimeMetrics` provides opt-in atomic counters for submitted, started,
completed, failed, cancellation-requested, and cancellation-acknowledged events.
The API deliberately uses explicit `record_*` methods: the current executor and
scheduler do not update counters automatically, so callers must instrument the
corresponding event sites. A cancellation request and cancellation acknowledgement
are separate counters and must not be conflated.

Counters use relaxed atomic operations because they are statistics, not a
synchronization mechanism for task data. Increments saturate at `u64::MAX` to
avoid wraparound. `snapshot()` reads each counter safely, but the group is not a
globally atomic snapshot while concurrent updates occur. No task labels, timing,
external exporter, allocation, or dependency is introduced. Tests cover event
separation and concurrent increments.


## Structured tracing (point 053)

`TraceEvent` defines lifecycle event kinds for task submission, start, completion,
failure, and cancellation request/acknowledgement. `TraceBuffer` stores events in
recording order in a bounded, mutex-protected in-memory buffer. The caller chooses
a non-zero capacity. When full, recording rejects the new event and returns it
without overwriting existing history; callers may inspect or drain the buffer.

Tracing is opt-in and not automatically emitted by the executor or scheduler,
which currently lack task identity and a unified instrumentation hook. Do not
record while holding unrelated application locks. Snapshot/drain briefly lock the
buffer and copy or remove events; this implementation does not add timestamps,
span nesting, exporters, automatic task IDs, or external dependencies. Tests cover
ordered recording, overflow preservation, draining, and concurrent recording.


## Concurrency regression coverage (point 054, complete)

The scheduler tests include a barrier-based regression test that requires two
tasks to reach a synchronization point concurrently, then verifies the results
remain in FIFO dispatch order and the queue is drained. The barrier avoids relying
on wall-clock timing to infer overlap.

## Executor regression coverage (point 057, complete)

Executor tests cover successful return values, exactly-once invocation, error
identity preservation, and propagation of task panics to the caller. Panic
propagation is intentional for this synchronous executor; it does not convert
unwinds into task errors or guarantee cleanup beyond Rust's normal unwinding
semantics.


## Scheduler regression coverage (point 056, complete)

Scheduler tests verify immediate submission, single dispatch, FIFO queue draining,
empty queues, task-error preservation, and parallel execution bounded by a
non-zero worker limit. Additional regressions verify that task errors do not
prevent later parallel batches from running and that a one-worker configuration
preserves FIFO results across batch boundaries. These tests describe the current
synchronous and temporary-scoped-thread implementation; they do not imply a
persistent worker pool or background scheduling.


## Race-condition regression coverage (point 055, complete)

Cancellation tests cover simultaneous idempotent requests and a separate race
between one requester and 16 cloned-token observers. A barrier releases the
observers and requester together; observers use the atomic cancellation flag and
finish only after observing the request. The test validates eventual visibility
without sleep-based timing assumptions. These tests validate the cancellation
signal's concurrency contract, not interruption or task termination. Broader
scheduler and executor coverage remains tracked by points 056–057.

## Runtime benchmark harness (point 058)

`benches/runtime.rs` is a dependency-free, release-mode benchmark executable.
Run it remotely with `cargo bench --bench runtime`; the dedicated GitHub Actions
workflow runs it when benchmark/runtime sources change or by manual dispatch and
uploads the raw output as a 90-day artifact. The normal CI suite remains separate
from timing runs to avoid treating noisy measurements as a correctness gate.

The harness reports seven-sample medians for direct `Executor` no-op calls,
immediate `Scheduler` submissions, sequential queue draining at 1/16/64/256
items, and parallel queue draining at 16/64/256 items with 2 or 4 workers,
including no-op tasks to expose dispatch/thread-startup overhead. The current
scheduler reuses scoped workers across batches within each `run_parallel` call;
the historical pre-optimization measurements below used temporary threads per
batch. It compares sequential and parallel execution of a deterministic CPU
workload with 1,000,000 integer-mixing iterations per task. A benchmark-only
persistent-pool prototype also reports pool startup/shutdown separately and
reuses 2 or 4 workers for no-op and CPU task batches. This prototype is isolated
to `benches/runtime.rs`; it is not production Runtime code and does not yet
establish a public pool API or lifecycle contract. It prints total median
nanoseconds, decimal nanoseconds per task, and approximate tasks per second.
`black_box` limits dead-code elimination, every production-scheduler queue is
asserted empty, and all tasks return a checked successful result.

These cases primarily characterize dispatch/queue/thread-creation overhead for a
trivial deterministic task; they do not model CPU-heavy application workloads,
prove that parallel dispatch is faster, or establish cross-machine comparisons.
Results are machine- and runner-specific observations. A successful reference run
on GitHub Actions (2026-10-10, [run 38057210199](https://github.com/lecodev-26/dinah/actions/runs/38057210199))
produced the following medians; the raw log is retained in the run artifact
`dinah-runtime-benchmarks-b95f75e...` (90-day retention):

| Case | Tasks | Median total | Median per task |
| --- | ---: | ---: | ---: |
| Executor no-op | 100,000 | 78.489 µs | 0.785 ns |
| Immediate scheduler no-op | 100,000 | 78.429 µs | 0.784 ns |
| Sequential queue drain | 256 | 2.865 µs | 11.191 ns |
| Parallel queue drain, 2 workers | 256 | 11.541 ms | 45.083 µs |
| Parallel queue drain, 4 workers | 256 | 10.958 ms | 42.805 µs |

The historical pre-optimization no-op parallel cases were dominated by creating
short-lived threads for each batch. The first 10,000-iteration CPU baseline was
also too small to infer the useful-work crossover, so the harness raised the work
to 1,000,000 iterations per task. The earlier run ([run 38057383426](https://github.com/lecodev-26/dinah/actions/runs/38057383426)) measured 256 tasks at 39.849 ms sequentially, 32.190 ms with 2 workers, and 34.690 ms with 4 workers. These are historical baseline results, not measurements of the current scheduler. Point 033 (Core benchmarks) remains open until a representative Core operation and suitable workload are identified and measured.


## Scheduler optimization investigation (point 059, completed)

The measurements justified reducing per-batch thread creation. `Scheduler::run_parallel`
now starts up to `min(max_workers, queued_tasks)` scoped workers once per invocation
and reuses them across batches, rather than creating a new set of threads for every
batch. This remains a per-call scoped worker set, not a background pool retained
across calls: it preserves borrowed task closures and avoids adding a public worker
lifecycle or shutdown API.

Two pre-change runs ([38058048968](https://github.com/lecodev-26/dinah/actions/runs/38058048968),
[38058161242](https://github.com/lecodev-26/dinah/actions/runs/38058161242)) and two
post-change runs ([38058454819](https://github.com/lecodev-26/dinah/actions/runs/38058454819),
[38058504887](https://github.com/lecodev-26/dinah/actions/runs/38058504887)) provide a
repeatable directional comparison at 256 tasks:

| Workload | Before: temporary threads | After: scoped worker reuse | Median change |
| --- | ---: | ---: | ---: |
| No-op, 2 workers | 12.20 ms | 3.27 ms | ~73% faster |
| No-op, 4 workers | 11.00 ms | 2.08 ms | ~81% faster |
| CPU, 2 workers | 31.69 ms | 24.91 ms | ~21% faster |
| CPU, 4 workers | 35.83 ms | 22.65 ms | ~37% faster |

These are synthetic, runner-specific medians rather than performance guarantees;
individual runs vary, especially for CPU-bound cases. Raw outputs and provenance
are preserved under `artifacts/benchmarks/runtime-scoped-workers-*`.

Regression tests verify FIFO result order across batches, execution within the
worker limit, task-error handling, empty queues, single-worker behavior, and panic
propagation without losing the later queued batch. The implementation catches task
panics only long enough to finish the current batch and shut down workers, then
resumes unwinding; it does not convert panics into ordinary task errors. The full
CI workflow and the dedicated benchmark workflow passed. Point 059 is complete for
this scoped-worker-reuse optimization. A truly persistent pool across calls remains
a separate future design decision requiring a public lifecycle/shutdown contract
and fresh measurements.



## Durable benchmark artifacts

The benchmark workflow uploads its output to GitHub Actions with a 90-day
retention period. A version-controlled copy of the first extended runtime
baseline is preserved under `artifacts/benchmarks/runtime-baseline-2026-10-10.txt`,
with provenance in `artifacts/benchmarks/README.md`. Future important generated
reports should be archived in `artifacts/` with dated filenames and source-run
metadata so they remain available after CI artifact retention expires.

## Concurrent determinism coverage (point 089)

`tests/runtime_determinism.rs` repeats concurrent updates through `Shared<T>` clones and confirms the final total and clone release. It also verifies metric totals and the `TraceBuffer`'s capacity, draining and clearing with concurrent producers. No deterministic order between events from distinct threads is claimed.

The same suite also checks `TaskQueue`'s public API: rejection returning the original element when full, absence of mutation after rejection, FIFO order and reuse after extracting elements. `TaskFailure` preserves the `ReturnedError` classification when querying, transforming and consuming the original error.