File size: 7,594 Bytes
0ffbb74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# BeamPilot evaluation plan v3: inaccessible waist with change_lens



Status: pre-registered on 31 Oct 2026, **before any run on the v3 seeds 900–907**.

- **Code:** branch `add-change-lens` at `2a7dfd5` (not merged into main).

- **Runner:** `scripts/run_evaluation_v3.py`.

- **Cases and oracle:** `scripts/v3_cases.py`.
- **No code changes between this commit and the runs.** If something breaks, the
  evaluation stops and is reported; no case is re-run.
- **v1 and v2 stay unchanged.** Later changes go under "Deviations" at the end.

Design: [change-lens-design.md](change-lens-design.md). Progress, margin table and
open issues: [change-lens-progress.md](change-lens-progress.md).

## Question

When the waist lies outside the stage, does the agent use a thin lens to form an
artificial waist, measure it to ISO 11146-1, and give back the original beam (d0,
z0) and M² within tolerance? And does it say `waist_not_accessible` honestly when
no kit lens can help? It is compared with a fair scripted operator that has a
fixed lens rule.

## Contenders

| Name | What it is |
|---|---|
| `agent` | `run_agent`, `muse-spark-1.2`, effort `low`, limits 60 model calls, 1.5 M tokens, $0.50 per run; `AgentConfig(change_lens=True)`, `view_frame` on |
| `scripted` | `scripted_iso` with the lens rule (design section 6): first listed lens option, then scouts from the prediction (at least 4 positions) and its normal fill |
| `naive` | `naive_uniform`, reference only (never uses a lens) |

Every case has the lens kit (+25, +40, +300, +750, −200 mm; rail −300…−20 mm in
10 mm steps) and **28 positions**, for every contender. One run per case.

## Cases (seeds fixed now)

| Case | Seed | Waist z0 (mm) | d0 (µm) | M² | Expected (oracle) | Lens margin |
|---|---|---|---|---|---|---|
| L1 | 900 | −30 | 200 | 1.2 | iso_compliant | **borderline** (1 lens; best works at 12/27 corners) |

| L2 | 901 | −60 | 200 | 1.2 | iso_compliant | ok (2 lenses; 27/27) |
| L3 | 902 | −100 | 250 | 1.4 | iso_compliant | ok (5; 27/27) |

| L4 | 903 | −80 | 160 | 1.0 | iso_compliant | **borderline** (6; best 23/27) |
| L5 | 904 | +280 | 200 | 1.2 | iso_compliant | ok (29; 27/27) |

| L6 | 905 | +320 | 300 | 1.6 | iso_compliant | ok (46; 27/27) |
| L7 | 906 | +380 | 250 | 1.4 | iso_compliant | ok (29; 27/27) |

| L8 | 907 | +260 | 130 | 1.0 | waist_not_accessible | no lens at any of the 27 corners |



Both axes have the same d0, z0 and M². Wavelength 1064 nm; the camera and stage are

the same as in v1 and v2.



**How each column was set:**

- **Expected verdict:** the oracle below, applied to the true beam.

- **Lens margin:** the best feasible lens for the true beam, checked at the 27

  corners z0 ±10 mm, d0 ±10 %, M² ±10 %.

- **L8:** chosen by a recorded search (`choose_l8`):
  - grid: waist −150…−10 mm and 260…500 mm, d0 100…350 µm, M² 1.0…1.8;
  - at least 10 usable stage positions;
  - no feasible lens for the true beam or for any of the 27 corners;
  - the most usable positions wins (25); 87 qualifying candidates.

**Oracle with lenses** (`scripts/v3_cases.py`):
1. `measurement_failed` if fewer than 5 stage positions (5 mm grid) give a usable
   beam size: at least 20 px across and at most half the smaller sensor side.
2. Waist inside the stage: the v1 rule (room for 5 near and 5 far points per
   axis).
3. Waist outside: `iso_compliant` if any kit × rail lens gives an artificial
   waist that passes that rule on both axes (inside the stage, ≥ 20 px, beam
   ≤ 8 mm at the lens); otherwise `waist_not_accessible`.

## Metrics

- **Correct verdict:** equals the oracle's expected verdict.
- **Within tolerance** (cases expected `iso_compliant`), on both axes:
  - |M² error| ≤ 3 %;
  - back-calculated original d0 within 5 %;
  - back-calculated z0 within 0.1 × the true zR.
