PyCForge memory, idle activity, and worker reliability audit
14 October 2026 (UTC) — releases 1.2.2 and 1.1.12

RESULT

The audit found real resource leaks and process-lifetime failures. They are
corrected in both maintained branches. This is a reliability update; conversion
coverage, converter source, dependencies, semantic contracts and the HTML Help
articles remain unchanged. Earlier module-close, folding, About-label and
PCFC11 file-dialog fixes are retained. Product version surfaces match each wheel.

Use 1.2.2 for the existing 1.2.0/1.2.1 installation with optional PCFC11 builds.
Use 1.1.12 for the separate 1.1.11 line with runtime builtins, lists, formatting,
modules and connected file workflows. These are distinct branches of the same
package; install one branch per Python environment.

CONFIRMED FAILURES AND FIXES

1. Closed dialogs kept their Qt children alive. The main window parent retained
   each dismissed About and file chooser. The file chooser's QFileSystemModel
   retained a filesystem worker thread and operating-system resources. About
   clocks continued running while the dialogs were hidden.

   Temporary dialogs now schedule deletion after the modal result is consumed.
   This covers About, Python/C file choosers, discard/reload/save-close prompts,
   editor settings, command palette and go-to-line. Closed session reports and
   Ting feedback dialogs also delete. The newer branch cleans up rename prompts.
   About's clock starts on show and stops on hide. Help is intentionally reused;
   the PCFC11 branch intentionally reuses one build dialog.

2. The idle conversion supervisor woke approximately 100 times per second to
   look for work. It now waits for notification when idle. Timed waits remain
   while actual work needs process supervision or shared-budget admission.
   Measured idle coordinator cycles fell from roughly 188 per two seconds to 0.
   In 1.2.2, the PCFC11 100 ms timer runs only during a build. Workspace signals
   update source validity and build availability without hidden polling.

3. Sending a large request or reading an incomplete worker response could block
   the coordinator inside pipe I/O. The original supervisor could then fail to
   cancel/reap a child during shutdown. Fault injection reproduced both paths:
   after 2.5 seconds the coordinator, unfinished Future and child remained live.

   A single bounded transport thread now owns those pipe operations. The
   coordinator remains available to cancel/terminate/reap the child, which
   releases the blocked transfer. The frame queue holds at most one queued
   frame; protocol identity and size checks still apply. Decoding/publication
   stays with the coordinator. The two injected close cases completed in about
   26 ms and 3 ms in one fixed run. These are observations, not latency promises.
   A malformed envelope from a child that keeps running now triggers reclamation
   and reports a worker failure. Reaped process handles close explicitly.

4. Idle Python worker stacks retained their last request, result, Future or
   callback owner. This could keep closed source text and GUI-related objects
   alive until the next task. I/O, position indexing, search and source-structure
   workers now clear completed references before waiting. The older branch also
   receives revision-worker cleanup; the newer branch closes an early-continue
   retention path. Six weak-reference regressions fail on the older baseline
   and pass after the changes, including a 2 MiB I/O payload.

REPEATED DIALOG MEASUREMENTS

One fresh process per branch; 20 About opens/closes followed by 40 alternating
Python open/save chooser cancellations. Same actual modal calls and deferred Qt
cleanup. The following samples are after all 60 interactions:

Version  Closed dialogs  OS threads  Open FDs  Resident MiB
1.2.1                60           53          90       133.43
1.2.2                 0           13          10       104.00
1.1.11               60           53          90       139.52
1.1.12                0           13          10       108.20

The fixed samples come from installed wheels. No closed About/file dialogs
remain owned by the window. Initial Qt caches warm and then dialog memory
settles. The file-model thread and descriptor growth is eliminated in this
workload. Opening/closing the reused PCFC11 dialog adds one intended dialog and
leaves no active build timer. The regression also checks timer shutdown after a
real build attempt fails because its compiler path is missing.

These dialog measurements deliberately isolate window/dialog behavior and do
not run full numerical readiness initialization. They must not be quoted as the
newer branch's complete startup footprint.

