Skip to content

Commit 06d1b10

Browse files
authored
Merge pull request #292 from PyAutoLabs/claude/test-mode-bypass-assertion-ties-zl8yv6
mind: test-mode-bypass-assertion-ties close-out + three CTI Phase 5 prompts
2 parents 5f22c49 + 84876af commit 06d1b10

9 files changed

Lines changed: 526 additions & 146 deletions
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
## test-mode-bypass-assertion-ties
2+
- issue: https://github.com/PyAutoLabs/PyAutoFit/issues/1519 (closed)
3+
- completed: 2026-08-24
4+
- library-pr: https://github.com/PyAutoLabs/PyAutoFit/pull/1520 (merged 438f56fac)
5+
- summary: The PYAUTO_TEST_MODE=2/3 bypass evaluated the model at the prior
6+
medians, so a model with identical priors plus an ordering assertion (the
7+
exchange-degeneracy idiom, e.g. PyAutoCTI trap models) tied exactly there and
8+
check_assertions hard-failed the run. The bypass now picks its point through a
9+
shared `_test_mode_valid_parameter_vector` — prior medians first, then
10+
`default_rng(seed=0)` prior draws, each candidate validated — so the vector it
11+
evaluates AND stores satisfies the assertions.
12+
- key-finding: **the prompt's own suggested fix would not have worked.** The
13+
2026-08-09 note had narrowed this to a one-liner (move `instance_from_vector`
14+
inside the existing try, or pass `ignore_assertions=True`). Reading main
15+
d3625a8 found THREE sites, and both one-liners fix only the first:
16+
1. `abstract_search.py:1007` — instantiation outside the FitException guard;
17+
2. `_build_fake_samples:1112` — the perturbed samples are the median vector
18+
scaled UNIFORMLY (1.001/0.999/1.002), and a uniform scale preserves an
19+
ordering tie, so every stored sample fails the same assertion;
20+
3. `SamplesSummary.max_log_likelihood` (`interface.py:122`) is `@to_instance()`
21+
with `recover="raise"` — it extends SamplesInterface directly so it does NOT
22+
inherit Samples' next-valid recovery — so `result.max_log_likelihood_instance`
23+
raises SamplesException, and `Result.instance` catches only AttributeError.
24+
- key-finding: **TEST_MODE=3 was broken too and nobody had noticed.** It never
25+
calls instance_from_vector in the bypass, so it survived the fit and died at
26+
the first `result.max_log_likelihood_instance`. Confirmed by reproduction, not
27+
just by reading. Fixing the stored vector fixes modes 2 and 3 together.
28+
- trap: do NOT re-run a bypass reproduction without clearing `output/` first. A
29+
bypassed fit calls `paths.completed()`, so a second run with the same
30+
unique_tag takes `result_via_completed_fit` and replays the OLD (broken)
31+
samples — which reads exactly like "the fix didn't work". Cost one false
32+
negative during verification.
33+
- trap: `af.m.MockAnalysis` maps its likelihood over the model and returns a
34+
LIST for an `af.Collection`, which the bypass's `float()` rejects. Regression
35+
tests needed a small float-returning analysis instead.
36+
- behaviour-change: mode 3 now instantiates the model once (previously zero
37+
times). A model whose constructor raises a non-FitException at the medians now
38+
fails at fit time rather than result time — same failure, surfaced earlier.
39+
Flagged in the PR body for downstream repos.
40+
- verification: reproduced on clean main first (mode 2 raised FitException in the
41+
fit; mode 3 raised SamplesException at result access), then both modes complete
42+
and select the identical vector after. All 5 new tests in
43+
`TestBypassToleratesAssertionTies` fail against the un-patched source. Full
44+
suite 2016 passed / 34 skipped / 0 failed; CI green on all three legs
45+
(unittest 3.12, unittest 3.13, unittest-nojax) plus Docs.
46+
- gate-caveat: shipped from a web-github session where `pyauto-heart` is
47+
unreachable, so the readiness gate ran in the WORKFLOW.md fallback form (full
48+
library suite as the gate). No Heart verdict was recorded for this task; CI
49+
green at merge is the stronger confirmation that stands in its place. The
50+
workspace-impact grep was likewise not run (workspace clones absent) — API
51+
Changes are "none, internal", so option (iii) was inferred, not measured.
52+
- follow-up: `autocti_workspace` documents this artifact in its AGENTS.md as a
53+
workaround. Delete that note now the fix has shipped — the testmode-env-drift
54+
precedent ("delete the trap, don't document it"). Separate repo, separate task.
55+
- follow-up: re-enable autocti_workspace smoke coverage of the
56+
`modeling/start_here.py`-class scripts (CTI epic Phase 5) that this unblocks.
57+
- environment: web-github; no worktree was ever created, so there is none to
58+
remove. PyAutoFit was worked in a session clone at /home/user/pyautofit.
59+
60+
## Original prompt
61+
62+
# TEST_MODE bypass crashes on ordered-parameter assertion ties
63+
64+
Type: bug
65+
Target: PyAutoFit
66+
Repos:
67+
- @PyAutoFit
68+
Difficulty: small
69+
Autonomy: supervised
70+
Priority: normal
71+
Status: formalised — STILL REPRODUCES; see the 2026-08-09 note before grading this against main
72+
Filed: 2026-07-17 (backfilled from git)
73+
Issued: 2026-08-24
74+
75+
## 2026-08-09 — do NOT mistake the adjacent FitException catch for this fix
76+
77+
Checked by the draft/ sweep against PyAutoFit main (`3b960609`). The bypass path
78+
in `abstract_search.py` **now catches `exc.FitException`** and continues with the
79+
`-1e99` sentinel, logging "TEST MODE 2: likelihood verification raised
80+
FitException … treating as a resample-rejected instance". That reads exactly like
81+
this prompt's suggested fix. **It is not.** The bug below still reproduces.
82+
83+
The catch wraps only the likelihood call. The model instantiation is on the line
84+
*before* the `try`:
85+
86+
```python
87+
if call_likelihood:
88+
instance = model.instance_from_vector(vector=parameter_vector) # <-- outside
89+
try:
90+
log_likelihood = float(analysis.log_likelihood_function(instance))
91+
except exc.FitException as e:
92+
...
93+
```
94+
95+
and `instance_from_vector``instance_for_arguments``check_assertions`
96+
(`autofit/mapper/prior_model/abstract.py:193`) is precisely what raises
97+
`exc.FitException("N assertions failed!")` when an ordering assertion ties at the
98+
prior medians. `ignore_assertions` defaults to `False` and the bypass does not
99+
pass it. So the assertion exception escapes the guard entirely and still
100+
hard-fails the run.
101+
102+
The upside: the fix is now a one-liner rather than the "catch and retry with a
103+
perturbation" design sketched below. Two options, both cheap and both
104+
deterministic:
105+
106+
- move the `instance_from_vector` call inside the existing `try` — the sentinel
107+
path already does the right thing for a rejected instance; or
108+
- pass `ignore_assertions=True` at the bypass instantiation, on the grounds that
109+
a verification eval at the medians is not a sampled point and assertions exist
110+
to steer sampling.
111+
112+
The second is probably the better semantics (a tied median is not a pathological
113+
model), but it changes what the verification eval attests to — pick deliberately.
114+
Prefer either over adding perturbation logic.
115+
116+
`Difficulty:` stays small. The § Blocks note below still holds.
117+
118+
---
119+
120+
Found during the CTI resurrection epic (Phase 4, 2026-07-17). `PYAUTO_TEST_MODE=2/3`
121+
bypass evaluates the model at the **prior medians**. A model whose components have
122+
identical priors plus an ordering assertion (the standard idiom for breaking
123+
exchange degeneracy, e.g. PyAutoCTI trap models with
124+
`model.add_assertion(trap_0.release_timescale < trap_1.release_timescale)`)
125+
ties exactly at the medians, so the bypass evaluation raises
126+
`autofit.exc.FitException: GreaterThanLessThanAssertion` and the script crashes.
127+
128+
Real samplers resample assertion-failing points gracefully — this is purely a
129+
bypass-path artifact, and it makes every ordered-trap CTI workspace script
130+
un-smokeable at TEST_MODE=2 (reproduced with a bare
131+
`model.instance_from_prior_medians()`; TEST_MODE=1 passes).
132+
133+
Suggested fix: at the bypass evaluation, catch `FitException` from assertions
134+
and retry with a small deterministic perturbation of the unit-cube point (or a
135+
seeded random draw), mirroring what a real sampler does. Keep it deterministic
136+
so smoke runs stay reproducible.
137+
138+
Blocks: autocti_workspace smoke coverage of `modeling/start_here.py`-class
139+
scripts (CTI epic Phase 5); the workspace documents the artifact in its
140+
AGENTS.md meanwhile.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
## testmode-assertion-note-removal
2+
- issue: https://github.com/PyAutoLabs/autocti_workspace/issues/24 (closed)
3+
- completed: 2026-08-24
4+
- workspace-pr: https://github.com/PyAutoLabs/autocti_workspace/pull/25 (merged 2933cddd)
5+
- workspace-pr: https://github.com/PyAutoLabs/autocti_workspace_test/pull/17 (merged bfdb876b)
6+
- workspace-pr: https://github.com/PyAutoLabs/autocti_assistant/pull/21 (merged 6a0f645c)
7+
- summary: Follow-up to test-mode-bypass-assertion-ties (PyAutoFit#1520, merged
8+
438f56fac). Three CTI repos documented the now-fixed TEST_MODE bypass crash as a
9+
live artifact readers must work around. Deleted per the testmode-env-drift
10+
precedent ("delete the trap, don't document it") rather than updated. All three
11+
branches proven ancestors of their main (0 unmerged commits each).
12+
- key-finding: the prompt said "if a sibling note exists" — BOTH siblings existed,
13+
and the second was not a note. In autocti_workspace_test/AGENTS.md the claim was
14+
a parenthetical RATIONALE for a convention ("integration scripts are ...
15+
single-trap (because ordered traps tie under the bypass)"). Deleting the whole
16+
bullet would have quietly repealed a test-design rule. Removed only the
17+
rationale; left the convention standing and flagged it for a maintainer.
18+
- open-question: autocti_workspace_test's single-trap convention now has no stated
19+
reason. If it existed only to dodge the bypass crash it can be dropped, and
20+
multi-trap ordered models exercised — arguably better coverage, since ordered
21+
traps are the realistic CTI case. If it also exists for runtime/simplicity the
22+
bullet needs that reason written in. Deliberately not guessed.
23+
- key-finding: the third site was in a SKILL (autocti_assistant
24+
skills/ac_fit_cti_model.md), i.e. what the assistant tells users — a stale
25+
"known artifact" there is active advice to work around a bug that no longer
26+
exists. Worse than a stale AGENTS.md note. Its .claude/skills/ copy is a
27+
SYMLINK (git mode 120000), so one edit covered both discovery surfaces; do not
28+
assume the mirrored skills dirs are copies.
29+
- deliberately-untouched: the add_assertion example at ac_fit_cti_model.md:54.
30+
The ordering-assertion API is unchanged and still the right idiom for breaking
31+
exchange degeneracy — only the bypass's handling of it was broken.
32+
- trap: autocti_assistant's `wiki-currency` check is RED ON MAIN, independently of
33+
any PR. Confirmed by dispatching it on main at 960fdd1c (run 32762029277):
34+
failed identically. Proof it is not the PR's: the drift-report artifacts are
35+
964 bytes (PR) vs 961 (main), and the report header's `assistant_ref` line
36+
differs by exactly 3 chars (refs/pull/21/merge vs refs/heads/main) — so every
37+
drift FINDING is byte-identical. Artifact byte-size diffing is a cheap way to
38+
prove two CI failures are the same failure when the log hides the detail.
39+
- trap: that workflow redirects each sub-check into drift-report.md
40+
(`>> "$REPORT" 2>&1`), so the job log NEVER names which of its five checks
41+
failed. Read the artifact; do not try to infer it from the log tail.
42+
- decision: PR#21 was merged deliberately over that known-bad base (human-
43+
authorised at /prm) rather than regenerating an API baseline inside a docs PR.
44+
The drift is filed separately as draft/bug/autocti/wiki_currency_baseline_drift.md.
45+
- gate-note: autocti_workspace has NO CI configured at all (zero workflow runs,
46+
zero checks). #25 was merged on explicit human authorisation per /prm's
47+
no-checks guard. autocti_workspace_test's smoke ran `changes` green and skipped
48+
`smoke` by path filter — correct for a docs-only diff, not a pending check.
49+
- follow-up: draft/test/autocti/phase5_smoke_reenable_ordered_trap_scripts.md —
50+
re-enable the smoke coverage the PyAutoFit fix unblocks (CTI epic Phase 5).
51+
- environment: web-github; no worktree. Clones at /home/user/autocti_{workspace,
52+
workspace_test,assistant}.
53+
54+
## Original prompt
55+
56+
# Delete the TEST_MODE ordered-assertion workaround note from @autocti_workspace AGENTS.md
57+
58+
Type: docs
59+
Target: autocti_workspace
60+
Repos:
61+
- @autocti_workspace
62+
- @autocti_workspace_test
63+
- @autocti_assistant
64+
Difficulty: small
65+
Autonomy: supervised
66+
Priority: normal
67+
Status: formalised
68+
Filed: 2026-08-24
69+
Issued: 2026-08-24
70+
71+
Re-homed from `draft/triage/` by the filing session: intake classified this
72+
`triage` / `Target: PyAutoFit` / `too-large` on low confidence, which is wrong on
73+
all three counts — the PyAutoFit fix has already shipped, so nothing here touches
74+
library source. This is a prose deletion across up to three workspace repos.
75+
76+
Delete the TEST_MODE ordered-assertion workaround note from @autocti_workspace AGENTS.md. PyAutoFit#1520 (merged 438f56fac, 2026-08-24) fixed the bypass so a model with identical priors plus an ordering assertion no longer ties at the prior medians — the bypass now picks a deterministic assertion-valid point via _test_mode_valid_parameter_vector, at TEST_MODE 2 and 3. The workspace AGENTS.md documents the crash as a live artifact and tells readers to work around it; that text is now wrong. Delete the note rather than update it — the testmode-env-drift precedent is delete the trap, do not document it. Verify the note's exact wording and location in autocti_workspace first; if a sibling note exists in autocti_workspace_test or autocti_assistant, remove those too.
77+
78+
<!-- formalised by the Intake (Conception) Agent on 2026-08-24 from user-intake -->

