New goal G-076 (active): adopt the upstream tcl9 dead-console fix (ticket
f10d91c2d3, root-caused in G-039) via a single version gate shared by the
'help tcl' warning and the repl watchdog arming.
- punk::lib 0.4.3: has_tclbug_console_deadspin + pure classifier
tclbug_console_deadspin_applies, gate variable
check::tclbug_console_deadspin_fixed_in (empty = all Tcl 9 windows
runtimes affected). Buginfo dicts gain a mitigated/mitigation axis
orthogonal to level; the deadspin check reports mitigated when
punk::repl >= 0.5.0 is available to the runtime.
- punk::repl 0.5.1: repl::start arms the dead-console watchdog only when
the check reports the runtime affected - recording a verified fixed
release once flips both consumers.
- punk 0.2.6: 'help tcl' renders mitigated warnings subdued (term-grey)
with a "(mitigated)" annotation and the mitigation text; severity
classification unchanged.
- checkbugs.test: classifier truth table, live-check consistency, and
mitigated-axis validation in the generic every-check sweep.
The fixed_in flip is deliberately deferred until a released Tcl passes
the G-039 kill-procedure re-verification (watchdog disabled, clean
script-visible eof exit) - recorded as the goal's remaining acceptance
item.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
An interactive shell orphaned by its hosting console dying (killed
conhost/terminal) previously spun ~2 cores forever: the Tcl 9 windows
console driver never delivers the dead-console state to the script as a
fileevent (tclWinConsole.c ConsoleEventProc only notifies on buffered
data) while its reader thread busy-loops on the persistent channel error,
so the script level is completely blind and nothing ever closes the
channel.
Fix: repl::console_watchdog - a self-rescheduling liveness poll (default
5s, repl::console_watchdog_ms; read-only GetConsoleMode probe via
chan configure -inputmode) armed by repl::start only for a tcl9 console
input channel serving the process-default console on windows. On probe
failure it closes the input channel (stopping the driver's reader
thread) and finishes the repl via the normal eof done-path; app-punkshell
then finds no console reopenable and exits cleanly. Piped, foreign-console
and tcl 8.6 inputs never arm it. repl::start's post-vwait reader
deregistration now tolerates a watchdog-closed channel.
Verification (2026-07-12): kill procedure post-fix exits the orphan in
1.5-2.0s with no traceback (src mode and rebuilt kits punkbi/punk91/
punk902z); 25s live-console soak shows no spurious trigger, CPU delta 0;
piped stdin unaffected; repl consolebackends 3/3 and punk::console suites
88 pass / 1 skip; root cause verified against plain tclsh 9.0.3.
Upstream ticket filed: https://core.tcl-lang.org/tcl/tktview/f10d91c2d3
Included:
- goals: G-039 flipped achieved 2026-07-12 and archived (index entry to
GOALS-archive.md, detail file to goals/archive/ with full root-cause,
repro and verification record); reference sweep adds the eof done-value
producer note to G-038
- src/modules/punk/AGENTS.md: watchdog contract documented
- project version 0.12.1 -> 0.12.2 + CHANGELOG entry
- vfs payload sync: _vfscommon.vfs punk/repl 0.4.0 -> 0.5.0
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
repl::init -console <spec> selects the console the repl reads/writes (any
punk::console::console_spec_resolve form); repl::start's inchan is now
optional, defaulting to the selected console's input. Per-repl channel state
(conin/conout/conerr) routes rputs (stdout/stderr mapped per-repl) and
doprompt; for a foreign console the code interp's stdout/stderr are diverted
via shellfilter 'var' junction stacks and emitted to the console after each
run (repltype punk/0). New helpers console_is_default/console_at_eof/
console_get_size dispatch eof/size to the selected ::opunk::Console object's
(possibly overridden) methods. Process-console behaviours (tcl_interactive
prompt gating, stdin reopen on eof, raw-mode re-enable, windows utf-16be
line re-decode experiment, mode-line on exit) now apply only to the default
console. Default stdin/stdout behaviour unchanged. Also fixes rputs pseudo-
channel mapping in the 3-arg -nonewline form (mapped value previously
written over the -nonewline flag).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- punk_main.tcl captures the runtime static baseline at kit boot: empty-filename [info loaded] prefixes probe-loaded in a throwaway interp (configurable denylist tk*/vfs/mk4tcl/vlerq/tdbc* for side-effecting/composite inits), recording only packages a load actually PROVIDES (diff on provides, not package names - probe loads can trigger index scans) into ::punkboot::static_packages/static_prefixes; the boot interp is seeded with ifneeded {load {} <prefix>} mappings
- punk::lib interp_sync_package_paths/snapshot_package_paths propagate the baseline and seed fabricated interps/threads; punk::repl codethread init (new %staticprefixes%/%staticpackages% scriptmap) and shellthread worker init do the same - the repl code interp on a static runtime resolves Thread again (punk91 = tclsfe-x64 + punk9win.vfs failed with "can't find package Thread"; the appended vfs replaces the stub's //zipfs:/app mount taking its static pkgIndex files with it, and the tclsfe PostInit hook seeds per-interp auto_path only)
- punk::packagepreference static-vs-bundled policy: requiring a baseline package ensures the static mapping and triggers the package unknown scan BEFORE resolution so bundled copies register and the highest version wins (newer bundled reachable, static not masked by older bundled); static registrations excluded from the loaded-shared-object same-version pinning
- composite statics defer correctly: static twapi provides nothing under the probe (script layer lived in the replaced stub zip) so the bundled complete twapi is the resolution; punk91 vfs/vfs::zip warning re-diagnosed as kit content (tclsfe bundles no tclvfs) - recorded in the goal detail, not a seeding defect
- tests: staticseed.test (4) + staticpolicy.test (4) un-gated simulated-baseline units (green 9.0.3 + 8.7); staticruntime.test (4) kit integration behind the capability-probed statickitavailable constraint (probes ::punkboot baseline incl Thread; env PUNK_STATICKIT_TEST_EXE override, default bin/punk91.exe) - 4/4 against the rebuilt punk91; punk902z/full shell tree/full source-tree suite baselines unchanged (exec-14.3 only)
- verification runtime tclsfe-x64.exe pinned in the punkbin artifact repo (separate repo, commit b2dbbe6) with sha1sums + new AGENTS.md scaffolding
- punkshell 0.6.0: CHANGELOG entry; AGENTS updates (src/vfs capture contract, tests indexes incl new punk/lib + punk/packagepreference entries, shell staticruntime contract)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Companion to punk::console 0.7.1 (three-site 8.6 console misdetection fix).
The line-mode waiting-chunks path in repl_handler performed an unsized read
that, entered via 'after idle' with the channel drained, made the tcl 8.6
windows console driver park a blocking cooked-mode ReadConsole (driver reads
sample the console mode at issue time - a later raw flip cannot cancel one).
After typed-ahead was stashed during a terminal-query raw window, that
parked read swallowed every subsequent query response in the session until
Enter, with the responses later leaking to the reader.
On hostage-prone consoles (windows, no -inputmode configure key, real twapi
console handle) the path now consumes only data already in the Tcl channel
buffer (chan pending input + sized read - no driver probe). With nothing
buffered it processes the stashed complete lines directly - preserving the
recovered-typeahead execution order - and arms the readable handler for any
remaining partial line, replacing an after-idle reinvoke that, without the
read, could never have progressed; the parked read that arming creates is
the reader legitimately waiting for the user to finish typing. Behaviour on
tcl 9/8.7 (-inputmode consoles) and in raw mode is unchanged.
Interactively verified on punksys (tcl 8.6.13, src launch): the previously
failing case - typing a full command during a line-mode brokered query loop
- now runs with all queries answering, the typed line executing after the
loop, and no timeouts, response leakage or chan-blocked diagnostics; the
session remains healthy afterwards. Full suite passes at baseline
(exec-14.3 only) under Tcl 9.0.3.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Per-console facts are now visible to every thread of a punk session: the
default console {stdin stdout} keeps its legacy namespace variables as
authoritative local storage, with write traces mirroring every write
(including direct variable writes) into tsv punk_console_facts; non-default
consoles store facts only in tsv with owner-qualified keys (non-std channel
names are thread-local and would otherwise alias). console_fact_set from a
non-owner thread forwards to the owning thread (vt52-alias transport) and
always writes the tsv mirror itself (covers unreachable or older-version
owners). New console_fact_clear for tests/maintenance.
New console ownership registry (console_owner_register/get/forget, tsv
punk_console_owners keyed by canonical {in out} pair): ownership is captured
when an opunk::console instance is anchored - via the new pluggable
::opunk::console::lifecycle_callback (opunk::console 0.4.0, base class
unchanged) wired by ensure_object_integration - and by default_console.
Consult-time liveness validation clears entries for exited threads. For
{stdin stdout} first registration wins and only the owner's forget releases
the entry, so a thread anchoring a local view cannot steal ownership.
dec_has_mode/ansi_has_mode caches moved to tsv punk_console_modecache
(single-key atomic entries shared process-wide).
Infrastructure tsv arrays are now punk_-prefixed to avoid collisions with
application tsv usage in subshells: console -> punk_console (is_raw), with
call-site patches in punk::repl 0.2.1, punk::lib 0.2.1, punk::basictelnet
0.1.1 (rule recorded in src/modules/AGENTS.md; legacy repl/codethread_*/
zzzload_pkg* arrays left for a coordinated follow-up).
punk::console is now loadable in secondary threads without ::argv0
(powershell consolemode fallback guarded).
Tests: consolefacts gains ownership-registry and cross-thread visibility
coverage (worker-thread punk::console load, trace-mirror, forwarded set with
deadline-polled sync - see comments re runtests child-interp topology);
cleanups use console_fact_clear / tsv instead of poking removed internal
stores. Full suite passes (baseline exec-14.3 only).
G-007 flipped proposed -> active (user-confirmed); remaining acceptance work
is the choke-point brokering slice.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The codethread init_script now propagates package prefer via a new %packageprefer% scriptmap entry. The code interp (punk/0 repltype) creation replaces the manual epoch-only copy with punk::lib::interp_sync_package_paths code -libunknown 1, which propagates tm list, auto_path, package prefer, and libunknown epoch+init from the codethread to the code interp. This fixes the issue where package prefer latest set in punk_main.tcl was not inherited by the REPL's code interp, causing 999999.0a1.0 dev modules to be passed over in favour of stable VFS-bundled versions.
opunk::console (0.2.0):
- probe-byte waiting store changed from dict to array (::opunk::console::waiting_chunks) and made
pluggable via waiting_chunks_arrayvar: integrating layers redirect it to their established
cooperative store so bytes consumed by opunk::Console probe reads stay visible to active readers.
The class remains free of punk::console dependencies - redirection is the integrator's act.
punk::console (0.1.6):
- default_console redirects the opunk::console store to punk::console::input_chunks_waiting
(closing the split-store hazard that previously made opunk-level probes invisible to the
punk repl reader)
- get_ansi_response_payload performs first-use active settling for persistable console specs,
guarded by a recursion latch around settle_can_respond's own probe: every query path
(get_cursor_pos, get_device_status, ...) now pays at most one settling probe instead of a
fresh timeout per call on unsettled ambiguous consoles
punk::repl (0.1.6):
- repl::init routes default console construction through punk::console::default_console
(deduplicates creation logic and gains the store unification)
tests: store-unification and get_cursor_pos first-use settling cases in consolespec.test
(zero-emission and persistence asserts); consoleclass.test store assertions converted to array
form. Console suites 25/25 + 9/9; punkexe 6/6, goals 8/8; full suite green except pre-existing
exec-14.3.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
punk::console (0.1.5):
- settle_can_respond: layered active settling of a console's response capability, persisted on
the anchored opunk::console instance. Layer 1 certainty (input eof -> 0, no emission),
layer 2 strong-negative heuristic (plain pipe, no terminal hints -> 0, no emission - piped/CI
output streams stay clean), layer 3 active CSI 6n probe with timeout, only in the ambiguous
zone (real consoles / pipe-presenting terminals such as mintty without winpty). -force
unsettles and re-probes; -timeout_ms overrides can_respond_probe_timeout_ms (default 500).
Unmatched input consumed while waiting is preserved via input_chunks_waiting.
- get_size triggers first-use settling for persistable specs only (anchored instance name or
the auto-attached default) so unanchored object values cannot cause per-call probes
- get_ansi_response_payload only cycles console raw mode when the input channel is a
console/tty: pipe-targeted probes no longer flip the real console's mode as a side effect,
and a repeat-cycle deadlock under captured-channel environments (runtests runx) is fixed.
(Latent enableRaw_twapi/disableRaw_twapi restore asymmetry noted for future attention.)
punk::repl (0.1.5) / opunk::console (0.1.1): docs reference the active settling mechanism
(no behaviour change; probe machinery stays in punk::console so the class remains free of
that dependency)
tests: 5 new settle_can_respond cases in consolespec.test - certainty and clean-pipe settling
with zero-emission asserts, ambiguous probe timeout with emission and input-preservation
asserts, response arrival settling 1 with realistic event-loop delivery then -force
transition to 0, and get_size first-use settling. Console suites 23/23; full suite green
except pre-existing exec-14.3.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com