Skip to content

fix(preflight): break retry loops and give usable hints (#340, #334, #347, #305, #304) - #356

Merged
MattGyverLee merged 9 commits into
mainfrom
fix/preflight-recovery-dx
Oct 2, 2026
Merged

MattGyverLee merged 9 commits into
mainfrom
fix/preflight-recovery-dx

Conversation

@MattGyverLee

Copy link
Copy Markdown
Owner

Fixes five preflight/recovery problems that sent models (especially weaker ones) into retry loops or left them without a usable hint.

Closes #340
Closes #334
Closes #347
Closes #305
Closes #304

Changes

Merge with main

Merged origin/main (#353 recipes, #354 write gate). Conflicts were in execution.py (_attach_assistance_if_loop), session.py and CHANGELOG.md; both sides were kept. The branch's own #334 recipe helper was dropped in favor of #335's _closest_recipes_for_failure, which already covers partial_module_structure and leaves out the recipe the code came from. Before that, a reject carried two recipe pointers; a test now asserts one.

Pattern audit

Tests

  • Full offline suite (-m "not requires_flex"): 5090 passed, 4 skipped.
  • scripts/validate_integrity.py server clean; pre-commit clean.
  • An independent adversarial review ran on the branch, and its findings were fixed (the bare-snippet binding check and the identical-resubmit false-positive commits).

Live verification

None. These are offline preflight and response-shape changes; no FLEx project was opened.

Merge interactions

#343 (exclusive-access gate, draft) also adds an error code and edits execution.py. Whichever lands second needs the code count (48 here) and goldens reconciled.

🤖 Generated with Claude Code

MattGyverLee and others added 9 commits October 2, 2026 12:10
The runner's except-branch after exec(MODULE_CODE) set only
result["error"], dropping everything the script reported before the
exception. Copy messages and summary on that path too, mirroring
run_scan's error path.

closes #347

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The write branch of the zero-discovery gate inlined get_object_api docs
for the detected entities and told the model to resubmit, but never
recorded them, so the identical resubmit took the same branch again.
Record the inlined entities the way get_object_api does (methods and
properties into discovered_apis, the entity into validated_apis) and
make the copy name them. The read-only redirect grants the entities it
inlines on the auto-discovery set only (write isolation, #47).

closes #340

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Detect a byte-identical resubmit of just-rejected code (sha256 snapshot
  per submission) and attach _assistance on the first repeat, for every
  gate; the 5-in-a-row detector never fired on the 2-4 long streaks.
- operations.jsonl assistance_triggered now previews what the response
  carries instead of being hardcoded False.
- Lead the rejection with a mechanical bare-snippet auto_fixed_code
  (def Main line named, body dedented, lone scaffold piece removed) when
  safe; otherwise say why and keep skip_module_check first.
- With a user_intent, point at the closest recipes via rank_recipes.

closes #334

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Read the installed flexicon statically (find_spec + ast, never imported;
index fallback judging only Operations-shaped names; fail open) and
reject 'from flexicon import X' / 'import flexicon.X' when X does not
exist, with did-you-mean on the Operations stem (WfiWordformOperations
-> WordformOperations) and access_path for FLExProject accessors
imported as classes. A runtime flexicon ImportError gets the same
candidates (UnknownImportError). New error code: detail model, golden,
TOOL-CONTRACT row, count 47 -> 48; validate_only and the eval preflight
runner carry the gate too.

closes #305

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The second hand-maintained union count (tests/test_parser_error_models.py)
also tracks the code count; #305 moved it from 47 to 48.

Refs #305

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ccessor

- _accessor_to_ops_map resolves documented import aliases to the
  indexed class (Wordforms -> WfiWordformOperations -> WordformOperations),
  closing the gap PR #342 called out: project.Wordforms typos are now
  checked. check_doc_snippets.py uses the same resolver.
- invalid_api_chain always flags project.<Name>Operations: exact index
  mapping -> did_you_mean the accessor at ratio 1.0, else nearest
  accessors below the auto-fix threshold, still blocking.
- missing_imports no longer suggests importing a name only used as
  project.<Name>.
- search_by_capability / get_object_api derive access_path when the
  index lacks it; the generator records the real class behind an
  aliased facade import.

closes #304

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Byte-identical code can legitimately pass on resubmit: #340 records the
inlined discovery, and skip_module_check lifts partial_module_structure.
The 'same answer' message was then false. Fire only when the new error
code equals the previous rejection's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Inlining Main's body runs it where def Main stood, before the rest of the
module loads. Decline auto_fixed_code when the body reads a name bound only
after Main (NameError, possibly after earlier writes), reads a scaffold name
the fix deletes (docs), or holds a multi-line string the dedent would
rewrite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in #353 (recipe surfacing, #335) and #354 (write-gate fixes).

Conflicts resolved keeping both sides:
- execution.py _attach_assistance_if_loop: #334's record_failure_and_detect
  (identical-resubmit detection) plus #335's closest-recipe pointer.
- session.py: #334 identical-resubmit fields/methods plus #335's
  intent_failure_streak; reset_op_signals clears both.
- CHANGELOG.md: both sets of Unreleased entries.

Dropped the branch's own #334 _closest_recipes_step: #335's
_closest_recipes_for_failure already covers partial_module_structure (and
skips the recipe the code came from), so a reject got two recipe pointers.
The #334 test now asserts exactly one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@MattGyverLee
MattGyverLee merged commit cbd2050 into main Oct 2, 2026
2 checks passed
@MattGyverLee
MattGyverLee deleted the fix/preflight-recovery-dx branch October 2, 2026 18:24
MattGyverLee added a commit that referenced this pull request Oct 2, 2026
Brings in #354 (write-gate fail-opens), #353 (surface recipes), #355 (docs), #356 (preflight retry loops).

Conflict resolution keeps both sides: unknown_import (#305) and requires_exclusive_access sit side by side and the error-code count moves to 49 everywhere (response_models, both count tests, TOOL-CONTRACT, CHANGELOG). execution.py keeps main's #334 assistance-log ordering with the gate's requires_exclusive_access key. test_issue55 ladder test stubs the exclusive-access detector so it still exercises Rung 3 (gate covered in test_exclusive_access_gate.py). Full offline suite: 5273 passed, 4 skipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment