Browse Source

punk::lib 0.4.3 + punk::repl 0.5.1 + punk 0.2.6: G-076 dead-console defect in 'help tcl' with mitigated axis, version-gated watchdog (project 0.12.21)

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
master
Julian Noble 2 days ago
parent
commit
97ca1ceb1e
  1. 4
      CHANGELOG.md
  2. 4
      GOALS.md
  3. 89
      goals/G-076-tcl9-deadconsole-fix-adoption.md
  4. 2
      punkproject.toml
  5. 37
      src/modules/punk-999999.0a1.0.tm
  6. 3
      src/modules/punk-buildversion.txt
  7. 2
      src/modules/punk/AGENTS.md
  8. 57
      src/modules/punk/lib-999999.0a1.0.tm
  9. 4
      src/modules/punk/lib-buildversion.txt
  10. 13
      src/modules/punk/repl-999999.0a1.0.tm
  11. 3
      src/modules/punk/repl-buildversion.txt
  12. 43
      src/tests/modules/punk/lib/testsuites/lib/checkbugs.test

4
CHANGELOG.md

@ -5,6 +5,10 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.12.21] - 2026-07-13
- G-076 (new goal, active): 'help tcl' now warns about the tcl9 dead-console defect (upstream ticket f10d91c2d3, root-caused in G-039: a dead console is never delivered to the script as a fileevent while the core's console reader thread busy-loops) via punk::lib 0.4.3's has_tclbug_console_deadspin — version-based detection through the pure classifier tclbug_console_deadspin_applies, gated by check::tclbug_console_deadspin_fixed_in (empty until a released Tcl contains the upstream fix verified by re-running the G-039 kill procedure). punk::repl 0.5.1 arms the dead-console watchdog only when that same check reports the runtime affected, so recording the fixed release once silences the warning and stops arming the watchdog together. Buginfo dicts gain a mitigated/mitigation axis orthogonal to level (punk 0.2.6 renders it): the deadspin warning keeps severity major but displays "(mitigated)" in subdued grey with the watchdog's scope described when punk::repl >= 0.5.0 is available to the runtime; non-repl console reads remain exposed and unmitigated warnings render unchanged.
## [0.12.20] - 2026-07-13
- tclcore moduledoc 0.3.3: removed the unnecessary @dynamic tags from the split, array and join definitions - no bad-@dynamic warnings remain anywhere in the module, and the three definitions now cache their display expansion like the rest instead of re-expanding per render.

4
GOALS.md

@ -293,3 +293,7 @@ Detail: goals/G-074-punkargs-multiform-ambiguity-lint.md
### G-075 [proposed] punk::args (package) ids: working lookup and a user-facing package documentation surface
Scope: src/modules/punk/args-999999.0a1.0.tm (id lookup/update_definitions prefix handling, usage/arg_error rendering of package-level ids), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/help-system surface), src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/modules/template_module-0.0.4.tm (template block as touched/verified), src/tests/modules/punk/args/testsuites/args/ + src/tests/modules/punk/ns/testsuites/ns/ (new coverage)
Detail: goals/G-075-punkargs-package-ids.md
### G-076 [active] Adopt upstream tcl9 dead-console fix: shared version gate for watchdog and help tcl warning
Scope: src/modules/punk/lib-999999.0a1.0.tm (punk::lib::check), src/modules/punk/repl-999999.0a1.0.tm (repl::start watchdog arming), src/modules/punk/AGENTS.md, src/tests/modules/punk/lib/testsuites/lib/checkbugs.test
Detail: goals/G-076-tcl9-deadconsole-fix-adoption.md

89
goals/G-076-tcl9-deadconsole-fix-adoption.md

