feat(spec): refuse a multi-item authored sampler while enumerating

from().generate() draws from the picker's rng, which exists only inside
walkActions. The model policy enumerates authored leaves outside that walk, so
the sampler silently yielded its first item on every step: measured over 30
draws the seeded arm reached three targets in roughly equal proportion and the
model was offered only the first. The two policies had different action spaces
and nothing said so.

A single-item sampler short-circuits before the rng, so both policies get the
same value and it is not refused.

Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
This commit is contained in:
pj committed 2026-08-13 00:44:00 +05:30
1 parent 7dc23b137a
commit c76ba4b497
4 files changed
+59 -1

No files matched your search

+22
View File
@@ -30,6 +30,7 @@ import {
whenRoute,
} from "../src/index.ts";
import { setSamplerRng } from "../src/actions.ts";
import { SAMPLER_REFUSAL_NAME, setEnumeratingCandidates } from "../src/sampler-rng.ts";
import { Pcg } from "../src/pcg.ts";
import type { GeneratorNode } from "../src/action-tree.ts";
import type {
@@ -332,6 +333,27 @@ test("from falls back to the first item outside a picker walk", () => {
assert.equal(from(["a", "b", "c"]).generate(), "a");
});
test("from refuses a multi-item draw while the model policy enumerates", () => {
setEnumeratingCandidates(true);
try {
assert.throws(() => from(["a", "b", "c"]).generate(), {
name: SAMPLER_REFUSAL_NAME,
message: /draws 1 of 3 sampled items/,
});
} finally {
setEnumeratingCandidates(false);
}
});
test("from keeps serving a single item while the model policy enumerates", () => {
setEnumeratingCandidates(true);
try {
assert.equal(from(["only"]).generate(), "only");
} finally {
setEnumeratingCandidates(false);
}
});
function elementWithChildren(cells: Record<string, string>): AccessibilityElement {
return {
find: selector => {