complete/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Token-light navigation over the finished-work records (schema:
66
only then grep a dated bucket. Curators: edit the band between the CURATED
77
markers; everything below GENERATED is rebuilt.
88

9-
1100 records across 7 buckets.
9+
1102 records across 7 buckets.
1010

1111
<!-- CURATED:START -->
1212
## Highlights
@@ -211,9 +211,11 @@ _(curate hard-won records here — survives regeneration.)_
211211
- [tenant-firewall-drift-aug](2026/08/tenant-firewall-drift-aug.md) — issue #198; 9 → OK, and a green `--check` is only evidence for the organs actually checked out
212212
- [tenant-firewall-hygiene-extras](2026/08/tenant-firewall-hygiene-extras.md) — auto-closed by the merge
213213
- [tenth-sample-hardcoded-index](2026/08/tenth-sample-hardcoded-index.md) — results/database/aggregator tutorials hardcoded stored-sample index 9 ("the tenth
214+
- [test-mode-bypass-assertion-ties](2026/08/test-mode-bypass-assertion-ties.md) — The PYAUTO_TEST_MODE=2/3 bypass evaluated the model at the prior
214215
- [test-mode-fit-exception-finalization](2026/08/test-mode-fit-exception-finalization.md)`PYAUTO_TEST_MODE=1` no longer finalizes a model point whose reconstruction raises `FitException`; it substitu…
215216
- [test-mode-samples-info-hook-contract](2026/08/test-mode-samples-info-hook-contract.md)`NonLinearSearch._test_mode_samples_info()`'s docstring told subclasses
216217
- [test-performance-board](2026/08/test-performance-board.md)
218+
- [testmode-assertion-note-removal](2026/08/testmode-assertion-note-removal.md) — Follow-up to test-mode-bypass-assertion-ties (PyAutoFit#1520, merged
217219
- [transformed-message-semantics-doc](2026/08/transformed-message-semantics-doc.md)
218220
- [undo-community-file-declutter](2026/08/undo-community-file-declutter.md) — Undid the 2026-08-19 community-file declutter (#248 Mind, #32 Memory):
219221
- [uniform-prior-bounds-numpy-path](2026/08/uniform-prior-bounds-numpy-path.md) — auto-closed by the merge's `Closes` line

0 commit comments

Comments
 (0)