-
2026-07-16 —
CoreUtils.listify(threading=True)built aThreadPoolExecutoreven for a scalar call — sustained, the churn aborted the interpreter. Found while running tentacle's suite: the full run died mid-way withFatal Python error: Aborted(no traceback, no summary — just a native exit code), andfaulthandlerput the main thread insideThread.start(), reached fromptk.format_path(ui_file, "name")under uitk's.uiloader. The decorator wraps a scalar argument as[arg]and then mapped that one item through a pool: nothing to parallelize, a worker thread spun up and torn down per call, on pure-CPU helpers callers hit per path/string (format_path,truncate,math_utils/img_utils/str_utils— nine functions carry the flag). It presented as a full-suite-only crash because it needs the churn to accumulate; each test module passed alone, which is why it read as flakiness. Fixed by gating the pool on the already-computedis_single_itemflag, so scalars run inline and genuine batches still fan out — no parallelism is lost, since mapping one item never had any to begin with;format_path(one_path)measures 388 µs → 7 µs per call (~55×). Gated on the flag rather thanlen(arg_list)because a real iterable may be lazy (generator/zip/map):executor.mapaccepts those,lenraises. Behaviour is otherwise unchanged (executor.mapand the list comprehension both preserve order). Tentacle's full suite now completes (449 tests, 0 failures) where it previously aborted; guarded bytest_core.py, which pins both halves — scalars stay on the calling thread, batches still leave it. -
2026-07-16 — New
TempArtifacts(root:ptk.TempArtifacts) — the single home for the "stage a temp payload for another process" pattern;HandoffBridge._make_payload_pathnow delegates to it. An audit found ~14 independent temp-payload sites across the bridges with four divergent lifecycle patterns — including the ScriptLaunch payloads, which were never deleted (unique tag per send →%TEMP%accumulation forever). The class allocates prefix-scoped<prefix>_<tag><ext>paths and names the only three lifetimes that are sound for inter-process payloads: scoped (delete on success, keep + log on exception — a blocking convert), session (atexitremoval — a launched app reads during this process's life), and detached (no deterministic delete exists, so allocation garbage-collects stale same-prefix files instead — age-gated ≥max_age_days, never a fresh payload another app may still be reading). Also:register()adopts side artifacts into the lifecycle, andon_cleanup(paths)fires just before removal (exceptions logged, never propagated)._make_payload_pathkeeps its exact path shape (same prefix_tag scheme, now monotonic per instance — Windows'time_nsis too coarse to be unique back-to-back) and gains the stale sweep for free; all four push bridges inherit the fix with zero call-site changes. Pre-push review refinements: the first-allocation stale sweep now runs for every policy, not just detached — scoped's keep-on-failure and session's hard-crash leftovers had no other reclamation path (the exact accumulation this class exists to end); the keep-on-failure message logs atwarning(the default level —infomade the documented "keep + log" contract silent); andpath()'s default extension is a neutral.tmpinstead of the bridge-flavored.fbx(_make_payload_pathkeeps its own.fbxdefault at the bridge layer, where it belongs). Newtest/test_temp_artifacts.py(22) pins policies, CM keep-on-failure, callback, sweep conservatism (age gate, prefix scope, all-policy leftover GC), and the root export. -
2026-07-16 — New
run_script_to_artifact/ScriptRunResult(core_utils/script_run.py) — the blocking run-script-collect-artifact counterpart ofScriptLaunchDeliverer. The deliverer renders a script and launches a detached app (fire-and-forget push); pull-direction bridges need the opposite: write the rendered script, run the app attached viaAppLauncher.run, wait, and hand back a verified artifact. Success is judged by the artifact (exists + non-empty) — the exit code is advisory only, because DCC standalone interpreters (mayapy) are known to crash in teardown after the real work succeeded (observed live during verification: mayapy fatal-errored post-save and the artifact was perfect). On failure it raisesRuntimeErrorembedding the exit code + output tail, keeps the temp script (path carried on the exception) for debugging, and removes it on success — the scoped-TempArtifactscontract, which it composes.ScriptRunResultcarriesartifact/returncode/output(combined stdout+stderr — DCC warnings are diagnostic gold)/duration/script_path. First consumer: blendertk'sMayaSceneImport(import a.ma/.mbvia headless mayapy → FBX). Two hardenings from the post-ship critique pass: a pre-existing artifact is removed before the run (TDD'd red-first — a stale leftover from a prior run would otherwise fake success for a failed script, the exact hole a judged-by-artifact contract can't afford), and the child runs with the newAppLauncher.run(hide_window=True)— Windows pops a console window for a console-subsystem child (mayapy) when the parent is a GUI app (live Blender), and a captured-output child has no use for one (additive param, default off, POSIX no-op). Pre-push review refinement:subprocess.TimeoutExpirednow carriesscript_pathtoo — the docstring promised "script kept" on timeout but only theRuntimeErrorpath said where, leaving timeout debuggers to dig through%TEMP%by hand. Newtest/test_script_run.py(11) drives it withsys.executableas the app — success, artifact-missing/empty/stale failure, stderr capture, kept-script, timeout (incl. the carried path), customlaunch_args; the failure tests sweep their kept scripts in teardown via a test-ownedscript_prefix. -
2026-07-16 — New
StrUtils.strip_ansi(root:ptk.strip_ansi): remove ANSI/VT100 escape sequences from text. Any process that tees a stream inherits itsisatty(), and CPython emits colored tracebacks wheneverstderris a TTY — so a tee that faithfully delegatesisattystarts receiving escape bytes it never asked for. Consumers that render text without interpreting VT100 (a Qt view, a log file) then show literal[35mgarbage. Scope is CSI (ESC [ … final-byte, covering SGR color) plus theESC Ferange; a bareESCthat opens nothing recognizable is left alone rather than swallowing the next character. Pattern compiled at module scope (ANSI_ESCAPE_RE) — callers run it per console write. Consumed byuitk.ScriptOutput(at the widget, for every host) andblendertk's Script Output capture (at ingest, so the transcript buffer's char cap counts characters a reader will actually see).