You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

14 KiB

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 shipped via the tclMain piperepl patch rev in the G-117 cycle (G-117 achieved 2026-07-24 - see goals/archive/G-117-self-describing-runtimes.md); 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 <punkexe> 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).
  • 2026-07-24 items 1 + 7 LANDED and verified (G-117 cycle suite rebuild): reopen notice now TCLSH_PIPEREPL_DEBUG-gated with all reopen-path messages on stderr; ::tclsh(reopened) published (family_check gate probe asserts 0 at gate-open start / absent on plain+opt-out on all three family members; tclshcmd.test tclsh_pr_noarg_machinery pins it against the rebuilt punk9_beta). The console-reopen FLIP to 1 and the notice's console-side silence remain item-11 console-harness verification.
  • 2026-07-25 item 10 LANDED (punkproject 0.20.1): launch-subcommand-family documentation authored as G-032-compatible punk::args definitions in NEW module punk::args::moduledoc::punkexe (0.1.0; deferred PUNKARGS registration, argdoc child, make.tcl (script)::punkboot id convention): (script)::punkexe top-level (package-mode leader documented in help text, subcommand enumeration with choicelabels, default script/shell reclassification) + (script)::punkexe::tclsh full contract (multi-form scriptfile/stdin with explicit synopses) + summary-level script/shell/punk/shellspy definitions. The tclsh definition carries the documented-as-intended records: item 2 (early tcl_interactive link + load-bearing write path), item 6 (whole-buffer piped eval with honest exit codes on machinery-less runtimes, per-command C repl loop with machinery), item 8 (istty read-only by convention, no trace), and the G-077 boundary (no -e one-liner - deliberate stock parity inside this subcommand). Supporting work: punk::args 0.12.7 update_definitions handles tag-prefixed id namespaces first-class (namespace_docpackages consulted with the tagged key; punk::args::usage on (script)::* ids no longer errors no-such-id; spurious unqualified-ns stderr warning gone - args/ns/repl suites 352 pass 0 fail); punk::repl 0.5.3 loads the module catch-guarded at both code-interp setup sites (docs queryable in any punk shell); ARCHITECTURE.md boot/launch tclsh-subcommand bullet added (architecture_lint clean). Verified: usage-first render in fresh interp (real_id path), src + rebuilt-kit (punk902z script probe: 0.1.0, 5298-char tclsh render) + repl-wired (piped punk session renders (script)::punkexe without manual require).
  • 2026-07-25 item 11 LANDED: env-gated console-driven coverage of the dorepl console-reopen path (tclshcmd.test tests tclsh_cr_reopen_state + tclsh_cr_reopen_debug_notice, constraint punkconsole = windows + env PUNK_TEST_CONSOLE=1 so normal runs skip; new committed helper src/tests/testsupport/consoleinject.ps1 = the G-106 recipe's injection half: FreeConsole/AttachConsole + CONIN$ WriteConsoleInput key events). Harness shape: outer powershell Start-Process -WindowStyle Hidden gives an inner wrapper its own fresh console; the wrapper runs ' tclsh' there with std handles redirected to files; piped code sets dorepl; injection types the verification lines + exit 0. Verified live against punk9_beta (PUNK_TEST_CONSOLE=1 run 21/21): CRPHASE1 0 0 1 (istty 0, reopened 0, dorepl set) -> CRPHASE2 0 1 1 (reopened FLIPPED to 1, istty still 0 - immutable launch fact) + CRPHASE2BUF 0 (inputbuffer published empty), stderr free of the reopen notice by default, notice present on STDERR only (not stdout) under TCLSH_PIPEREPL_DEBUG=1, injected exit gives exit code 0. This closes the item-1/7 console-side residue (notice silence + reopened flip). Hang-proofing: deadlines + taskkill /T on the test's own wrapper tree; the piped-harness HANG RULE stands for non-gated tests (tclshcmd.test header + src/tests/shell/AGENTS.md updated; consoleinject.ps1 indexed in src/tests/AGENTS.md).
  • Remaining for acceptance: none - all 11 items dispositioned and landed/documented.