Repository navigation
refactor(app): collapse the mode-launch recipes - #11
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Candidate #3 from the architecture review: collapse the 10 mode-launch recipes in
app.pyinto one recipe. A new deep modulebackend/launch.py(LaunchSpec= data + small callbacks, consumed by a singleLauncher.launch(); the seam is theLaunchHostadapter app.py supplies) owns the launch recipe and the finish/toast/status derivation in one place. JobBoard (jobs.py, #7) still owns job state — the host's begin/finish are_begin_job/_finish_job; WorkerLifecycle (worker.py, #1) still owns start/stop/outcome()in each adapter.docs/architecture.mdnames the new module.Census note: on fresh main (f8f80cd, app.py 479–711) it is 5
_exit_handlermodes + 4 hand-builton_doneclosures + 1 scan-event closure = 10 finish derivations (the review said 6_exit_handleruses).10-mode inventory (identical skeleton vs per-mode ingredients)
Identical for all 10 (already in
_begin_job): device gate frommode_spec().uses_device(Cannot start: HackRF not detected.), stop-previous (_stop_all_locked), identity,make -> worker.start()(failure: halt +Could not start <death_label.lower()>),JobBoard.publish,_emit_status. Finish skeleton now lives once inlaunch.py._state["sweep_config"]=cfgin buildSweepStreamer(cfg, on_sweep, on_exit)Sweep stopped unexpectedly.+ status_state["decode_config"]=cfgRtl433Decoder(cfg, on_event, on_exit)geo=_receiver_geotag()pre-launch;_state["capture_config"]IqCapture(cfg, on_progress, on_done, geolocation=geo)capture_started {record}capture_done {record, reason}→ captures list → status if cleared; completedCapture done: <name> (<MB> MB)info / cancelled warn / died errorCapture failed:only if device present_state["adsb_config"]AdsbReceiver(cfg, on_update, on_exit)_state["radio_config"]RadioReceiver(cfg, on_audio, on_exit, on_signal)radio_started {sample_rate, freq_mhz, demod}No marked frequencies to scan.(board untouched); no_statewriteRadioScanner(cfg, on_audio, on_event, on_exit)scan_radio_started {count, sample_rate, demod}capture_basename→Invalid capture name;find_capture/exists →Capture not found; centre/rate from catalogue entry (#2)IqReplay(path, centre, sr, on_frame, on_done)replay_started {name, freq_hz, sample_rate}replay_done {reason, name}→ status if cleared, no toast.iq→Invalid capture; not found; demod coerced tofmIqAudioPlayer(path, sr, demod, offset, on_audio, on_done)radio_startedtheniq_play_startediq_play_done→ status if cleared.iq→Invalid capture; not foundIqFileDecoder(path, sr, centre, on_event, on_done)decode_file_started+ toastDecoding <name> (this can take a moment)decode_file_done→ completed-only toastDecode finished: <name>→ status if cleared_state["scan_config"]AutoScanner(cfg, on_event)scan_startedvia event)scan_completed/scan_failedfinish job → status if clearedError paths (invalid name/not found/bad demod/empty scan) stay in the thin
_start_*entry points before a spec is launched — unchanged text and order.Design
LaunchSpec(kind, build(ctx), finish, started)—buildreceivesLaunchContext(the identity-wiredon_exit/on_done/on_eventto pass to the worker constructor),finishis aFinishSpec(exit_finish()/done_finish(event, payload, toast_for, toast_before_event, refresh_captures)/scan_finish()),startedreturns ordered("emit", …)/("toast", …)actions.Launcher.launch()is the only path: hostbegin_job→ started actions. All 10_start_*in app.py are now thin spec builders (_launcher.launch(×10, asserted by test; no_exit_handler, no hand-builton_done/on_scan_eventclosures remain; dead_emit_scanwrapper removed).Behaviour parity
Socket event names, status/toast copy, payloads, ordering, and job outcomes are byte-identical, pinned by
tests/test_mode_launch.py(fake workers + socket recorder across all 10 modes: publish/start, started payloads, exit/done/scan finish, guards). Characterization tests were written and passed against the pre-refactor code first (6/6), then the single-recipe tests failed first (nobackend.launch, 0 launches), then passed.Tests + mutant
tests/test_mode_launch.py— 9 tests (above + a test-only mode spec launching through the same path with no new recipe code, and a shared done-derivation across two modes).completed↔diedin the shared_finish_donederivation reddenedtest_done_modes_finish_derivation+test_shared_done_finish_derivation_across_modes(capture toast/payload + shared modes) — 7/9; after restore 9/9.tests/test_*.pyviauv run pythonpassed,check_html_templates.pyclean,check_architecture_names.pyclean,node --checkon all frontend JS clean.Self-review (two axes)
launch.py's docstring; architecture-names doc updated; no new config/auth behaviour. No hard findings (removed the dead_emit_scanwrapper found in review).