- **Reported, not scored:**
  - the lens chosen and whether it was a listed option;
  - lens changes;
  - positions;
  - model calls, views and cost;
  - the "dz0 per +1 % f" sensitivity.

## Calibration and development runs (none on 900–907)

**Scripted calibration, seeds 950–957** (L1 → 950 … L8 → 957), 28 positions:

| Case | Lens | Positions | Verdict | Worst error d0 / z0 / M² | dz0 per +1 % f |
|---|---|---|---|---|---|
| L1 | −200 @ −110 | 24 | iso_compliant | 0.1 % / 0.004 zR / 0.1 % | +0.29 mm |

| L2 | +25 @ −20 | 19 | iso_compliant | 0.2 % / 0.002 zR / 0.3 % | −0.40 mm |
| L3 | +40 @ −40 | 19 | iso_compliant | 0.3 % / 0.000 zR / 0.5 % | −0.62 mm |

| L4 | +40 @ −20 | 19 | iso_compliant | 0.1 % / 0.003 zR / 0.5 % | −0.81 mm |
| L5 | +750 @ −20 | 26 | iso_compliant | 0.2 % / 0.005 zR / 0.2 % | −1.18 mm |

| L6 | +750 @ −20 | 24 | iso_compliant | 0.3 % / 0.001 zR / 0.4 % | −1.50 mm |
| L7 | +750 @ −50 | 23 | iso_compliant | 0.2 % / 0.002 zR / 0.5 % | −2.41 mm |

| L8 | none | 13 | waist_not_accessible | – | – |



- **Spread over the 7 lens cases:** d0 ≤ 0.3 %, z0 ≤ 0.005 zR, M² ≤ 0.5 %, well

  inside the tolerances.

- **Development dry run (seeds 60–75, two per case):** 16 of 16 runs reached the

  expected verdict, with worst errors d0 0.6 %, z0 0.012 zR, M² 1.0 %.



**One live agent smoke run** (`waist_outside_range`, waist −50 mm, seed 920, 28

positions; not a v3 case):

- **Outcome:** `iso_compliant`, method `artificial_waist`.

- **Lens:** +25 mm at −20 mm, the listed option with fraction 1.0.

- **Accuracy:** back-calculated d0 +0.2 %, z0 +0.002 zR, M² +0.3 % on both axes.

- **Effort and cost:** 41 model calls, 21 positions, $0.133.



## Budget and stopping



- **Estimate:** 8 agent runs at about $0.10–0.15 → about $1.

- **Hard stop at $2** for the agent runs; the remaining cases are then reported as

  not run.

- **Order:** scripted and naive for all cases first (no API), then the agent L1 to

  L8, one at a time.



## Known limitations



- **Small N.** One run per case, so a difference of one or two cases is noise.

- **Not held out.** The cases, the kit, the scripted lens rule and the agent's lens

  prompt were developed together, with dry runs on development seeds. v3 measures

  whether the feature works end to end, not generalisation to unseen faults.

- **Fixes during development.** After the first dry run L8 was re-selected twice

  and the scripted scout rule was fixed; no fix was aimed at a single case (see

  the progress note).

- **Borderline cases.** L1 and L4 are borderline: their best lens fails at some of

  the ±10 % corners. L8 lies 10 mm past the stage end (about 0.8 zR).

- **Thin-lens model, exact f.** Real lenses (±1 % focal length) would add the

  listed "per +1 % f" error to z0. The wording stays "ISO 11146-1 procedure

  applied at an artificial waist (thin-lens model)", never "ISO compliant for the

  original beam" (design section 10).



## Deviations



1. **31 Oct 2026, before any v3 run: higher agent limits and budget.**

   - **Reason:** the smoke run used 41 of 60 model calls for 21 positions, and

     L5–L7 need 23–26 positions, so the 60-call limit would probably decide some

     runs (as A8 and C2 in v1).

   - **For v3 only:** the agent limits are **80 model calls** and **3 M tokens**;

     the per-run cost cap stays at **$0.50**.

   - **The total hard stop rises from $2 to $3.**

   - **Change:** only the settings in `scripts/run_evaluation_v3.py`

     (`AGENT_CONFIG`, `BUDGET_USD`); no other code changed. Commit `58e1b29`.

   - **The v3 runs use the code at `58e1b29`.**