# G-118 tclsh subcommand review Status: active Scope: src/vfs/_config/punk_main.tcl (tclsh dispatch), src/vfs/_config/project_main.tcl (parity), src/buildsuites/suite_tcl90/patches/ (tclMain piperepl rev items), src/tests/shell/testsuites/punkexe/tclshcmd.test (coverage additions) Goal: The 'tclsh' subcommand has a settled, documented contract - each remaining divergence from stock tclsh and from the sibling 'script' subcommand in the issue list below is either fixed or explicitly recorded as intended, the piperepl patch residue items have decisions (batched into at most one patch rev + suite rebuild), and the console-entry paths have a test-coverage decision. Acceptance: Every item in the "Issue list" section carries a recorded user decision (fix / document-as-intended / wontfix) and the decided fixes and documentation have landed; the tclsh subcommand's user-facing contract is documented in a durable doc location (not TEMP_REFERENCE); tclshcmd.test covers the decided pipe-testable behaviours; and the dorepl/console-reopen path either has a console-driven test or a recorded decision that G-106-style coverage is not required. ## Context Origin: PIPEREPL testing after G-103 (achieved - see goals/archive/G-103-runtime-kit-family.md; user session notes in machine-local TEMP_REFERENCE/piperepl_info.txt) plus the 2026-07-23 agent review of the tclsh dispatch in punk_main.tcl and the tclMain_piperepl_905.c patch. The review deduced and pinned the current contract: src/tests/shell/testsuites/punkexe/tclshcmd.test covers the pipe-testable behaviours, and src/buildsuites/suite_tcl90/patches/README.md carries the corrected patch-side semantics (tcl_interactive early-link divergence, reopen-message residue). Already landed 2026-07-23, prior to this goal (not part of its acceptance): stock leading-dash argument handling ('tclsh - args...' keeps all args in ::argv, stock parity verified against native tclsh 9.0.3), '-encoding name fileName' support (incl. stock fallthrough for incomplete forms), and the lib: refusal pointing at the 'script' subcommand (decision: option (d) - tclsh stays plain, no scriptlib resolution). The subcommand is an extension of standard tclsh behaviour, not an exact emulator; the guiding principle for the items below is least surprise across punk exe subcommands ('tclsh' vs 'script' vs 'shell'). ## Issue list All 11 items dispositioned by the user 2026-07-24 (activation session). Items 1 and 7 need a tclMain piperepl patch rev + suite rebuild and are batched with the G-117 cycle (item 2 stays document-only, no patch change). 1. Reopen message noise: on the eof-with-dorepl console-reopen path the patch unconditionally prints "? reopen stdin from console" to STDOUT (tclMain_piperepl_905.c). Options: drop / move to stderr / env-gate (e.g TCLSH_PIPEREPL_DEBUG). Decision 2026-07-24: FIX - drop the message (stock tclsh prints nothing entering a repl); add a TCLSH_PIPEREPL_DEBUG=1 env gate re-enabling patch diagnostics. Patch rev, batched with the G-117 cycle. 2. tcl_interactive read divergence (gate open): a script-arg launch from a CONSOLE reads ::tcl_interactive as 1 where stock tclsh reads 0, because the patch links it to the tty flag before the startup script (the early link is load-bearing - it is how a piped script that sets dorepl opts into prompts/echo). Options: accept + document (current state, recorded in patches/README.md) / patch rev that preserves stock read semantics for script-arg launches while keeping the write lever. Decision 2026-07-24: ACCEPT + DOCUMENT - patches/README.md wording stands; no patch change; console dorepl sessions keep their automatic prompts. 3. argv0 in no-script forms: piped/console no-script runs leave ::argv0 as the kit boot script (e.g //zipfs:/app/main.tcl). Stock tclsh: the exe invocation path; 'script' subcommand stdin form: "-". Decide the value and set it in the no-script branch of the tclsh dispatch. Decision 2026-07-24: FIX - set ::argv0 to the executable path ([info nameofexecutable]) in both no-script paths (stock parity). 4. Missing script file: 'tclsh ' dumps the raw errorInfo trace, exposing punk_main lambda internals. The 'script' subcommand prints a clean not-found message and exits 1. Decide whether the tclsh subcommand gets the same file-exists pre-check (source errors from an existing script should keep their full trace - only the not-found case cleans up). Decision 2026-07-24: FIX - file-exists pre-check with a clean not-found message and exit 1; existing-file source errors keep the full trace. 5. Unpatched-runtime console no-arg: 'punk tclsh' with console stdin on a kit whose runtime lacks the piperepl patch blocks in 'read stdin' until ^Z, then evals the typed text. The 'script' subcommand fails fast on terminal stdin with a usage error (terminal probe via chan configure -inputmode/-mode, app-punkscript precedent). Decide fail-fast vs current block-and-eval. Also: the TCLSH_PIPEREPL=0 dispatch path emits leftover debug lines (tcl_interactive value) - clean up. Decision 2026-07-24: FIX - fail fast on terminal stdin with a usage-style stderr message (app-punkscript terminal probe); delete the TCLSH_PIPEREPL=0 debug lines. Piped degraded-mode behaviour (notice + eval) unchanged. 6. Piped no-arg eval semantics: the dispatch reads stdin to eof then evals the whole buffer - no output until eof on slow pipes, and a mid-stream error aborts the remainder with exit 1. Stock tclsh streams per command-complete chunk, continues after errors, and exits 0. Current behaviour is 'script'-subcommand-coherent (honest exit codes); decide document-as-intended-extension vs emulating stock streaming. Decision 2026-07-24: DOCUMENT-AS-INTENDED (honest exit codes; deliberate extension, not stock emulation). 7. ::tclsh(reopened) is C-internal and unpublished - Tcl code can only infer that the console reopen happened via 'info exists ::tclsh(inputbuffer)'. Consider publishing it in the same patch rev as item 1. Decision 2026-07-24: FIX - publish ::tclsh(reopened) in the same patch rev as item 1 (G-117 cycle). 8. ::tclsh(istty) is read-only by convention only: a script write corrupts the Tcl-visible launch fact (C behaviour is unaffected - the variable is deliberately unlinked). Options: document-only / a readonly variable trace installed by the dispatch. Decision 2026-07-24: DOCUMENT-ONLY (a dispatch-installed trace would not protect bare patched-runtime launches - inconsistent protection not worth it). 9. project_main.tcl parity: mirror the punk_main.tcl tclsh dispatch (piperepl no-arg branch, leading-dash/-encoding forms, lib: refusal) into project_main.tcl and decide how the project-layout copies under src/project_layouts pick it up, so generated-project kits carry the same contract. Decision 2026-07-24: FIX in this work - mirror the dispatch and refresh the layout _config copies (byte-sync; layout-refresh precedent from G-097, achieved - see goals/archive/G-097-bin-utility-naming-sweep.md). The mirrored dispatch is deliberate interim duplication across punk_main/project_main + layout copies; G-031's componentized boot core is the consolidation path. 10. Documentation home: the tclsh subcommand contract (and the launch subcommand family generally: tclsh/script/shell/punk/shellspy) needs durable user-facing documentation; punk::args definitions are the intended doc source of truth. TEMP_REFERENCE/piperepl_info.txt is machine-local source material only. Decision 2026-07-24 (refined same day after G-032 overlap review): punk::args-backed launch-subcommand-family documentation authored as G-032-COMPATIBLE punk::args definitions (subcommand enumeration + contract prose as definition help text) - the definitions ARE the doc home; -help rendering, option parsing and degradation wiring stay with G-032 (cross-referenced in its Notes). 11. Console-path test coverage: the dorepl/console-reopen path and console repl entry are deliberately untested by the piped harness (tclshcmd.test hang rule: nothing may set ::tclsh(dorepl)). Decide whether to build a G-106-style hidden-console driven test (AttachConsole + WriteConsoleInput) for these paths. Decision 2026-07-24: FIX - one env-gated G-106-recipe console-driven test for the dorepl/console-reopen path (normal runs skip). ## Notes - Drafted 2026-07-23 at user request; input provided and goal activated 2026-07-24 alongside G-117 (shared rebuild cycle - see G-117 Progress for the cycle sequencing: patch rev + v1 embedding -> r2 re-emission -> family_check -> publication). - Behaviour references for the review: patches/README.md (patch-side contract incl. gate policy and known divergences), src/tests/shell/AGENTS.md tclshcmd.test bullet (pinned pipe-testable contract), stock comparisons verified against native tclsh 9.0.3 (2026-07-23: leading-dash keeps all args in ::argv with argv0 = exe path; script-arg runs read tcl_interactive 0). - Patched-runtime kits in bin/ as of 2026-07-23: punk9_beta.exe, punk9bi_beta.exe (punk902z/punk905/punk91 runtimes are unpatched; all carry the current dispatch after the 2026-07-23 rebuild). - G-077 cross-note (2026-07-24 overlap review): the leading-dash stock fallthrough means ` tclsh -e ...` reproduces stock tclsh's -e misparse WITHIN the tclsh subcommand - intended (stock parity is this subcommand's contract); the one-liner instinct is served by G-077's top-level and `script` subcommand `-e` forms, and the claude-harness PreToolUse hook already denies bare-tclsh `-e` invocations. The item-10 definitions should state this boundary. ## Progress - 2026-07-24: all 11 items dispositioned (Decision lines inline above); goal activated alongside G-117. - 2026-07-24 items 3/4/5/9 LANDED (punkproject 0.20.0): no-script forms set ::argv0 to [info nameofexecutable] (both repl and piped-eval paths); missing script file pre-checked with clean not-found + exit 1; machinery-absent console stdin fails fast via the app-punkscript terminal probe (piped degraded eval unchanged; patch-missing notice reworded to "note:", deliberate TCLSH_PIPEREPL=0 opt-out now quiet, old debug lines deleted); project_main.tcl carries the full mirrored dispatch (script-arg/dash/-encoding/lib: forms - previously single-arg-only with stale gate default) and both layout _config copies byte-synced. Verified: all mapvfs kits rebuilt; tclshcmd.test extended to 19 (tclsh_noscript_argv0, tclsh_script_notfound); shell/*** suite 64 pass / 2 goal-gated skips / 0 fail. - 2026-07-24 overlap reconciliation (user-prompted review): cross-references recorded with G-025 (kit-layer stamp counterpart - see G-117 Notes for the record-survives-kit-wrap + filename-namespacing considerations), G-032 (item 10 refined: docs authored as G-032-compatible punk::args definitions), G-031 (item 9 duplication interim), G-077 (tclsh -e boundary note). - Remaining for acceptance: patch-rev items (1, 7) via the G-117 suite rebuild cycle; item 10 definitions (G-032-compatible) also carrying the documented-as-intended records (items 2, 6, 8) and the G-077 boundary; console-driven test (item 11); ARCHITECTURE.md tclsh-subcommand bullet rides the item-10 docs change-set.