diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ea2667..04515850 100644 --- a/CHANGELOG.md +++ b/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.9.0] - 2026-07-11 + +- G-001 (in progress) increment 1: three pluggable `::opunk::Console` backend modules, with the base class and punk::console untouched — `opunk::console::test` (`::opunk::TestConsole`: deterministic channel-pair test double with fixed size and probe-free eof — the console seam the repl/editbuf characterization work needs), `opunk::console::ssh` (`::opunk::SshConsole`: socket-carried terminal sessions — construction-time capability, chan-eof without byte-consuming probes, and size resolved by punk::console's ANSI cursor-report provider *querying over the connection*, proven against a scripted remote terminal answering `CSI 6n` over a socket pair), and `opunk::console::tk` (`::opunk::TkConsole`: a Tk text widget as terminal — widget char dimensions as size, backend eof marker via `opunk::console::tk::set_eof`, verified live under the tk-capable punk91 kit). Subclass values dispatch through existing base-class holders and `console_spec_resolve` virtually. Remaining for the goal: repl launch-time console selection and output-channel parameterization. + ## [0.8.2] - 2026-07-11 - G-054 achieved: tclcore moduledoc 0.2.0 — the `string is` class choices shown by `i string is` (and the per-class docids like `i string is digit`) are now harvested from the running interpreter at define time instead of a hand-maintained list, so the documented/parsed class set always matches what the interpreter accepts (previously the static Tcl 9.0 list wrongly accepted `dict` under 8.6 and rejected 8.7's `unicode`). Version notes render on classes that differ across releases; unrecognized future classes get a generic label instead of vanishing. Behavioural parity pinned by `tclcoreparity.test` with expectations derived from the live interpreter — green on Tcl 8.6.13, 8.7a6 and 9.0.3. diff --git a/GOALS.md b/GOALS.md index 43742f3d..50195132 100644 --- a/GOALS.md +++ b/GOALS.md @@ -53,7 +53,7 @@ When the user asks to "write a goal for X" or "help me draft a goal for Y", do t -### G-001 [proposed] Pluggable console backends for non-detectable terminals +### G-001 [active] Pluggable console backends for non-detectable terminals Scope: src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/lib/app-punkshell/punkshell.tcl Detail: goals/G-001-pluggable-console-backends.md Goal: an interactive REPL can be launched against a non-detectable terminal-like device (ssh channel, tk text widget) via an ::opunk::Console subclass, with no edits to the base class or punk::console. diff --git a/goals/G-001-pluggable-console-backends.md b/goals/G-001-pluggable-console-backends.md index 69e09915..180f691b 100644 --- a/goals/G-001-pluggable-console-backends.md +++ b/goals/G-001-pluggable-console-backends.md @@ -44,3 +44,51 @@ Then add launch-time console selection so a REPL or subshell can be started agai - The base class `at_eof` does a non-blocking 1-byte probe on pipe-like channels and parks the byte in `waiting_chunks_waiting`. The ssh subclass should override `at_eof` to use `chan eof` on the ssh channel directly — probing a socket would consume a byte the protocol layer may need. - The "first subshell asymmetry" TODO at `repl-999999.0a1.0.tm:3130-3132` is G-002's concern, not G-001's, but G-001's launch-time console selection is a prerequisite for G-002's "target a named console" acceptance criterion. Sequence G-001 before G-002. - No persisted prior chat on this topic was found in project sessions; the motivation comes from the existing class design comments and the user's stated intent. + +## Progress (activated 2026-07-11) + +Additional context since authoring: the G-044-detail repl characterization work (2026-07-11) +identified this goal as the ENABLING refactor for repl/editbuf unit testing - class_editbuf +is console-coupled and needs a deterministic console double. A third reference subclass +(the test double) was therefore added ahead of ssh/tk in the build order. + +### Landed (increment 1) + +Three backend modules under src/modules/opunk/console/, base ::opunk::Console and +punk::console UNCHANGED (verified - no diffs): + +- `opunk::console::test` / ::opunk::TestConsole - deterministic channel-pair double: + fixed size (-columns/-rows at construction, stored in the inherited default-size + field), is_console_or_tty/can_respond 1 (settled wins), at_eof = plain chan eof with + NO probe (a pending byte is never consumed - pinned by test). +- `opunk::console::ssh` / ::opunk::SshConsole - per the Approach: construction-time + capability, chan-eof without probe, size via the registered size_query_provider. + Flagship verification: with a scripted "remote terminal" answering CSI 6n over a + socket pair, ::opunk::Console::size on the subclass value resolves 100x30 through + punk::console's ANSI cursor-report provider QUERYING OVER THE SOCKET - the entire + value proposition proven without a real ssh connection. +- `opunk::console::tk` / ::opunk::TkConsole - widget path in the in/out slots, + terminal_class tk-text, size from widget char dims, at_eof via backend marker + (::opunk::console::tk::set_eof) or widget destruction; no Tk require at module load. + Verified live under punk91 src (the tk-loading experiment kit): size/eof + flag/clear/destroy all correct. + +voo -extends findings recorded in src/modules/opunk/AGENTS.md: child classes inherit +public accessors + field INDEX variables but not parent-private my.* accessors - +subclass constructors initialise inherited private fields via the index variables; +method bodies use the parent's public accessor methods. + +Tests: src/tests/modules/opunk/console/testsuites/console/backends.test (8 tests; +7 green + tk env-gated on tcl 9.0.3 and 8.7; tk case verified under punk91). + +### Remaining for acceptance + +- repl launch wiring: -console at repl::init (resolve via console_spec_resolve), + repl::start default input from the console, and OUTPUT parameterization - rputs/ + doprompt (and the code-interp channel shares) are stdout/stderr-hardwired today; + routing them via repl-level channel variables derived from the selected console is + the remaining engineering (repl.tm and app-punkshell are in scope for edits). +- Interactive-repl-through-backend verification for the ssh and tk cases (the + acceptance sentence). The tk case pairs naturally with the punkwish91 textwidget + virtual-console exploration (mapvfs notes); the ssh case can be simulated with the + scripted-remote-terminal socket technique before a real sshd session. diff --git a/punkproject.toml b/punkproject.toml index 1bff70bb..739b3f89 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,3 +1,3 @@ [project] name = "punkshell" -version = "0.8.2" +version = "0.9.0" diff --git a/src/modules/opunk/AGENTS.md b/src/modules/opunk/AGENTS.md index 964a4f09..855d162d 100644 --- a/src/modules/opunk/AGENTS.md +++ b/src/modules/opunk/AGENTS.md @@ -20,6 +20,8 @@ Modules under the `opunk::*` namespace explore value-based class implementations - Query-based mechanisms enter the class the same way: `::opunk::console::size_query_provider` is a command prefix (invoked with the console object) the base `size` method consults after the -winsize fast path and capability gating; punk::console registers `console_size_provider` via `ensure_object_integration`. Non-channel subclasses override the relevant `-virtual` methods and never touch providers. - Anchor lifecycle is observable the same way: `::opunk::console::lifecycle_callback` is a command prefix invoked as `{*}$cb created|forgotten {in out}` after `create` anchors / `forget` releases an instance; punk::console registers its handler via `ensure_object_integration` to maintain the G-007 console ownership registry. Empty by default; callback errors propagate to the create/forget caller. - Method-entry validity guards use `[llength $this] < N` (not `!= N`) so field-adding subclasses keep working. +- Subclassing (`voo::class -extends ::opunk::Console {...}`): the child inherits public accessors and the field INDEX variables, but not the parent's private `my.*` accessors - a subclass CONSTRUCTOR initialises inherited private fields via the index variables (`variable o_in` then `lset obj $o_in $value` - the sanctioned exception to the avoid-`variable o_` rule, which still applies in method bodies where the parent's public accessor METHODS should be used instead, e.g `[::opunk::Console::in $this]`). Overridden `-virtual` methods are plain `method` declarations in the child; base-class calls dispatch to them via the slot-0 tag. +- G-001 backend subclasses (console/ subfolder): `opunk::console::test` (`::opunk::TestConsole` - deterministic channel-pair test double: fixed size, no probes; the console seam for repl/editbuf characterization), `opunk::console::ssh` (`::opunk::SshConsole` - socket-carried terminal sessions: construction-time capability, chan-eof without probe, size via the ANSI size-query provider over the connection), `opunk::console::tk` (`::opunk::TkConsole` - Tk text widget as terminal: widget path in the in/out slots, size from widget char dims, backend eof marker via `::opunk::console::tk::set_eof`; no Tk package require at load). Tests: `src/tests/modules/opunk/console/testsuites/console/backends.test` (tk case gated behind env `PUNK_TEST_TK=1` - loading Tk into the shared runtests testinterp has event-loop side effects). - Module filenames use the `-999999.0a1.0.tm` magic suffix with `-buildversion.txt` per parent conventions. - `opunk::console` (with its dependency `voo`) is included in bootsupport via `src/bootsupport/modules/include_modules.config` for eventual boot/repl-startup console objects; after releasing a new version, rebuild with `make.tcl modules` then refresh the snapshot with `make.tcl bootsupport`. diff --git a/src/modules/opunk/console/ssh-999999.0a1.0.tm b/src/modules/opunk/console/ssh-999999.0a1.0.tm new file mode 100644 index 00000000..45cc68fc --- /dev/null +++ b/src/modules/opunk/console/ssh-999999.0a1.0.tm @@ -0,0 +1,111 @@ +# -*- tcl -*- +# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt +# +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# (C) 2026 +# +# @@ Meta Begin +# Application opunk::console::ssh 999999.0a1.0 +# Meta platform tcl +# Meta license BSD +# @@ Meta End + +package require Tcl 8.6- +package require voo +package require opunk::console + +#opunk::SshConsole - an ::opunk::Console backend for terminal sessions carried over a +#socket-like channel pair (G-001) - the ssh-channel case: a remote user's terminal is on +#the other end of the connection, but the local channel is a plain socket exposing none +#of the signals terminal detection relies on (-inputmode/-mode, twapi handles, env hints). +# +#The backend is CONSTRUCTED knowing the far end is a terminal, so: +# - is_console_or_tty: always 1 (explicit construction-time knowledge, not detection) +# - can_respond: settled value if set, else 1 (the remote terminal answers ANSI +# queries carried over the connection) +# - at_eof: plain [chan eof] on the channel - deliberately NO base-class +# pipe-probe: probing a socket would consume a byte the protocol +# layer/reader needs (see goals/G-001 detail) +# - size: the registered ::opunk::console::size_query_provider when +# available (punk::console's ANSI cursor-report mechanisms operate +# over the channel pair like any other console), else the +# configured default size. No -winsize attempt (sockets have none). +# +#No edits to the base ::opunk::Console or punk::console are needed (G-001 acceptance). + +voo::class ::opunk::SshConsole -extends ::opunk::Console { + public { + method is_console_or_tty {} { + return 1 + } + method can_respond {} { + set settled [get.o_can_respond $this] + if {$settled != -1} { + return $settled + } + return 1 + } + method at_eof {} { + set in [::opunk::Console::in $this] + if {[catch {chan eof $in} is_eof]} { + return 1 ;#closed/invalid channel - unusable + } + return $is_eof + } + method size {} { + if {![can_respond $this]} { + return [::opunk::Console::default_size $this] + } + if {$::opunk::console::size_query_provider ne ""} { + if {![catch {{*}$::opunk::console::size_query_provider $this} sized]} { + if {[dict size $sized] && [string is integer -strict [dict get $sized columns]] && [string is integer -strict [dict get $sized rows]]} { + return $sized + } + } + } + return [::opunk::Console::default_size $this] + } + } + + constructor {in out} { + #in/out: the channel pair of the connection (a single socket channel may be + #passed for both). Parent fields are private - set via imported index variables. + variable o_in + variable o_out + set obj [new()] + lset obj $o_in $in + lset obj $o_out $out + return $obj + } +} +namespace eval ::opunk::SshConsole { + namespace ensemble create +} + +tcl::namespace::eval ::opunk::console::ssh { + tcl::namespace::export {[a-z]*} + variable PUNKARGS + + lappend PUNKARGS [list { + @id -id "(package)opunk::console::ssh" + @package -name "opunk::console::ssh" -help\ + "voo class ::opunk::SshConsole - an ::opunk::Console backend for terminal sessions + carried over socket-like channels (e.g an ssh connection): constructed knowing the + far end is a terminal, eof via chan eof (no byte-consuming probe), size via the + registered ANSI size-query provider over the connection (G-001)." + }] +} + +namespace eval ::punk::args::register { + #use fully qualified so 8.6 doesn't find existing var in global namespace + lappend ::punk::args::register::NAMESPACES ::opunk::console::ssh ::opunk::SshConsole +} + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Ready +package provide opunk::console::ssh [tcl::namespace::eval ::opunk::console::ssh { + variable pkg opunk::console::ssh + variable version + set version 999999.0a1.0 +}] +return diff --git a/src/modules/opunk/console/ssh-buildversion.txt b/src/modules/opunk/console/ssh-buildversion.txt new file mode 100644 index 00000000..12bddbe4 --- /dev/null +++ b/src/modules/opunk/console/ssh-buildversion.txt @@ -0,0 +1,4 @@ +0.1.0 +#First line must be a semantic version number +#all other lines are ignored. +#0.1.0 - G-001: initial ::opunk::Console backend subclass diff --git a/src/modules/opunk/console/test-999999.0a1.0.tm b/src/modules/opunk/console/test-999999.0a1.0.tm new file mode 100644 index 00000000..15b93cf2 --- /dev/null +++ b/src/modules/opunk/console/test-999999.0a1.0.tm @@ -0,0 +1,120 @@ +# -*- tcl -*- +# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt +# +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# (C) 2026 +# +# @@ Meta Begin +# Application opunk::console::test 999999.0a1.0 +# Meta platform tcl +# Meta license BSD +# @@ Meta End + +package require Tcl 8.6- +package require voo +package require opunk::console + +#opunk::TestConsole - a scripted/deterministic ::opunk::Console backend (G-001). +# +#The first client of the pluggable-backend seam and the console TEST DOUBLE the repl +#characterization work needs (see goals/G-044 detail - class_editbuf and raw-mode +#behaviours are console-coupled and cannot be unit-tested against a live terminal). +# +#Wraps a caller-supplied channel pair (typically [chan pipe] ends or reflected channels +#owned by a test harness) and answers the capability/size questions DETERMINISTICALLY: +# - is_console_or_tty: always 1 (constructed knowing it plays the terminal role) +# - can_respond: settled value if set, else 1 +# - at_eof: plain [chan eof] on the input channel - NEVER probes (a probe +# would consume a byte the test harness scripted) +# - size: the configured size - no -winsize, no query emission, no +# size_query_provider consultation. Set at construction +# (-columns/-rows) and stored in the inherited o_default_size. +# +#No edits to the base ::opunk::Console or punk::console are needed (G-001 acceptance): +#values carry this class's namespace tag at slot 0 and existing holders (e.g +#punk::console::console_spec_resolve callers) dispatch to these overrides virtually. + +voo::class ::opunk::TestConsole -extends ::opunk::Console { + public { + method is_console_or_tty {} { + return 1 + } + method can_respond {} { + set settled [get.o_can_respond $this] + if {$settled != -1} { + return $settled + } + return 1 + } + method at_eof {} { + set in [::opunk::Console::in $this] + if {[catch {chan eof $in} is_eof]} { + return 1 ;#closed/invalid channel - unusable + } + return $is_eof + } + method size {} { + return [::opunk::Console::default_size $this] + } + } + + constructor {in out args} { + #args: optional -columns -rows (default 80x24 from the base field). + #Parent fields are private, so their imported INDEX variables are used to set + #slots on the child-tagged default value (the voo -extends pattern for + #initialising inherited private fields). + variable o_in + variable o_out + variable o_default_size + set obj [new()] + lset obj $o_in $in + lset obj $o_out $out + set size [lindex $obj $o_default_size] + foreach {k v} $args { + switch -- $k { + -columns { + dict set size columns $v + } + -rows { + dict set size rows $v + } + default { + error "opunk::TestConsole::new unknown option '$k'. Known options: -columns -rows" + } + } + } + lset obj $o_default_size $size + return $obj + } +} +namespace eval ::opunk::TestConsole { + namespace ensemble create +} + +tcl::namespace::eval ::opunk::console::test { + tcl::namespace::export {[a-z]*} + variable PUNKARGS + + lappend PUNKARGS [list { + @id -id "(package)opunk::console::test" + @package -name "opunk::console::test" -help\ + "voo class ::opunk::TestConsole - a scripted/deterministic ::opunk::Console backend + wrapping a caller-supplied channel pair (e.g chan pipe ends) with fixed capability + answers and a configured size. The console test double for harness-driven repl and + editbuf testing (G-001)." + }] +} + +namespace eval ::punk::args::register { + #use fully qualified so 8.6 doesn't find existing var in global namespace + lappend ::punk::args::register::NAMESPACES ::opunk::console::test ::opunk::TestConsole +} + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Ready +package provide opunk::console::test [tcl::namespace::eval ::opunk::console::test { + variable pkg opunk::console::test + variable version + set version 999999.0a1.0 +}] +return diff --git a/src/modules/opunk/console/test-buildversion.txt b/src/modules/opunk/console/test-buildversion.txt new file mode 100644 index 00000000..12bddbe4 --- /dev/null +++ b/src/modules/opunk/console/test-buildversion.txt @@ -0,0 +1,4 @@ +0.1.0 +#First line must be a semantic version number +#all other lines are ignored. +#0.1.0 - G-001: initial ::opunk::Console backend subclass diff --git a/src/modules/opunk/console/tk-999999.0a1.0.tm b/src/modules/opunk/console/tk-999999.0a1.0.tm new file mode 100644 index 00000000..a40ec2f3 --- /dev/null +++ b/src/modules/opunk/console/tk-999999.0a1.0.tm @@ -0,0 +1,154 @@ +# -*- tcl -*- +# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from -buildversion.txt +# +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +# (C) 2026 +# +# @@ Meta Begin +# Application opunk::console::tk 999999.0a1.0 +# Meta platform tcl +# Meta license BSD +# @@ Meta End + +package require Tcl 8.6- +package require voo +package require opunk::console +#NOTE: deliberately NO 'package require Tk' here - the class definition is loadable +#without Tk; construction errors usefully if the widget doesn't exist. This keeps the +#backend requirable in non-GUI processes (introspection, docs) and leaves Tk loading +#policy to the application (punk runtimes load Tk as an extension - punkbin convention). + +#opunk::TkConsole - an ::opunk::Console backend for a Tk text widget acting as a +#terminal (G-001). A widget is not a channel at all, so every channel-shaped base +#method is overridden - none of the channel/provider logic applies (the class comment +#in opunk/console anticipates exactly this subclass). +# +# - o_in/o_out carry the WIDGET PATH (documented reuse of the inherited channel slots - +# holders calling in/out/channels get the widget path; a non-channel consumer must +# know its backend, which is what is_console_or_tty/terminal_class are for) +# - is_console_or_tty: always 1 (constructed as a terminal) +# - can_respond: settled value if set, else 1 (the widget side can answer - +# whatever integrating layer renders to the widget also responds) +# - at_eof: a backend eof MARKER, not a channel eof: 1 when the widget no +# longer exists (winfo exists - requires Tk loaded) or when the +# integrating layer has flagged eof via ::opunk::console::tk::set_eof +# (e.g the user closed the console frame but the widget remains) +# - size: the widget's character dimensions ([$w cget -width/-height] - +# the text widget's requested size in chars), falling back to the +# inherited default size if the widget can't be queried. +# +#No edits to the base ::opunk::Console or punk::console are needed (G-001 acceptance). + +tcl::namespace::eval ::opunk::console::tk { + #backend eof markers keyed by widget path - set via set_eof, consulted by at_eof + variable eof_flags + if {![array exists eof_flags]} { + array set eof_flags {} + } +} + +voo::class ::opunk::TkConsole -extends ::opunk::Console { + public { + method is_console_or_tty {} { + return 1 + } + method can_respond {} { + set settled [get.o_can_respond $this] + if {$settled != -1} { + return $settled + } + return 1 + } + method at_eof {} { + set w [::opunk::Console::in $this] + if {[info exists ::opunk::console::tk::eof_flags($w)] && $::opunk::console::tk::eof_flags($w)} { + return 1 + } + if {[catch {winfo exists $w} exists]} { + #Tk not loaded (or not usable) in this interp - the widget cannot be + #driven from here; report eof rather than pretending liveness + return 1 + } + return [expr {!$exists}] + } + method size {} { + set w [::opunk::Console::in $this] + if {![catch {list [$w cget -width] [$w cget -height]} wh]} { + lassign $wh cols rows + if {[string is integer -strict $cols] && [string is integer -strict $rows] && $cols > 0 && $rows > 0} { + return [dict create columns $cols rows $rows] + } + } + return [::opunk::Console::default_size $this] + } + } + + constructor {widget} { + #widget: path of a Tk text widget playing the terminal role. Stored in both the + #inherited in/out slots (documented non-channel reuse). Parent fields are + #private - set via imported index variables. + variable o_in + variable o_out + variable o_terminal_class + set obj [new()] + lset obj $o_in $widget + lset obj $o_out $widget + lset obj $o_terminal_class tk-text + return $obj + } +} +namespace eval ::opunk::TkConsole { + namespace ensemble create +} + +tcl::namespace::eval ::opunk::console::tk { + tcl::namespace::export {[a-z]*} + variable PUNKARGS + + #flag (or clear) backend eof for a widget-backed console - e.g when the hosting + #frame/toplevel is being torn down but holders may still consult the console value + proc set_eof {widget {value 1}} { + variable eof_flags + set eof_flags($widget) [expr {bool($value)}] + return + } + + lappend PUNKARGS [list { + @id -id "(package)opunk::console::tk" + @package -name "opunk::console::tk" -help\ + "voo class ::opunk::TkConsole - an ::opunk::Console backend wrapping a Tk text + widget acting as a terminal: size from the widget's character dimensions, eof via + a backend marker (::opunk::console::tk::set_eof) or widget destruction - a + non-channel subclass overriding all channel-shaped base methods (G-001)." + }] + lappend PUNKARGS [list { + @id -id ::opunk::console::tk::set_eof + @cmd -name opunk::console::tk::set_eof\ + -summary\ + "Flag or clear backend eof for a widget-backed console."\ + -help\ + "Sets the backend eof marker consulted by ::opunk::TkConsole at_eof. + Use when the hosting frame/toplevel is being torn down (or reopened) while + holders may still consult console values wrapping the widget." + @leaders -min 1 -max 1 + widget -type string -help\ + "Tk widget path the console wraps" + @values -min 0 -max 1 + value -type boolean -default 1 -optional 1 -help\ + "1 to flag eof (default), 0 to clear" + }] +} + +namespace eval ::punk::args::register { + #use fully qualified so 8.6 doesn't find existing var in global namespace + lappend ::punk::args::register::NAMESPACES ::opunk::console::tk ::opunk::TkConsole +} + +# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +## Ready +package provide opunk::console::tk [tcl::namespace::eval ::opunk::console::tk { + variable pkg opunk::console::tk + variable version + set version 999999.0a1.0 +}] +return diff --git a/src/modules/opunk/console/tk-buildversion.txt b/src/modules/opunk/console/tk-buildversion.txt new file mode 100644 index 00000000..12bddbe4 --- /dev/null +++ b/src/modules/opunk/console/tk-buildversion.txt @@ -0,0 +1,4 @@ +0.1.0 +#First line must be a semantic version number +#all other lines are ignored. +#0.1.0 - G-001: initial ::opunk::Console backend subclass diff --git a/src/tests/modules/AGENTS.md b/src/tests/modules/AGENTS.md index c2fba67e..b6c1341a 100644 --- a/src/tests/modules/AGENTS.md +++ b/src/tests/modules/AGENTS.md @@ -37,6 +37,7 @@ Unit tests for editable source modules under `src/modules/`, `src/modules_tcl8/` ## Child DOX Index +- `opunk/console/` — ::opunk::Console backend subclass tests (`testsuites/console/backends.test`, G-001): virtual dispatch of subclass overrides through base-class calls and punk::console::console_spec_resolve (both unchanged), TestConsole determinism + probe-free at_eof, SshConsole capability/eof + the flagship size-via-ANSI-query-over-socket case (a scripted remote terminal answers CSI 6n), TkConsole widget size/eof (gated behind env PUNK_TEST_TK=1 - Tk in the shared testinterp has side effects; also verifiable standalone under a tk-capable kit e.g `punk91 src