Comet Analyzer formula transactions and interactive gate

R179 completes the R177–R179 formula transaction work: one canonical parser, an explicit draft/commit model for manual editing, a separate atomic persistence operation and a real-PyQt5 qualification gate.

Qualification: RC / PASS_HEADLESS. R179 deterministic checks passed 13/13; the strict offscreen and native PyQt5 executions remain pending.

Formula state model

The production Analyzer separates editor drafts from committed formula state:

manual Regular/Additional editing
→ draft text only
→ explicit Refresh validates and atomically commits both documents

Analyse
→ typed fit result
→ immediate atomic commit
→ one implicit Refresh/render

Keystrokes never mutate the committed model, formula revision, rendered graph or persistent files. Intermediate text such as i, in, -, 1e or an unfinished interval is therefore safe while the user is typing.

Manual edit and Refresh transaction

  1. Editor signals store only the current Regular and Additional draft text.
  2. Refresh invokes the canonical typed document parser for both documents.
  3. Validation completes before any committed state changes.
  4. A valid pair is committed as one formula transaction, advances the formula revision exactly once and starts one plot update.
  5. An invalid pair leaves the previous committed formulas, formula revision and graph unchanged.

Formula validation errors use formula-specific titles and messages rather than the generic Plot rendering error.

Save transaction

Save retains the R149 same-directory temporary-file, flush, fsync and single os.replace() persistence boundary for config-files/MagParams.dat.

  • An invalid Save leaves the existing file byte-identical.
  • A valid Save writes the valid draft atomically.
  • Save does not apply that draft to the committed graph and does not advance the runtime formula revision.
  • The user applies manual draft changes through a separate valid Refresh.

This separation prevents file persistence from becoming an implicit GUI/model commit.

Analyse transaction

Analyse is intentionally not draft-only. Its typed fit result is committed immediately and one plot update is started as an implicit Refresh/render. It must not require a literal legacy button_click_update() dispatcher call.

The typed result remains inside the canonical R176+ Analyzer runtime and retains the existing transactional graph-preservation guarantees on failure.

Canonical parser and compatibility isolation

  • wlightcurves.py::_analysis_parse_formula_text() is removed.
  • The compatibility method ParseFormulas() delegates directly to the canonical typed document/state parser and remains side-effect free.
  • Production wlightcurves.py contains no call to koprobs.magparams().
  • light_curve_runtime.prepare_multi_target_curve_request() is compatibility-only and is not used by the typed Analyzer runtime.
  • Its legacy row conversion no longer imports or calls koprobs.magparams().
  • The converter retained in koprobs.py is compatibility-only for historical widgets or external callers.

Error and iterator-context contract

Expected or exceptional iterator exhaustion must retain the operation and target context. No production Analyzer path may print an unqualified bare StopIteration(). Invalid Refresh and invalid Save are handled as formula-transaction failures, not rendering failures.

Interactive qualification gate

R179 adds r179_formula_qt_smoke.py, r179_formula_interactive_release_gate.py and tests/test_r179_formula_interactive_qualification.py.

The real runner covers character-by-character i → in → inf input, no mutation during typing, one Refresh commit, invalid Refresh preservation, byte-identical invalid Save, corrected Save/reload and Analyse immediate commit.

QT_QPA_PLATFORM=offscreen python3 r179_formula_interactive_release_gate.py --require-qt

python3 r179_formula_interactive_release_gate.py --require-qt --native

The strict command must fail when PyQt5 is unavailable; a headless deterministic pass alone is not a full GUI qualification.

Source-reported qualification evidence

  • R177: 9/9 PASS.
  • R178: 9/9 PASS.
  • R179 headless: 13/13 PASS.
  • R179 real Qt: SKIP because PyQt5 was unavailable.
  • R177–R179 targeted: 24 passed, 1 skipped.
  • R149–R179 current regression: 236 passed, 3 skipped.
  • CLEAN syntax: 107 files / 0 errors; QA syntax: 425 files / 0 errors.
  • CLEAN manifest: 116 entries; QA manifest: 482 entries; aggregate validator and ZIP integrity: PASS.

The three skipped tests are the real PyQt5 gates from R154, R161 and R179.

Historical source-assertion boundary

Eight R141–R148 source-assertion tests require implementation details removed by the typed runtime, including obs_list, the old formula dispatcher and a literal self.button_click_update() call after Analyse. They contradict the R176–R179 architecture and are not valid R179 release evidence. Current behavioral gates are authoritative.

Formula document and persistence foundation · WLightCurves architecture · Supplied R179 report