Repository navigation
Phase 2, theme 3: async ChoicesFrom lookups and worker ownership (ASY-01..08), Tools compatibility at load, conformance tests - #32
Conversation
…t load, conformance tests LcLookups: every lookup is identified by (form generation, app, tool, parameter, dependencies, request id) and its answer is applied only if all of it still holds; a dependency change invalidates at once; one lookup in flight, coalesced questions, at most 8 waiting, a time limit. LcWorkers: one owner for the workers of a window; a job leases a worker and uses only that one; a lookup never uses or replaces a run's worker; closing leaves no worker. LcCompat runs when the registry is read. New bodies read conformance.json (limits, messages, codes, compat vectors, done status, a ledger of every fixture id) and run ASY-01..08. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
…on its own A reply that differs from the current question in only the form generation, the app, the tool, the request id, the dependencies or the parameter is dropped; the same reply with all of them current is applied. (Found by sabotage: dropping the generation check alone was not caught, the other checks covered it in every fixture.) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
…qupath-async-compat Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
…ependency changes Found by sabotage: removing the visible part of the pending state (the dropdown cleared, the text field with its tooltip shown) was not caught, because in the delayed-source fixtures the dropdown had never been filled. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
Sabotage checksEach row: production code broken in a copy of the final tree of this PR (a regular expression replaced one place), the PR's own body run on it (
"aborted with an exception" means the body died on a missing marker or on the stress threads (the exception fails the body). Generated by Claude Code |
… run-time refusal is checked on an app that is in the list anyway With the compatibility check in the registry load, the three old-Tools apps of the body are skipped when the window rescans (the status says why); the run-time check stays as the last line and is exercised by putting such an app into the window's list. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
Theme 3 of the wrong-science wave for QuPath: asynchronous ChoicesFrom lookups and who owns the worker (SPEC_WRONG_SCIENCE section 5, ASY-01..08), a Tools compatibility check when the registry is read, and the conformance tests that read
conformance.json(docs/CONFORMANCE.md says QuPath hastests/gui_test_conformance_body.groovy; it did not exist). Reference: Napari #54.Fixes #19.
This PR is based on PR #31 (
claude/phase2-qupath-number-limits), which is based on PR #30 (claude/phase2-qupath-retention): it needs theparts.txtsplit, therunstest app and the newrun_gui_ci.shentries of those two. Merge #30 as a merge commit, retarget #31 to main and merge it the same way, then retarget this one.What changes
Async lookups and worker ownership (issue #19)
Before:
resolveChoicesstarted one thread per ChoicesFrom parameter; each ranensureWorker(read and overwrite the sharedworker/workerAppfields, no lock) andworker.run; a run did the same from its own thread;choiceSeqwas reset per form, so a late answer of tool A with number 1 could fill the dropdown of tool B (also number 1); and a lookup could be running on the worker a run was starting on.LcLookups(LcLookups.groovy): every question is(form generation, app, tool, parameter, the values it depends on, request id); the generation is process-wide and never reset (it grows when a form is built or destroyed), the request id is per window and never reset. An answer is applied only if all of it still holds when it arrives (the dependencies are recomputed then); otherwise it is dropped whole, with its error and tooltip. A dependency change invalidates at once (the dropdown goes back to the text field with the tooltip "waiting for the choices...") and only the new question waits for the 400 ms pause. One lookup in flight per window on one thread; waiting questions are coalesced (one slot per parameter); at most 8 wait (the oldest is dropped and logged); a lookup past 60 s is ended with its worker (-Dlc.qupath.lookup_timeout_sshortens it for tests, said in the status asdid not answer within N s).LcWorkers(LcWorkers.groovy): the one owner of the workers of a window, under one lock. A run or a lookup leases a worker when it is submitted and uses only that one. A lookup never uses the worker a run holds and never starts while a run is active (it asks again after 1.5 s); a run waits for a lookup that holds its worker. An app switch during a run does not replace the run's worker (the new app's worker is made for the next job; the old one is closed when its last job ends). A crashed or timed-out worker is discarded. Closing the window drops the waiting lookups, ends the one in flight, stops the thread and closes every worker (also one that was being made at that moment).LcWorkerimplementsLcWorkerLike, counts the processes it starts and takes a time limit;dlg.worker/dlg.workerAppare now read-only views of the controller; Cancel goes to the worker of the run (the run's lease).Compatibility when the registry is read
LcCompat(introduced in PR #30 for the run-time refusal) now also runs inLcRegistry.load(): an app whose Tools is older than 0.3.0a1, or lacksreserved_inputs_v1/retention_prune, or has no version, is not offered and is listed under "skipped" with one sentence (... is older than the 0.3.0a1 this host needs: update it); an unparsable version is offered with a warning in the status line. README has a "Tools compatibility" section.Conformance tests
gui_test_conformance_body.groovy(115 checks): validates the file with Tools'--validate; compares every limit, message template and host-internal code of the file with this script's constants (an unknown key fails: a new rule is a failing test until the host follows it); runs all 7compatvectors and the version order throughLcCompat; runs the done-status rule through the real Tools helper for each way a run can end; and keeps a ledger of every fixture id of the file (83) mapped to the body that runs it or to the reason it does not apply (printed asUNSUPPORTED, never passed): CF-02..06, CF-13 (no transform), RET-01..09, 12, 13 (Tools' own), RET-14, RUN-05.gui_test_async_body.groovy(64 checks): ASY-01..08 read from conformance.json against gated source tools of therunstest app (the dependency of the dropdown is the gate path, so the test controls both the delay and the dependency): see README. ASY-08 also runs three threads over fake workers at a fixed interleaving (a barrier hook) and at random.Existing tests changed (spec supersedes them)
gui_test_area_body.groovy:fx { dlg.worker = null }(it assigned the field) isdlg.stopWorker(true).gui_test_parity_trust_body.groovy: its good schema now saystools_versionandfeatures(an app with an older Tools is not offered); new cases for the compatibility rules.gui_test_retention_body.groovy(from PR Phase 2, theme 1: retention markers + prune subprocess + Tools run record, and run identity (RUN-01..07) #30): its Tools-too-old block registered three apps with an old Tools and ran them; they are now not offered when the window rescans (the body checks the status text and that they are not indlg.apps), and the run-time refusal is checked by putting such an app into the window's list.tests/runs_appgained the gated source tools;tests/run_gui_ci.shlists the two new bodies.Nothing was skipped or loosened.
Sabotage checks
(see the comment on this PR)
Spec gaps and conservative choices
status: defaultin conformance.json.compatresult "warn" only appears for a version text that is not understood (a missing version is a refusal here because this host sets a minimum).The other open issues
placePointsstill reads points withreadLines+splitCsv, so a quoted newline in a points file still breaks. Open.shellQuotealready follows the Tools vectors (gui_test_parity_quote_body.groovy), so it looks fixed on main; I did not verify on Windows and left the issue for the maintainers.gui_test_parity_trust_body.groovy, 26 checks). Left open for the maintainers.Not verified
ProcessHandle, the file locks of Tools' prune); no human at a display (private Xvfb, real QuPath 0.7.0 / Java 25). Timing: the retry after a run is 1.5 s and the tests wait for it; on a very slow machine a test could time out, not fail wrongly.🤖 Generated with Claude Code
https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
Generated by Claude Code