Skip to content

feat(benchmark): add-enumerate arm — tests whether ADD's single ranked flag is the surfacing ceiling - #191

Open
TinDang97 wants to merge 3 commits into
mainfrom
exp/add-enumerate-arm
Open

feat(benchmark): add-enumerate arm — tests whether ADD's single ranked flag is the surfacing ceiling#191
TinDang97 wants to merge 3 commits into
mainfrom
exp/add-enumerate-arm

Conversation

@TinDang97

Copy link
Copy Markdown
Collaborator

Across three amb1 reps ADD surfaced exactly 1 of 7 planted ambiguities EVERY
time — never 0, never 2 — while its PLAN.md template asks for one "Least-sure
flag surfaced at freeze", singular and ranked lowest-confidence first. Two
readings fit that data:

a) the singular flag is a CEILING — ADD noticed more and reported one;
b) ADD noticed one.

This arm separates them by adding ONE clause to the wrapper: at freeze, list
every choice the source spec does not settle, not just the least-sure one. If
(a), the rate rises; if (b), it does not and the flag design is exonerated.
Either outcome is informative, which is what an A/B needs.

Why this and not gate infrastructure: the earlier claim that ADD's disabled
human gate was the benchmark's largest distortion does not survive checking.
The frozen §3 contract in all three ADD runs contains no trace of the two gaps
every build shipped — no ownership, 403, permission or caller-scoping vocabulary
in ~3000 characters of contract each. A human at that gate would have approved a
document in which both decisions were invisible, because a gate reviews what was
written down and a silent gap is by definition not written down.

Also checked and NOT acted on: MARKERS contains "clarif", documented as catching
spec-kit's NEEDS CLARIFICATION token, while ADD's ⚠ idiom is absent — a real
asymmetry in the detector's construction. Adding "⚠" (and an "overlap" anchor)
changes NOTHING on these runs: the one sentence it newly matches attributes to
A-conflict-response, which ADD already surfaced. A change that would have
favoured the method this benchmark belongs to was tested and dropped.

Isolation is asserted mechanically, not by eye: the variant minus the clause must
equal the baseline byte for byte, so a second smuggled difference fails the suite
(verified by mutation). The clause is checked to leak no planted anchor, no
domain vocabulary from this workload, and no quota that could be padded.

Registered as EXPERIMENTAL: selectable by name, absent from ARM_NAMES, so no
default campaign changes cost or composition.

Not yet run — a live A/B costs roughly $2 per rep and is the human's call.

benchmark/tests: 411 -> 423 passing.

…d flag is the surfacing ceiling

Across three amb1 reps ADD surfaced exactly 1 of 7 planted ambiguities EVERY
time — never 0, never 2 — while its PLAN.md template asks for one "Least-sure
flag surfaced at freeze", singular and ranked lowest-confidence first. Two
readings fit that data:

  a) the singular flag is a CEILING — ADD noticed more and reported one;
  b) ADD noticed one.

This arm separates them by adding ONE clause to the wrapper: at freeze, list
every choice the source spec does not settle, not just the least-sure one. If
(a), the rate rises; if (b), it does not and the flag design is exonerated.
Either outcome is informative, which is what an A/B needs.

Why this and not gate infrastructure: the earlier claim that ADD's disabled
human gate was the benchmark's largest distortion does not survive checking.
The frozen §3 contract in all three ADD runs contains no trace of the two gaps
every build shipped — no ownership, 403, permission or caller-scoping vocabulary
in ~3000 characters of contract each. A human at that gate would have approved a
document in which both decisions were invisible, because a gate reviews what was
written down and a silent gap is by definition not written down.

Also checked and NOT acted on: MARKERS contains "clarif", documented as catching
spec-kit's NEEDS CLARIFICATION token, while ADD's ⚠ idiom is absent — a real
asymmetry in the detector's construction. Adding "⚠" (and an "overlap" anchor)
changes NOTHING on these runs: the one sentence it newly matches attributes to
A-conflict-response, which ADD already surfaced. A change that would have
favoured the method this benchmark belongs to was tested and dropped.

Isolation is asserted mechanically, not by eye: the variant minus the clause must
equal the baseline byte for byte, so a second smuggled difference fails the suite
(verified by mutation). The clause is checked to leak no planted anchor, no
domain vocabulary from this workload, and no quota that could be padded.