FULL STARTUP AND REPEATED EDITING/CONVERSION

The separate probe performs normal readiness initialization, then 20 GUI
conversion/edit/clear cycles. Ten valid functions convert and ten syntactically
valid unsupported functions are rejected. Each cycle also opens a 30,000-line
comment document, edits it, synchronizes it and closes it. The harness accepts
discard for those synthetic unsaved edits. There are no uncaught GUI exceptions.

1.2.2: full empty workspace 99.25 MiB; cycle 5: 123.27 MiB; cycle 10: 124.60 MiB;
cycle 20: 126.71 MiB. OS threads remain 13. FDs rise once from 10 to 11 after
multiprocessing initializes, then remain 11. There is a small memory increase
between later samples; this short run is not a long-duration soak guarantee.

1.1.12: full empty workspace 284.16 MiB; cycle 5: 306.55 MiB; cycle 10: 305.92 MiB;
cycle 20: 306.86 MiB. OS threads remain 29. FDs rise once from 11 to 12, then
remain 12. Startup loads NumPy 2.3.5, Numba 0.63.1 and llvmlite 0.46.0. Their
resident libraries and native threads explain a substantial baseline cost;
thread count alone does not mean those threads are consuming CPU continuously.

No active conversion/outline children remain after each settled sample. After
teardown, only MainThread remains in Python's thread list, with no active
multiprocessing children. Qt/native numerical threads and allocator-reserved
memory remain in the still-running measurement host; they are not application
worker leaks. Python's process resource tracker can remain until interpreter
exit. These measurements do not capture peak child-process RSS.

Settled main-process CPU consumption was about 4–6 ms during each two-second
sample. Remaining observed timer events include a focused text cursor and
single-shot editor work; the probe's own timer also appears in its raw counts.
The measurements support reduced unnecessary waking. They do not measure watts,
whole-system energy, a battery-life percentage, or native Windows behavior.

THREADING AND MULTIPROCESSING REVIEW

The conversion scheduler keeps one active process and one latest pending
request. New submissions retire obsolete work; cancellation remains asynchronous
on the GUI path. Spawn isolation avoids forking live Qt threads. Results are
validated against request identity before publication. Conversion success,
rejection, supersession, cooperative/forced stop, abrupt exit, malformed output,
blocked request transfer and partial response transfer were exercised.

The newer branch additionally uses a shared two-slot background-work budget,
a cancellable outline process, byte-bounded revision/outline caches, and
QProcess-based editor services with deadlines and idle retirement. Existing
cache eviction, rapid edits, stale-result suppression, process cancellation,
completion/formatting/navigation and outline tests pass in the selected suite.

Workspace I/O has two daemon workers and a bounded queue. Search and revision
services use replaceable pending work rather than an unlimited work backlog.
GUI updates are delivered through Qt signals; background calculations operate
on captured data. The fixes preserve those boundaries. This audit supports the
tested paths; it does not prove the absence of every race in every environment.

The PCFC11 build service's existing process-group cancellation and timeout tests
pass using synthetic Python processes, including a descendant heartbeat check.
Native PCFC11 compiler execution was unavailable. Windows Job Object behavior,
Windows spawn/pipe behavior, suspend/resume and actual laptop power use require
native acceptance. No compiler or generated program was executed from user code.

GENERATED C MEMORY: A SEPARATE REMAINING DESIGN LIMIT

The runtime-value branch uses a context-owned arena. Public strings, containers,
objects and closures survive until pv_destroy. Discarding a Python local does
not reclaim its runtime value during that context's lifetime. Some internal
scratch buffers already have explicit reclamation. The default logical arena
budget is 64 MiB, plus a work budget; neither is a cap on the entire process RSS.
Allocator bookkeeping and external libraries can add memory beyond that budget.