@ -0,0 +1,89 @@
# G-076 Adopt upstream tcl9 dead-console fix: shared version gate for watchdog and help tcl warning
Status: active
Scope: src/modules/punk/lib-999999.0a1.0.tm (punk::lib::check), src/modules/punk/repl-999999.0a1.0.tm (repl::start watchdog arming), src/modules/punk/AGENTS.md, src/tests/modules/punk/lib/testsuites/lib/checkbugs.test
Goal: the tcl9 dead-console defect (upstream ticket f10d91c2d3, root-caused in G-039) is surfaced by 'help tcl' via a has_tclbug_* check, and that check and the repl dead-console watchdog arming share a single version gate, so that runtimes at or past the first released Tcl containing the verified upstream fix neither warn nor arm the watchdog, while all earlier Tcl 9 windows runtimes keep both.
Acceptance: 'help tcl' on an affected runtime shows the warning with the f10d91c2d3 link and mitigation note; the applicability decision is a pure facts-in/verdict-out classifier proc with tests (libbug_udp_threadexit_applies precedent); repl::start arms the watchdog only when the same classifier reports the runtime affected, with live-console/piped/8.6 behaviour unchanged; the fixed-in constant is set only after the G-039 kill procedure, re-run on a released runtime containing the fix with the watchdog disabled, shows the repl exiting cleanly via the script-visible eof path - until then every Tcl 9 windows runtime is treated as affected.
## Context
G-039 (achieved 2026-07-12, goals/archive/G-039-orphan-console-spin.md) root-caused an orphaned-shell
CPU spin to a Tcl 9 core defect pair in win/tclWinConsole.c: (a) ConsoleEventProc drops the error/EOF
notification for a dead console so the script-level fileevent never fires; (b) ConsoleReaderThread
busy-loops on the persistent error. The script-level mitigation is repl::console_watchdog (punk::repl
0.5.0). The upstream ticket https://core.tcl-lang.org/tcl/tktview/f10d91c2d3 was filed 2026-07-12 and
has since been updated upstream with a fix branch in the Tcl repository (observed 2026-07-13).
This goal tracks adoption of the eventual released fix: surfacing the defect through the existing
'help tcl' has_tclbug_* auto-discovery, and making the watchdog version-conditional so runtimes
carrying the released fix don't warn and don't arm the probe.
## Approach
- punk::lib::check gains a pure classifier `tclbug_console_deadspin_applies {platform tclversion}`
(facts in, verdict out, testable - same pattern as libbug_udp_threadexit_applies from G-036, achieved -
see goals/archive/G-036-tcl9-udp-console-worker-wedge.md) and a
`has_tclbug_console_deadspin` wrapper returning the standard buginfo dict (bugref f10d91c2d3,
level major, description noting punkshell's watchdog mitigation).
- Detection is version-based only: a behavioural probe is impossible (it would require killing a
console). The classifier compares against a `tclbug_console_deadspin_fixed_in` namespace variable;
empty means "no fixed release known" and every Tcl 9 windows runtime classifies as affected.
- repl::start's watchdog arming consults the same classifier as an additional AND-term, so setting
the fixed-in constant once flips both the 'help tcl' warning and the watchdog together.
- Error direction is safe: a trunk/dev build carrying the fix under an old-looking patchlevel merely
arms a harmless 5s read-only probe.
- The fixed-in flip is deliberately gated on verification, not on the upstream branch: G-039 found
TWO defects, and the watchdog is only redundant if the notification defect (a) is fixed - i.e. the
script actually sees EOF on console death. Before setting fixed_in, re-run the G-039 kill
procedure (documented in the archived detail file) on the fixed released runtime with the watchdog
disabled and confirm the repl exits via the normal fileevent/eof path. If upstream fixes only the
spin, the watchdog stays necessary and the check's description changes instead.
## Notes
- G-039 (archived) recorded the reproduction/kill procedure and the two-defect mechanism - see
goals/archive/G-039-orphan-console-spin.md. Full ticket text with repro scripts:
TEMP_REFERENCE/tcl9-dead-console-spin-TICKET-DRAFT.md.
- 8.6 remains out of scope (different console driver; per G-039 user decision 2026-07-12).
## Progress
### 2026-07-13 classifier + help tcl surfacing + watchdog gating (initial increment)
Landed (punk::lib 0.4.2, punk::repl 0.5.1):
- punk::lib::check: `tclbug_console_deadspin_applies {platform tclversion fixed_in}` pure
classifier + `has_tclbug_console_deadspin` wrapper (bugref f10d91c2d3, level major, description
notes the punk::repl watchdog mitigation) + gate variable `tclbug_console_deadspin_fixed_in`
(empty = all Tcl 9 windows affected).
- repl::start watchdog arming gained the `[dict get [punk::lib::check::has_tclbug_console_deadspin] bug]`
AND-term; console_watchdog argdoc and src/modules/punk/AGENTS.md note updated.
- Tests: checkbugs.test +2 (classifier truth table incl. fixed_in comparisons; live-check/classifier
consistency + bugref).
Verification (2026-07-13, native tclsh 9.0.3 runtests + punk902z kit 9.0.2 in src mode):
- lib checkbugs.test 5/5 pass; repl consolebackends.test 3/3 pass; `make.tcl modules` builds clean.
- Live check on tclsh 9.0.3: bug=1; setting fixed_in to the running version flips bug=0.
- `'help tcl' | punk902z src` (piped, PUNK_PIPE_EOF=exit): major warning displayed with
description, mitigation note and f10d91c2d3 hyperlink.
### 2026-07-13 mitigated/mitigation buginfo axis (punk::lib 0.4.3, punk 0.2.6)
- buginfo dicts gain an optional axis orthogonal to level: `mitigated` boolean + `mitigation`
text. 'help tcl' renders a triggered mitigated check as "warning level: <level> (mitigated)"
in subdued grey (term-grey foreground) instead of the level colour, with an indented
"mitigated: <text>" block; unmitigated warnings render unchanged.
- has_tclbug_console_deadspin populates it: mitigated when punk::repl >= 0.5.0 is available to
the runtime (version discovery without loading, udp-check precedent), mitigation text noting
the watchdog scope (non-repl console reads remain exposed). Severity stays major.
- checkbugs.test extended (axis key presence/boolean/consistency rules incl. the generic
every-check sweep); 5/5 pass under native tclsh 9.0.3. Piped `'help tcl' | punk902z src`
verified: grey (38;5;8 fg) mitigated rendering with mitigation block and ticket hyperlink.
Remaining for acceptance:
- When a released Tcl contains the merged upstream fix: re-run the G-039 kill procedure on that
runtime with the watchdog disabled, confirm clean script-visible eof exit, then set
`punk::lib::check::tclbug_console_deadspin_fixed_in` to that version (one edit flips both
consumers) and update the AGENTS.md note.

2
punkproject.toml

@ -1,4 +1,4 @@
[project]
name = "punkshell"
version = "0.12.20"
version = "0.12.21"
license = "BSD-2-Clause"

37
src/modules/punk-999999.0a1.0.tm

@ -8616,7 +8616,11 @@ namespace eval punk {
warnings for known Tcl bugs affecting this interpreter
and known bugs in bundled library packages
(as detected by the punk::lib::check::has_tclbug_*
and has_libbug_* checks)."
and has_libbug_* checks).
A warning whose buginfo reports a shipped punkshell
mitigation keeps its severity level but is annotated
'(mitigated)' and rendered subdued (grey), with the
mitigation described."
@values -min 0 -max 0
}
proc tcl {context args} {
@ -8639,17 +8643,36 @@ namespace eval punk {
if {[dict exists $buginfo level]} {
set level [dict get $buginfo level]
}
switch -- $level {
minor {set highlight [punk::ansi::a+ cyan]}
medium {set highlight [punk::ansi::a+ yellow]}
major {set highlight [punk::ansi::a+ red bold]}
default {set highlight ""}
#mitigated is an axis orthogonal to level: the defect keeps its severity
#classification but a shipped punkshell mitigation covers it, so the
#warning renders subdued (grey) with a '(mitigated)' annotation and any
#mitigation text from the buginfo dict.
set mitigated 0
if {[dict exists $buginfo mitigated]} {
set mitigated [dict get $buginfo mitigated]
}
if {$mitigated} {
set highlight [punk::ansi::a+ term-grey]
} else {
switch -- $level {
minor {set highlight [punk::ansi::a+ cyan]}
medium {set highlight [punk::ansi::a+ yellow]}
major {set highlight [punk::ansi::a+ red bold]}
default {set highlight ""}
}
}
set levelshown $level
if {$mitigated} {
append levelshown " (mitigated)"
}
set indent " "
append warningblock \n $highlight "warning level: $level $bp triggered."
append warningblock \n $highlight "warning level: $levelshown $bp triggered."
if {[dict exists $buginfo description]} {
append warningblock \n "[punk::lib::indent [dict get $buginfo description] $indent]"
}
if {[dict exists $buginfo mitigation] && [dict get $buginfo mitigation] ne ""} {
append warningblock \n "[punk::lib::indent "mitigated: [dict get $buginfo mitigation]" $indent]"
}
if {[dict exists $buginfo url] && [dict get $buginfo url] ne ""} {
#full reference url (e.g. non tcl-core trackers such as tcludp)
append warningblock \n "${indent}see [punk::ansi::hyperlink [dict get $buginfo url]]"

3
src/modules/punk-buildversion.txt

@ -1,6 +1,7 @@
0.2.5
0.2.6
#First line must be a semantic version number
#all other lines are ignored.
#0.2.6 - G-076: 'help tcl' renders the mitigated/mitigation buginfo axis (punk::lib 0.4.3+): a triggered check reporting mitigated keeps its severity level but displays 'warning level: <level> (mitigated)' in subdued grey (term-grey foreground) instead of the level colour, followed by an indented 'mitigated: <text>' block when mitigation text is supplied. Unmitigated warnings render unchanged.
#0.2.5 - G-045: ::punk::helptopic::define_docs converts from interim left-margin authoring to indented block-form values under the new punk::args @normalize directive (the constructed-definition normalization consumer proof): basehelp/topichelp/help_chunks-extra are braced indented blocks with a structural leading newline, the generated definitions declare @normalize, and the -unindentedfields declarations from punk 0.2.4 are dropped. Rendered 'i help'/'i help_chunks' output unchanged (verified aligned incl. the blank-line separator in help_chunks).
#0.2.4 - G-045: 'i help' usage table alignment - ::punk::helptopic::define_docs authors its help text at the left margin and declares -unindentedfields {-help} on both the generated @cmd line (honoured as of punk::args 0.6.1) and the topic argument line. Previously the @cmd -help braced literal carried ~16 spaces of source indent into the constructed definition (no whole-block normalization), rendering Description continuations +12 right of the first line, and the \n-relative topic -help rendered its first line +4 (the injected display prefix). Both blocks now render flush. Text content unchanged (manual ~70-col folding retained).
#0.2.3 - punk::help topic definition adopts punk::args -choicealiases (G-040): choices are the four canonical topics with registry aliases folded ('i help' shows one entry per topic with an (alias:...) note), unique prefixes of topics and aliases accepted, minimum-prefix policy per user decision recorded in ::punk::helptopic (denylist {help}: h/he/hel stay command words; reservelist {c to tc}: fall through to command lookup); unrecognised words still fall through to basic command info; argless 'help' overview unchanged

2
src/modules/punk/AGENTS.md

@ -29,7 +29,7 @@ Source of truth for all modules under the `punk::*` namespace. This is the prima
- New modules under `punk::*` should be created as `<subpath>/<modulename>-999999.0a1.0.tm` following the namespace-to-path convention.
- punk::repl supports launch-time console selection (G-001): `repl::init -console <spec>` (any `punk::console::console_spec_resolve` spec form) selects the console the repl reads/writes; `repl::start`'s input channel argument is optional and defaults to the selected console's input. Repl output flows through per-repl channel state (`repl::conin/conout/conerr` - rputs maps stdout/stderr per-repl, conerr==conout for a foreign console pending G-011), the code interp's stdout/stderr are diverted via shellfilter `var` junction stacks and emitted to the console after each run (repltype punk/0 only so far), and eof/size/capability questions go through `repl::console_at_eof`/`repl::console_get_size` which dispatch 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) apply only when no foreign console is selected. Tests: `src/tests/modules/punk/repl/testsuites/repl/consolebackends.test` (child-process drivers - a repl cannot run inside the shared testinterp; see the suite header).
- punk::repl has a dead-console watchdog (G-039): `repl::start` arms `repl::console_watchdog` (default 5s, read-only `chan configure -inputmode` probe) only for a tcl9 console input channel (-inputmode present) serving the process-default console on windows - the Tcl 9 console driver never delivers a dead console (killed conhost/terminal) to the script as a fileevent and its reader thread busy-loops on the persistent error, so without the watchdog an orphaned shell spins CPU forever. On probe failure the watchdog closes the input channel (stopping the driver's reader thread) and finishes the repl via the normal eof path. Piped, foreign-console and tcl 8.6 inputs never arm it. Root-cause and verification detail: `goals/archive/G-039-orphan-console-spin.md` (achieved 2026-07-12); upstream ticket FILED 2026-07-12: https://core.tcl-lang.org/tcl/tktview/f10d91c2d3 (full text with repro scripts in `TEMP_REFERENCE/tcl9-dead-console-spin-TICKET-DRAFT.md` - scripts elided from the web submission). If a future Tcl release fixes the driver, the watchdog can become version-conditional.
- punk::repl has a dead-console watchdog (G-039): `repl::start` arms `repl::console_watchdog` (default 5s, read-only `chan configure -inputmode` probe) only for a tcl9 console input channel (-inputmode present) serving the process-default console on windows - the Tcl 9 console driver never delivers a dead console (killed conhost/terminal) to the script as a fileevent and its reader thread busy-loops on the persistent error, so without the watchdog an orphaned shell spins CPU forever. On probe failure the watchdog closes the input channel (stopping the driver's reader thread) and finishes the repl via the normal eof path. Piped, foreign-console and tcl 8.6 inputs never arm it. Root-cause and verification detail: `goals/archive/G-039-orphan-console-spin.md` (achieved 2026-07-12); upstream ticket FILED 2026-07-12: https://core.tcl-lang.org/tcl/tktview/f10d91c2d3 (full text with repro scripts in `TEMP_REFERENCE/tcl9-dead-console-spin-TICKET-DRAFT.md` - scripts elided from the web submission). The watchdog is version-conditional (G-076): arming additionally requires `punk::lib::check::has_tclbug_console_deadspin` to report the runtime affected - the same check `help tcl` surfaces - gated by `check::tclbug_console_deadspin_fixed_in` (empty until a released Tcl containing the verified upstream fix passes the G-039 kill-procedure re-verification; see `goals/G-076-tcl9-deadconsole-fix-adoption.md`).
- punk::console uses the documented `-console` convention throughout: a `-console` value may be a 2-element {in out} channel list, an anchored `opunk::console` instance name, or an `::opunk::Console` object value (resolved via `punk::console::console_spec_resolve`). Query functions use the hybrid pattern (legacy trailing positional spec also accepted, parsed by `punk::console::internal::hybrid_console_spec`) - new query procs must follow it, with tests (see `src/tests/modules/punk/console/testsuites/console/queryprocs.test`). PUNKARGS definitions include the `-console` option via the shared fragments `::punk::console::argdoc::console_opts` (query/set functions) or `::punk::console::argdoc::console_emit_opts` (emit functions) rather than duplicating the option text; never re-add a `-minsize 2` constraint to `-console` (it rejects instance-name specs). The internal `get_size_using_*` size mechanisms deliberately remain canonical-pair positional (always fed by `get_size`).
- punk::console emit-side functions (the `punk::console::ansi::*` emit wrappers, mouse/paste toggles, `vt52`, `set_tabstop_width`, `titleset`, top-level `move`, and the width-test probes) accept an optional trailing `-console <consolespec>` pair, parsed manually for performance by `punk::console::internal::opt_console_out`/`opt_console_channels` (`_var` variants for procs whose args-tail also carries row/col/data triples). Each carries a documentation-only PUNKARGS definition that includes the shared `::punk::console::argdoc::console_emit_opts` fragment via `punk::args::resolved_def`; keep manual parsing and PUNKARGS synchronized. New emit procs must follow this pattern. Tests live in `src/tests/modules/punk/console/testsuites/console/emitconsole.test`.
- punk::console terminal-property facts (is_vt52, tabwidth, cell_size, last_da1_result, grapheme_cluster_support, check::has_bug_*) are per-console: read/write them via `punk::console::console_fact_get`/`console_fact_set`, keyed by canonical {in out} channel pair. The store is tsv-backed (G-007) so all threads read the same values: the process-default console `{stdin stdout}` keeps the legacy namespace variables (`::punk::console::is_vt52`, `tabwidth`, ...) as its authoritative local storage with write traces mirroring into tsv `punk_console_facts` (so existing external readers and direct writers keep working); non-default consoles store facts only in tsv with an owner-qualified key. Do not bypass the helpers for non-default consoles; use `console_fact_clear` (not direct store manipulation) to reset facts in tests. `ansi_wanted`/`colour_disabled` (string-generation gates), `ansi_available` and raw-mode state are deliberately process-global (rationale documented at the fact store in the module). Tests live in `src/tests/modules/punk/console/testsuites/console/consolefacts.test`.

57
src/modules/punk/lib-999999.0a1.0.tm

@ -250,6 +250,63 @@ tcl::namespace::eval punk::lib::check {
return [dict create bug $bug bugref e38dc74e2 description $description level medium]
}
#G-076: version gate for the tcl9 dead-console defect (upstream ticket f10d91c2d3, root-caused
#in G-039). Shared by the 'help tcl' warning and the repl dead-console watchdog arming (both
#consult has_tclbug_console_deadspin). Empty = no released Tcl is known to contain the upstream
#fix, so every Tcl 9 windows runtime classifies as affected. Set this only after the G-039 kill
#procedure, re-run on the fixed released runtime with the watchdog disabled, shows a clean
#script-visible eof exit (see goals/G-076-tcl9-deadconsole-fix-adoption.md).
variable tclbug_console_deadspin_fixed_in ""
#pure classifier, separated for testability - facts in, verdict out
proc tclbug_console_deadspin_applies {platform tclversion fixed_in} {
if {$platform ne "windows"} {
return 0
}
if {![package vsatisfies $tclversion 9]} {
#tcl 8.6 has a different console driver - out of scope per G-039 (user decision 2026-07-12)
return 0
}
if {$fixed_in eq ""} {
#no released fix known - all Tcl 9 windows runtimes affected
return 1
}
return [expr {[package vcompare $tclversion $fixed_in] < 0}]
}
proc has_tclbug_console_deadspin {} {
#Tcl 9 windows console driver defect pair (win/tclWinConsole.c): when the hosting
#console dies (killed conhost/terminal), (a) ConsoleEventProc drops the error/EOF
#notification so a stdin readable fileevent never fires - the script is blind to the
#dead console; (b) ConsoleReaderThread busy-loops on the persistent error, spinning
#~2 cores until the channel is closed. Root-caused 2026-07-12 - see archived goal
#G-039; upstream ticket f10d91c2d3 filed 2026-07-12. punk::repl >= 0.5.0 mitigates
#with a console liveness watchdog (repl::console_watchdog) gated on this same check.
#Version-based detection only - a behavioural probe would require killing a console.
#The buginfo dict carries the mitigated/mitigation axis: level stays major (the core
#defect's severity), mitigated reports whether punk::repl >= 0.5.0 (console liveness
#watchdog) is available to this runtime - 'help tcl' renders mitigated warnings subdued.
variable tclbug_console_deadspin_fixed_in
set bug [tclbug_console_deadspin_applies $::tcl_platform(platform) [info patchlevel] $tclbug_console_deadspin_fixed_in]
set description "Tcl 9 windows console driver: a dead console (killed conhost/terminal) is never\ndelivered to the script as a fileevent, and the core's console reader thread busy-loops\non the persistent error - an orphaned tclsh spins CPU indefinitely. Plain tclsh scripts\nreading a console stdin have no script-level escape (see goal G-076)."
set replversion [package provide punk::repl]
if {$replversion eq ""} {
#not loaded - determine what version would be provided, without loading it:
#an unsatisfiable require triggers the package unknown scan (registering ifneeded
#scripts) then fails before any load (999999.0a1.0 dev modules are alpha - below 999999).
catch {package require punk::repl 999999}
set available [package versions punk::repl]
if {[llength $available]} {
set replversion [lindex [lsort -command {package vcompare} $available] end]
}
}
set mitigated [expr {$bug && $replversion ne "" && [package vsatisfies $replversion 0.5-]}]
set mitigation ""
if {$mitigated} {
set mitigation "punk::repl $replversion is available to this runtime: its console liveness watchdog\n(armed when the repl serves the process-default console) closes the dead channel and\nexits cleanly instead of spinning. Non-repl console reads remain exposed."
}
return [dict create bug $bug bugref f10d91c2d3 description $description level major mitigated $mitigated mitigation $mitigation]
}
#has_libbug_* procs report bugs in bundled/vendored library packages rather than the Tcl core.
#They are surfaced through the same 'help tcl' warning report as the has_tclbug_* checks.

4
src/modules/punk/lib-buildversion.txt

@ -1,6 +1,8 @@
0.4.1
0.4.3
#First line must be a semantic version number
#all other lines are ignored.
#0.4.3 - G-076: buginfo dicts gain an optional mitigated/mitigation axis (mitigated boolean + mitigation text) orthogonal to level - a check whose defect is covered by a shipped punkshell mitigation reports it so 'help tcl' can render the warning subdued while keeping the severity classification. has_tclbug_console_deadspin populates it: mitigated when punk::repl >= 0.5.0 (console liveness watchdog) is available to the runtime (udp-check-style version discovery without loading), with the watchdog scope described in the mitigation text (non-repl console reads remain exposed).
#0.4.2 - G-076: new punk::lib::check::has_tclbug_console_deadspin + tclbug_console_deadspin_applies classifier - version-based detection of the tcl9 windows dead-console defect (f10d91c2d3, root-caused in G-039: dead console never delivered as a fileevent + core reader thread busy-loop). Gate variable check::tclbug_console_deadspin_fixed_in (empty = no fixed release known - all Tcl 9 windows affected); shared by the 'help tcl' warning and repl::start's watchdog arming.
#0.4.1 - G-051 call-site adaptation: tclscript analysis accepts cmdinfo's new 'doconly' cmdtype alongside 'notfound' (subcommand-walk stop condition and the dispatchwords bucketing switch) - bucketed identically to notfound to preserve prior analysis behaviour; a future refinement may count doconly (a documented pseudo-command such as 'string is xdigit') as a valid command word.
#0.4.0 - G-058: snapshot_package_paths and interp_sync_package_paths now also propagate the runtime static/builtin package baseline captured at kit boot (::punkboot::static_prefixes/static_packages - see punk_main.tcl) and seed 'package ifneeded <name> <ver> {load {} <prefix>}' entries in the target thread/interp, so statically-linked runtime packages (e.g Thread/twapi on a tcl-sfe runtime) resolve in fabricated interps whose package search paths punkshell controls. No-op when no baseline was captured (plain tclsh dev launches).
#0.3.1 - Tcl 9.1 compatibility (TIP 746 removed expr behaviour from lseq operands): range (lseq branch) now normalizes int[+-]int offset operands itself via offset_expr - callers such as `range 0 [llength $list]-1` keep working; also aligned the lseq branch with the fallback contract: default by now infers direction (range 5 1 -> descending, previously empty under tcl9) and by 0 returns empty (Tcl 9.1 lseq changed by-0 to return one element). lzipn_tcl9b/lzipn_tcl9c/cols/cols2: lseq expression operands wrapped in expr. check::has_tclbug_safeinterp_compile: Tcl 9.1 safe interps hide tcl::unsupported::* - falls back to interp invokehidden tcl:unsupported:disassemble

13
src/modules/punk/repl-999999.0a1.0.tm

@ -634,12 +634,16 @@ proc repl::start {args} {
#thread busy-loops on the persistent error - an orphaned shell would spin CPU forever.
#Poll liveness on the process console so the repl can finish via the normal eof path.
#Armed only for a tcl9 console channel (-inputmode present) on the process-default
#console; piped/foreign/8.6 inputs are unaffected.
#console; piped/foreign/8.6 inputs are unaffected. The version gate (G-076) is
#punk::lib::check::has_tclbug_console_deadspin - shared with the 'help tcl' warning -
#so runtimes at or past a verified fixed Tcl release (check::tclbug_console_deadspin_fixed_in)
#don't arm the probe.
variable console_watchdog_afterids
variable console_watchdog_ms
set watchdog_chan ""
if {"windows" eq $::tcl_platform(platform) && [console_is_default]
&& ![info exists console_watchdog_afterids($inchan)]} {
&& ![info exists console_watchdog_afterids($inchan)]
&& [dict get [punk::lib::check::has_tclbug_console_deadspin] bug]} {
if {![catch {chan configure $inchan} wdconf] && [dict exists $wdconf -inputmode]} {
set watchdog_chan $inchan
set console_watchdog_afterids($inchan) [after $console_watchdog_ms [list [namespace current]::console_watchdog $inchan]]
@ -983,7 +987,10 @@ namespace eval repl::argdoc {
Armed by repl::start only for a tcl9 console channel (-inputmode present
in the chan configure dict) serving the process-default console on
windows. Scheduling state is kept per channel name in
windows, and only while punk::lib::check::has_tclbug_console_deadspin
reports the runtime affected (G-076 shared version gate - a Tcl release
containing the verified upstream fix for ticket f10d91c2d3 arms nothing).
Scheduling state is kept per channel name in
repl::console_watchdog_afterids; a watchdog whose channel has
disappeared disarms itself silently."
@values -min 1 -max 1

3
src/modules/punk/repl-buildversion.txt

@ -1,6 +1,7 @@
0.5.0
0.5.1
#First line must be a semantic version number
#all other lines are ignored.
#0.5.1 - G-076: repl::start's dead-console watchdog arming now also requires punk::lib::check::has_tclbug_console_deadspin to report the runtime affected (shared version gate with the 'help tcl' warning; requires punk::lib 0.4.2+). Behaviour today is unchanged (gate variable check::tclbug_console_deadspin_fixed_in is empty = every Tcl 9 windows runtime affected); once a released Tcl containing the verified upstream fix for f10d91c2d3 is recorded there, such runtimes stop arming the watchdog.
#0.5.0 - G-039: new repl::console_watchdog - a self-rescheduling liveness poll (default 5s, repl::console_watchdog_ms) armed by repl::start for a tcl9 console input channel (-inputmode present) serving the process-default console on windows. The Tcl 9 windows console driver never delivers a dead console (killed conhost/terminal) to the script level as a fileevent (tclWinConsole.c ConsoleEventProc only notifies on buffered data) and its reader thread busy-loops on the persistent channel error, so an orphaned shell previously spun ~2 cores indefinitely. On a failed probe (chan configure -inputmode = live GetConsoleMode) the watchdog closes the input channel (stopping the driver reader thread) and finishes the repl via the normal eof done-path; app-punkshell's eof handling then finds no console reopenable and exits cleanly. repl::start's post-vwait reader-deregistration now tolerates an inchan closed by the watchdog. Piped/foreign-console/tcl8.6 inputs are unaffected (watchdog not armed).
#0.4.0 - G-001: repl::init -console <spec> selects the console the repl reads/writes ({in out} pair, anchored opunk::console instance name, or ::opunk::Console object value, resolved via punk::console::console_spec_resolve). repl::start's inchan is now optional (defaults to the selected console's input). New repl-level channel state (conin/conout/conerr) is routed through rputs (stdout/stderr mapped per-repl) and doprompt; for a selected foreign console the code interp's stdout/stderr are diverted via shellfilter 'var' junction stacks and emitted to the console after each run. New helpers repl::console_is_default / console_at_eof / console_get_size - eof and size are answered by the selected console object's (possibly overridden) methods. Process-console behaviours (tcl_interactive prompt gating, stdin reopen on eof, raw-mode re-enable, utf-16be windows line re-decode experiment, mode-line on exit) now apply only to the default console. Default-console (stdin/stdout) behaviour unchanged. Also fixes rputs pseudo-channel mapping in the 3-arg -nonewline form (mapped value previously written over the -nonewline flag).
#0.3.0 - G-058: codethread init script receives the runtime static/builtin package baseline (::punkboot::static_prefixes/static_packages via new %staticprefixes%/%staticpackages% scriptmap entries) and seeds ifneeded mappings in the codethread interp, so the code interp (seeded in turn via punk::lib::interp_sync_package_paths) can package-require statically-linked runtime packages - fixes 'can't find package Thread' booting a punk9win.vfs kit on the tclsfe-x64 static runtime (punk91)

43
src/tests/modules/punk/lib/testsuites/lib/checkbugs.test

@ -49,6 +49,46 @@ namespace eval ::testspace {
}\
-result [list 1 1 1 1 1 1 1]
test tclbug_console_deadspin_classifier {platform/tclversion/fixed_in combinations for the tcl9 dead-console defect (G-076)}\
-setup $common -body {
#affected: windows tcl 9, no fixed release known (empty fixed_in)
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 9.0.2 ""]
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 9.1b1 ""]
#not windows
lappend result [punk::lib::check::tclbug_console_deadspin_applies unix 9.0.2 ""]
#tcl 8.6 (different console driver - out of scope per G-039)
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 8.6.16 ""]
#fixed_in recorded: earlier runtimes affected, at/past runtimes clean
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 9.0.3 9.0.4]
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 9.0.4 9.0.4]
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 9.0.5 9.0.4]
lappend result [punk::lib::check::tclbug_console_deadspin_applies windows 9.1.0 9.0.4]
}\
-cleanup {
}\
-result [list 1 1 0 0 1 0 0 0]
test has_tclbug_console_deadspin_consistency {the live check matches the classifier applied to the live facts and gate variable}\
-setup $common -body {
set buginfo [punk::lib::check::has_tclbug_console_deadspin]
foreach key {bug bugref description level mitigated mitigation} {
lappend result [dict exists $buginfo $key]
}
lappend result [dict get $buginfo bugref]
lappend result [expr {
[dict get $buginfo bug] == [punk::lib::check::tclbug_console_deadspin_applies\
$::tcl_platform(platform) [info patchlevel] [set ::punk::lib::check::tclbug_console_deadspin_fixed_in]]
}]
#mitigated axis: boolean, only reportable on a triggered check, and mitigation
#text accompanies mitigated=1
lappend result [string is boolean -strict [dict get $buginfo mitigated]]
lappend result [expr {!([dict get $buginfo mitigated] && ![dict get $buginfo bug])}]
lappend result [expr {!([dict get $buginfo mitigated] && [dict get $buginfo mitigation] eq "")}]
}\
-cleanup {
}\
-result [list 1 1 1 1 1 1 f10d91c2d3 1 1 1 1]
test has_bugcheck_procs_return_standard_dict {every has_tclbug_*/has_libbug_* check returns a dict with a boolean bug key and a level}\
-setup $common -body {
set checkprocs [concat\
@ -69,6 +109,9 @@ namespace eval ::testspace {
if {[dict exists $buginfo level] && [dict get $buginfo level] ni {minor medium major}} {
lappend badprocs [list $bp badlevel [dict get $buginfo level]]
}
if {[dict exists $buginfo mitigated] && ![string is boolean -strict [dict get $buginfo mitigated]]} {
lappend badprocs [list $bp badmitigatedkey]
}
}
lappend result $badprocs
}\

Loading…
Cancel
Save