Registered as EXPERIMENTAL: selectable by name, absent from ARM_NAMES, so no
default campaign changes cost or composition.

Not yet run — a live A/B costs roughly $2 per rep and is the human's call.

benchmark/tests: 411 -> 423 passing.

author: Tin Dang
…d arm was unrunnable

The first launch of add-enumerate failed instantly on `unknown_arm` with 423
tests green. score.py validates arm names against ARM_NAMES independently of the
loader and the CLI, and the new suite covered the loader and the CLI only —
tested unit, untested seam, the same shape this codebase keeps producing.

Cheap this time: the run died before spending anything. It would not have been
cheap inside a multi-rep campaign.

The guard is now enumerative rather than remembered: no module outside tests may
validate against ARM_NAMES, the DEFAULT campaign set. A fourth gate added later
fails in the suite instead of at the start of a paid run.

author: Tin Dang
Comment thread benchmark/score.py
from benchmark import judge, tamper
from benchmark.ambiguity import is_implementation_write
from benchmark.arms.loader import ARM_NAMES
from benchmark.arms.loader import ALL_ARM_NAMES, ARM_NAMES
…ntation

add-enumerate's first run reported 3 of 7 surfaced against a baseline of 1 of 7.
Reading the evidence spans, two of the three were junk and the "rise" was
entirely spurious. Three distinct defects, all found by auditing a result that
moved in the direction I wanted:

D1 ANCHORS MATCHED INSIDE LONGER WORDS. "position" is a substring of
   "composition", so a sentence about image composition surfaced
   A-position-ordering. Anchors are TERMS; they now match on word boundaries.
   This also corrupted ATTRIBUTION, which ranks on anchor-hit count — a phantom
   hit could outrank and steal a genuine surfacing from the item that earned it.
   Markers stay unbounded deliberately: they are stems (assum -> assumption).

D2 A CLOSING XML TAG ACTED AS A MARKER. "assum" is a substring of
   "</assumptions>", so the tag ENDING ADD's assumptions block marked whichever
   sentence followed it — live, the opening line of the contract body. Simple
   tags are now blanked before matching, with EQUAL-LENGTH spaces so every offset
   (including edit_pos, which indexes the untouched transcript) still points
   where it did. Only well-formed tags: a greedy <...> strip would eat
   "start < other.end AND end > other.start", which is prose about a boundary and
   frequently the very thing being surfaced.

D3 THE ARTIFACT BUDGET READ SHIPPED DOCUMENTATION. _workspace_artifacts took the
   first 40 prose files in sort order. An ADD workspace holds 302, of which 256
   are the vendored personas-teacher library — so the budget was consumed
   entirely by ADD's own product documentation while PLAN.md, the agent's actual
   reasoning, sorted at index 270 and was never read at all. The live false
   positive came from design-image-prompt-engineer.md, whose boilerplate reads
   "Avoid ambiguous language that could be interpreted multiple ways". That
   sentence ships in every ADD workspace; crediting it scores an arm for the
   contents of its installer.

   Artifacts are now the documents the agent WROTE, per its own tool calls —
   arm-neutral by construction, since it asks what the run produced rather than
   where a method files things. Fails CLOSED: no transcript means no artifacts,
   never "read everything", because reading everything is what caused this.

D3 is the original `artifacts = ()` bug wearing the opposite sign: that one read
none of the agent's documents, this one read everything EXCEPT them. Both hid in
the same place — tests exercised classify() directly and never the seam that
chooses what to feed it.

RE-SCORED, corrected detector:

    add (baseline)   0.143   [0.143, 0.143, 0.143]   unchanged
    spec-kit         0.095   [0.143, 0.143, 0.0]     unchanged
    add-enumerate    0.143   [0.143]                 was 0.429

The experiment's apparent lift is gone. Every surviving verdict rests on a
readable sentence that names the contradiction.

Two tests from the earlier artifact fix are AMENDED, not weakened: they now
record the Write in the transcript, while the payload still omits the
recognition — the realistic shape, since an Edit records a slice and the file on
disk ends up saying what no single payload did. Each still passes only by
reading the file.

benchmark/tests: 425 -> 435 passing.

author: Tin Dang
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