A generated synthetic function repeatedly discarded a temporary string and list.
After three batches of 1,000 iterations in one context, retained arena bytes were
415,230; 830,124; and 1,245,018. With a deliberately smaller 4 MiB test budget,
a longer call reported MemoryError at 4,194,263 retained bytes, without exceeding
the configured arena accounting limit. Thirty separate create/use/destroy cycles
and the failed context's teardown left zero tracked live allocation blocks.
AddressSanitizer and UndefinedBehaviorSanitizer reported no error in this probe.
LeakSanitizer itself could not run because this container could not expose the
required process task directory; its failure log is preserved. This is targeted
allocation/teardown evidence, not a full runtime leak-free certification.

The arena behavior limits long-lived generated programs even when live data is
small. A future reclamation design needs explicit roots, alias/closure/default
lifetime rules, cycle handling and error-unwind tests. Inserting opportunistic
frees here would risk use-after-free. Converter/runtime bytes are unchanged by
this IDE reliability update.

REMAINING PRIORITIES BEFORE FURTHER LARGE COVERAGE EXPANSION

1. Run the included Windows workload on the target Python/PySide6 combination,
   including repeated cancel/close, build descendants and suspend/resume. Run a
   longer steady workload and inspect memory, handle/thread trends and CPU time.
2. Evaluate lazy numerical initialization for the newer branch. Its roughly
   284 MiB empty-workspace footprint deserves attention independently of leaks.
   Preserve truthful readiness and deterministic first-use behavior when changing
   it; global thread-pool environment overrides were not introduced here.
3. Design runtime-value reclamation for long-running generated C programs.
4. Define user-visible large-file and undo-memory policies. Conversion and caches
   are bounded, but open file reads and Qt undo history are not a whole-IDE RSS
   quota. Very large documents can still cost substantial RAM. A blocked network
   filesystem call also cannot be forcibly stopped safely inside a Python I/O
   thread; the current queue is bounded but does not provide a hard I/O deadline.

VALIDATION AND REPRODUCIBILITY

1.2.2:
  resource-lifecycle: 35 passed, 11 skipped, 10 subtests passed in 9.77s
  resource-final-gates: 54 passed, 185 subtests passed in 3.69s
  resource-services: 61 passed, 36 subtests passed in 1.55s
  installed wheel: 21 passed in 5.83s
1.1.12:
  resource-lifecycle: 104 passed, 1 skipped, 8 subtests passed in 32.28s
  resource-final-gates: 54 passed, 204 subtests passed in 4.71s
  resource-services: 61 passed, 49 subtests passed in 1.75s
  installed wheel: 20 passed, 1 skipped in 9.85s

Focused source selections total 150 passing tests in 1.2.2 and 219 in 1.1.12,
with 11 and 1 skips respectively. Subtest counts and raw XML are preserved. The
11 PCFC11 skips require the unavailable compiler; the newer branch skips the
PCFC11-only GUI case. Installed regression checks rerun the new resource tests
and eight earlier editor-lifecycle regressions. Installed version checks cover
package metadata, Qt application version, titles, startup, About, every Help
footer and the applicable PCFC11 report. An initial installed-test harness used
an import mode incompatible with spawn imports; correcting the harness resolved
those three fixture import failures. No application change was needed.

Environment: 3.12.14, Linux Qt offscreen, PySide6
6.12.0, pytest 9.1.1, GCC native C
probe. GUI probes use temporary settings and synthetic source. RSS is measured
from /proc/self/status; Python weak references test object lifetimes; thread and
FD counts cover Qt/native resources that tracemalloc alone would miss. Short
samples and one environment limit generalization. No full converter-suite rerun,
PyPI publication, native Windows execution or battery discharge test is claimed.

Source/complete archives include changed-file inventories, patches, probes,
raw measurements and test evidence under evidence/resource_audit. The README's
two original screenshot links and screenshot bytes are preserved. HTML Help
remains a usage/reference manual and contains no new audit or release-cycle text.

Primary API references used for ownership and timing interpretation:
https://doc.qt.io/qt-6/qobject.html
https://doc.qt.io/qt-6/qdialog.html
https://doc.qt.io/qt-6/qtimer.html
https://doc.qt.io/qt-6/threads-qobject.html
https://docs.python.org/3.12/library/tracemalloc.html
