Skip to content

Commit 3a3ac7b

Browse files
leifericfclaude
andcommitted
v0.102.1: agents adversarial-test pass + doc accuracy fixes
Adversarial whitebox test of v0.102.0's STM + agent surfaces (Clojure-level, C-API, and combinations) uncovered a misleading thread-budget message: agent_worker_ensure said embedders need ">= 2 to allow one agent worker plus the embedder thread", but the embedder thread does NOT count against thread_limit. Correct wording: >= 1 for one worker; >= 2 if both send and send-off are used concurrently. The corresponding STM page, Compatibility Matrix, Intentional Divergences, Coming-from-Clojure page, mino_send docstring, and the C-API agents cookbook example are updated separately (downstream-repo commits). A pre-existing future-worker thread_count starvation issue identified during the run is filed as NEEDS-DESIGN in .local/BUGS.md (fix requires a threading-model refactor). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b3962b0 commit 3a3ac7b

3 files changed

Lines changed: 61 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,45 @@
22

33
## Unreleased
44

5+
## v0.102.1 — Agents adversarial-test pass: doc accuracy fixes
6+
7+
Adversarial whitebox test of the v0.102.0 STM + Agent surfaces
8+
(both Clojure-level and the new C-API perimeter, individually and
9+
in combination) ran 70+ probes. All real findings are
10+
documentation accuracy issues -- no behavior changed.
11+
12+
- Fix the misleading thread-budget message in `agent_worker_ensure`
13+
(and the corresponding mino-site / `mino.h` / Coming-from-Clojure
14+
copy). The embedder thread does NOT count against
15+
`thread_limit`, so the previous wording (">= 2 to allow one
16+
agent worker plus the embedder thread") was wrong. Correct
17+
wording: ">= 1 for one agent worker; >= 2 if both send and
18+
send-off are used concurrently". The cookbook's `agents.c`,
19+
the STM page, the Compatibility Matrix, the Intentional
20+
Divergences page, and the Coming-from-Clojure page all updated
21+
to match.
22+
- Coming-from-Clojure previously said `agent / send / send-off
23+
/ pmap are not provided` when `thread_limit <= 1`. Updated:
24+
`agent` itself ships and constructors work; `send` /
25+
`send-off` throw MTH001 when their pool's worker can't spawn;
26+
only `pmap` is genuinely absent.
27+
- Compatibility Matrix's `send-via` row said "send and send-off
28+
share the same per-state worker." Stale -- v0.102.0 split them
29+
into POOLED + SOLO. Updated.
30+
- STM page intro paragraph said "a worker thread drains the queue."
31+
Updated to reflect both pools.
32+
- New adversarial probes added under `.local/adversarial/` for
33+
future regression coverage of the agent surfaces.
34+
35+
A pre-existing thread-count bookkeeping issue (`(future ...)`
36+
worker decrements lag the embedder under tight-loop contention,
37+
so a subsequent `(send ...)` may throw MTH001 even when fire-
38+
and-forget futures have logically completed) was identified and
39+
filed as NEEDS-DESIGN in `.local/BUGS.md`. The fix requires a
40+
non-trivial threading-model refactor; deferred to a dedicated
41+
cycle. Workaround: deref the last future or await an agent before
42+
spawning more workers.
43+
544
## v0.102.0 — Agents finish MVP: async dispatch + pool split + C-API
645

746
Agent execution model removes the synchronous-on-the-calling-thread
@@ -64,9 +103,9 @@ public C-API perimeter for embedders.
64103
pools still serialize, so the user-visible effect is the same
65104
as before, but the queues are independent: a long-running
66105
send-off action does not stall pending sends, and vice versa.
67-
Each pool's worker counts against `thread_limit`, so embedders
68-
that want both shapes alive concurrently must raise the limit
69-
to at least 3 (embedder + POOLED + SOLO worker). The split is
106+
Each pool's worker counts against `thread_limit` (the embedder
107+
thread does not). Embedders that want both shapes alive
108+
concurrently must raise the limit to at least 2. The split is
70109
also a clean seam for a future SOLO-yields-eval-lock-during-
71110
blocking-IO design without further user-facing churn.
72111
- Public C-API entries: `mino_send`, `mino_send_off`, `mino_await`,

src/mino.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*/
2929
#define MINO_VERSION_MAJOR 0
3030
#define MINO_VERSION_MINOR 102
31-
#define MINO_VERSION_PATCH 0
31+
#define MINO_VERSION_PATCH 1
3232

3333
/*
3434
* Human-readable version string of the *linked* runtime, e.g. "0.48.0".
@@ -588,8 +588,11 @@ int mino_is_agent(const mino_val_t *v);
588588
* - agent was created in a different state (MST007),
589589
* - agents have been shut down (MST008),
590590
* - the agent is failed and its error mode is :fail (MST002),
591-
* - the host has not granted enough threads to spawn the worker
592-
* (MTH001) -- raise via mino_set_thread_limit (>= 2).
591+
* - the host has not granted enough thread budget to spawn the
592+
* pool's worker (MTH001) -- the embedder thread does not count
593+
* against the limit; raise via mino_set_thread_limit (>= 1 for
594+
* one agent worker; >= 2 if both send and send-off are used
595+
* concurrently; more if mixing with futures / host threads).
593596
*
594597
* Inside a transaction, the action is queued and only fires after a
595598
* successful commit, matching JVM canon. */

src/prim/agent.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
*
1212
* Threading contract:
1313
*
14-
* - Each pool's worker counts against S->thread_limit, so a host
15-
* that has granted only one thread (default thread_limit == 1)
16-
* can have only one shape of send alive at a time. send /
17-
* send-off throw MTH001 if the host hasn't granted enough threads
18-
* to spawn the requested pool's worker; embedders that want both
19-
* shapes concurrently must raise the limit to >= 2 (in addition
20-
* to the embedder thread). Standalone `./mino` raises thread_limit
14+
* - Each pool's worker counts against S->thread_limit (the embedder
15+
* thread does NOT). Default thread_limit is 1, so a host that
16+
* hasn't called mino_set_thread_limit can have one agent worker
17+
* OR one future OR one host thread alive at a time. send /
18+
* send-off throw MTH001 if the host hasn't granted enough thread
19+
* budget to spawn the requested pool's worker. Embedders that
20+
* want both POOLED and SOLO alive concurrently must raise the
21+
* limit to >= 2; mixing with futures / host threads requires
22+
* correspondingly more. Standalone `./mino` raises thread_limit
2123
* to cpu_count after install_all so the REPL works out of the box.
2224
*
2325
* - Each worker is lazy-spawned on the first send/send-off into its
@@ -573,9 +575,10 @@ static int agent_worker_ensure(mino_state_t *S, agent_pool_kind_t kind)
573575
if (S->thread_count >= S->thread_limit) {
574576
prim_throw_classified(S, "mino/thread-limit-exceeded", "MTH001",
575577
"agent dispatch requires a host-granted worker thread; "
576-
"raise via mino_set_thread_limit (>= 2 to allow one agent "
577-
"worker plus the embedder thread; >= 3 if both send and "
578-
"send-off are used concurrently)");
578+
"raise via mino_set_thread_limit (>= 1 for one agent "
579+
"worker; >= 2 if both send and send-off are used "
580+
"concurrently). The embedder thread does not count "
581+
"against the limit -- only spawned workers do.");
579582
return 1;
580583
}
581584
S->multi_threaded = 1;

0 commit comments

Comments
 (0)