- commented out five development trace puts in the doc-lookup machinery:
"PROC auto def"/"ENSEMBLE auto def" (generate_autodef) were emitted on STDOUT
and polluted 'i'/'s' output in script/exec contexts (agent-facing per the
G-015 piped-call pattern); "cmd_traverse - skipping to documented subcommand"
(space-form id path), "---> cmd_traverse ensembleparam" (ensemble -parameters
traversal) and "cmd_traverse 10 ... - review" (fallthrough return) were
stderr noise. No functional change.
- cmdflow.test: 8 new tests characterizing ensemble -parameters handling in the
doc-lookup flow (the punk::netbox::man pattern - leading apicontextid before
the subcommand at every ensemble level):
* generate_autodef models each -parameters entry as a -ensembleparameter 1
leader with @leaders min/max = nparams+1 (single- and two-param fixtures)
* cmd_traverse consumes parameter words and resolves the subcommand's
explicit or autodef docid, re-presenting the parameter in args_remaining
so the subcommand's own leader-based definition (as punk::netbox::man's
explicit defs model the curried parameter) can parse it
* nested sub-ensembles with their own -parameters re-consume the curried
parameter at each level
* punk::args::parse treats -ensembleparameter leaders as ordinary named
leaders; cmdhelp renders a documented subcommand's usage through the
parameter (end-to-end smoke)
- suites: punk/ns 26 pass (was 18), punk/args 93 pass + 1 skip (unchanged)
- project 0.4.6 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- fixed "@opts -any 1" (arbitrary/adhoc option passthrough): an option not in
the definition crashed get_dict with "can't read argname" at the name-mapping
stage - adhoc opts now fall back to their raw supplied name, under which
argstate/arg_checks were already populated at scan time (real consumers:
vendored tomlish definitions declare -any 1)
- commented out a debug puts stderr (">>>_get_dict_can_assign_value NOT
alloc_ok...") that fired on every failed clause type assignment (visible on
any multiform parse miss; its happy-path twin was already commented)
- new forms.test (8 tests): @form spec structure (form_names, shared
{a b}-prologue blocks), explicit -form by name/index with cross-form
rejection, multiform synopsis rendering, and GAP pins for G-041 - default
parse is effectively form-0-only, -form rejects the documented list-of-forms
usage, and @form -synopsis overrides are stored in the spec but ignored by
synopsis rendering
- new validation.test (11 tests): -type validation matrix, -range/-minsize/
-maxsize, -nocase choice normalization, option -multiple accumulation,
required-option enforcement, unknown-option rejection vs -any adhoc
passthrough (the bugfix's regression test), -- end-of-options marker,
@values -max enforcement, received-key clause ordinals, solos list, and
default-vs-received distinction
- src/tests/AGENTS.md: note that tcltest compares the -body return value -
bodies ending in a loop must end with an explicit 'set result'
- suites: punk/args 93 pass + 1 skip (was 70+1), punk/ns 18 pass; full
source-tree run 569/581 with only the known exec-14.3 baseline failure
- project 0.4.5 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- each topic (topics|help, tcl, env|environment, console|term|terminal) is a
handler proc returning {channel text} chunks, with its own punk::args
definition (id ::punk::helptopic::<topic>) so 'i help <topic>' renders
documented usage
- registering a topic (re)generates the ::punk::help / ::punk::help_chunks
definitions, so 'i help' shows a documented topic table that always matches
the registry (previously an autogenerated stub); the registry is the seam
for future subshell-declared topics
- 'help topics' derived from the registry (all aliases listed, fits 80 cols)
- no-arg overview and command-fallthrough output byte-identical to before;
80-column layout preserved; verified on tcl 9 and 8.6 kits via the script
and shell subcommands
- 'help env' degrades to a one-line notice when punk::config is not
initialised (e.g. script contexts) instead of an error stack + exit 1
- shared table_block helper destroys table objects after print (previously
leaked several per help invocation)
- project 0.4.4 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Piping a script into shell mode then exiting the restarted interactive repl
intermittently froze the shell: shellfilter::run's teardown made a bare
synchronous thread::send (settings-reset in shellthread::manager::unsubscribe)
to the shellfilter-run syslog log worker, whose event loop had silently
stopped servicing events (G-036: Tcl 9-only console+udp worker wedge -
root-cause tracked separately).
- shellthread 1.6.3: unsubscribe settings-reset send is now -async (sends to
a thread are FIFO, so a later reuse still sees the reset applied first);
shutdown_free_threads keeps its timeout timer armed across all vwait
iterations (was cancelled on the first response, leaving later waits
unbounded); get_tag_config's bare sync send flagged as same hazard class.
- shellfilter 0.2.4: shellfilter::run honors its -syslog option (default
empty -> noop runtag log worker, no thread created) instead of the
hardcoded 127.0.0.1:514 debug leftover. Syslog remains supported when
explicitly configured.
Verified: automated repro (hidden console, piped script scheduling a delayed
::punk::repl::exit in the restarted repl) hung before, exits cleanly after -
including with syslog force-enabled against a genuinely wedged worker.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Root cause: Tk registers a main loop (Tcl_SetMainLoop); tclsh''s Tcl_Main
services it after the script, so `tclsh gui.tcl` behaves like wish. The
script subcommand sourced-then-exited, so a Tk script''s after-callbacks
never ran (scriptlib/tktimer.tcl flashed and died; shell was the
workaround). Confirmed: native tclsh tktimer 1 blocks ~1.35s; script did
0.38s.
Fix: after the script body (success paths only), if a Tk main loop is
registered (info exists ::tk_version && winfo exists .), tkwait window .
services the event loop until the main window closes, then exit - the
script-level equivalent of Tcl_Main. A script that exits from a callback
(tktimer countdown) terminates directly. Console scripts have no main
loop and exit at once; a script error exits at once (no hanging window).
Verified both generations: tktimer via script now blocks the countdown
(punk902z 1.49s, punksys 1.57s - Tk present in both) exit 0; tkhello_exit
immediate; tk-script-that-errors exit 1 in 0.37s (no hang); tkhello (no
self-close) blocks like wish; console script unaffected. Enables G-020
GUI automation via `script` instead of `shell` (noted in G-020 detail).
Project version 0.4.1.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
app-punkscript accepts lib:<name> (with or without .tcl - extensionless
appends it) as the shell subcommand does: `punkexe script lib:hello` or
bare `punkexe lib:hello` via reclassification. The prefix always wins; a
literal path beginning lib: (pathological - colon is illegal in Windows
filenames) is reachable via ./lib:... Not-found errors list every
searched location; only .tcl runs via the script subcommand.
Resolution policy is factored into punk::path::scriptlib_resolve
(PUNKARGS-documented) rather than copied inline: kit-internal
app/scriptlib first and deliberately not externally overridable, then
scriptlib dirs relative to the executable - the same policy
app-punkshell encodes inline (todo noted: refactor the shell path onto
the shared proc). Share definitions via modules, not launcher control
flow.
Verified on both generations (punk902z, punksys): lib:hello
extensionless and explicit, bare-arg reclassification, not-found
candidate listing (bin/scriptlib then <root>/scriptlib for a bin/ exe).
Project version 0.4.0 (backward-compatible behaviour addition).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
app-punkscript loads punk::args::moduledoc::tclcore (catch-guarded,
mirroring the punk::repl code-interp setup) so core Tcl commands are
documented in script runs as at the repl: `''i list'' | punkexe script`
now renders the doc table instead of "Undocumented command". Cost ~40ms;
kit script startup ~0.33s. REVIEW note added: the default-punk-shell
package set is now expressed in app-punkscript AND the repl codethread
setup - consolidation into one shared definition is the G-015-noted
follow-on.
punk::packagepreference 0.1.1: moduledoc auto-load success notice moved
stdout -> stderr (stdout must stay machine-clean for script/exec
contexts; the failure branch already used stderr).
Verified on both generations (punk902z, punksys): i list doc table on
stdout, notice on stderr, timing unchanged. Build-order footnote: lib
changes need make.tcl libs before vfscommonupdate - the modules-only
first rebuild shipped a stale app-punkscript payload.
Project version 0.3.1.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New lean app package src/lib/app-punkscript behind the (previously stub)
script subcommand in punk_main.tcl:
- `<commands> | <punkexe> script` runs piped stdin to EOF - no trailing
exit needed; `<punkexe> script <file> ?args?` runs a file with
conventional ::argv0/::argv (bare non-subcommand first arg still
reclassifies to the file form). Script may itself read stdin in the
file form.
- Default punk shell environment via shared definitions (package punk
registers deck aliases e.g dev; punk::aliascore::init adds the utility
aliases) - no local alias lists, no boilerplate in piped one-liners.
- Honest exit codes: 0 success, 1 on error with errorInfo on stderr,
script''s own exit honoured. No shellfilter stacks/transforms, no
logging side effects, no interactive fallback (terminal stdin with no
scriptname is a usage error, never a blocking read or shell).
- Stdin form echoes the script''s final result when non-empty (one-shot
eval ergonomics: return-value commands like `dev projects.work <glob>`
emit their table without a puts wrapper); file form keeps pure script
semantics.
Verified on both generations (punk902z/tcl9 from _build - bin deploy was
file-locked; punksys/tcl8.6 from bin): piped success/error/exit-code,
file form with args and stdin passthrough, missing-file error, motivating
one-liner `dev projects.work *tomlish*` emitting the project table with
exit 0, `src` mode combination (dev modules load), and `shell` subcommand
regression smoke. G-015 flipped to achieved 2026-07-07 per GOALS.md
rules; goal detail records the verification and the manual console
spot-check item. Project version 0.3.0 (new user-visible shell
capability - minor bump).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
vendorupdate pulls tomlish 1.1.10 from a clean tomlish checkout: the
library API punk::config will consume now carries punk::args (PUNKARGS)
documentation - from_toml, to_dict, from_dict, to_toml,
update_tomlish_from_dict - completing the G-014 precondition
(documentation added upstream in the tomlish project, landed here by
re-vendoring, never by editing src/vendormodules copies). 1.1.10 also
carries the explicit tomlish::cmdline_main entry point (wrapped-exe
startup fix; the load-time argv side effect is gone).
vfscommonupdate refreshes the punkshell kit payload to the current built
modules: shellfilter 0.2.3 (channel-restoration try/finally), shellrun
0.1.4, punkboot::utils 0.1.1, punk::console 0.7.1 content catch-up, and
tomlish 1.1.10.
Project version 0.2.7.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
shellfilter::run now wraps everything between tee-stack add and removal in
try/finally so the callers stdout/stderr are always restored, and the
decorative stack-status table rendering in log::critical blocks is
catch-guarded. Previously an error there aborted the run after diverting
the channels and before restoring them, leaving process stdout/stderr
broken with all subsequent output silently lost - observed when a stale
same-version textblock snapshot (old option-value syntax) called the new
punk::ansi sgr_merge_singles inside the stack-status render during tomlish
test runs.
runtests.tcl:
- testinterp tm path order: bootsupport moved to last test_tmlist element
so it wins same-version module ties over vendormodules. Rules verified
experimentally on Tcl 9.0.3 (identical under standard package unknown
and punk::libunknown): tcl:™️:add prepends each argument, head of
tcl:™️:list wins same-version ties, so the last element of a
single-call add list wins.
- testinterp auto_path gains the parent of [info library] so binary
packages such as Thread (needed by shellrun) resolve in projects
without a root lib_tcl<N>/<arch> payload.
Project version 0.2.6. Verified: full shellspy suite at baseline (517
pass / exec-14.3 known); tomlish ported suite 184/184; reconstructed
stale-module scenario now completes with captured results and live
stdout instead of silent loss.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
make.tcl vendorupdate now walks up from each vendor source path to the
nearest fossil and/or git root and warns (non-fatal, once per VCS root)
when the checkout is dirty - vendored artifacts built from uncommitted
source have no committed provenance. Silent for unversioned or missing
locations; dual git+fossil roots report each VCS separately. First real
run flagged 8 dirty source projects (the tomlish 1.1.7/1.1.8 incident -
vendored content whose source history ended at 1.1.6 - motivated this,
remedied upstream by tomlish checkin 37b71e82).
Goal G-026 (proposed) carries the enforcement follow-on: abort with
explicit override, warn-only as configured mode, shared coverage of
bootsupport_localupdate, and a recorded decision on the residual
staleness gap. Abort-by-default now would block routine work given the
dirty-project count.
Project version 0.2.5; vendorupdate doc note in src/AGENTS.md.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Goal G-022: scriptable safe fossil move/rename in dev repo (all checkouts
repointed, no phantom central config-db entries, no dangling old repo db,
project-name renamable with project-code preserved), then rename this
project fossil repo shellspy -> punkshell through that mechanism via a
G-015 piped script call.
New tests src/tests/modules/punk/mix/testsuites/repo/fossilmove.test pin
current behaviour in a FOSSIL_HOME-isolated sandbox with an isolation
guard: the file-copy + fossil test-move-repository sequence repoints the
checkout but leaves the old db file on disk, a phantom repo: entry in the
central config-db, and a stale ckout: back-reference; file moves never
touch project-name. GAP-marked tests flip when G-022 lands.
shellrun 0.1.4: add missing package require punk::args - the 0.1.3
migration left run/runout/runerr/runx calling punk::args::parse without
requiring it, which broke shellrun in bare interps and made every
src/tests/runtests.tcl run fail at test-file invocation. Full suite now
back to baseline (523 tests: 508 pass, 14 skip, exec-14.3 known failure).
Project version 0.2.4 (changelog entry for the shellrun fix).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
On a tcl 8.6 windows console in cooked (line) mode with a readable handler
armed on stdin - the idle-at-a-line-mode-prompt condition, e.g. a query
fired from an after-script or worker thread while the shell waits for
input - the 8.6 channel driver has a blocking cooked-mode ReadConsole
parked (arming posts it; driver reads sample the console mode at issue
time), so a terminal query's response is swallowed until Enter and then
leaks to the line reader as phantom input. This is the one remaining
hostage window after the 0.7.1 detection fixes and repl 0.2.2 read
discipline: it is the reader legitimately doing its job, so no repl-side
change can remove it (and line mode remains supported for user
scripts/mini-apps even once raw becomes the default).
get_ansi_response_payload now detects the condition before emitting
(cooked + no -inputmode key + twapi console handle + armed readable
handler) and refuses fast with errorcode
{PUNK CONSOLE QUERY HOSTAGE_COOKED_READ} - no emission, no ~500ms timeout,
no input corruption. Mid-command queries (repl reader disarmed) and raw
mode are unaffected. Best-effort by design: a parked read can outlive a
removed handler, so the guard catches the systematic case only. The G-007
routing wrapper now preserves the owner-side errorcode so brokered callers
can discriminate the refusal.
Tests: constraint-gated guard test in probes.test (engages on a real 8.6
console, self-skips on 8.7/9 via the -inputmode key and in piped runs);
ownerrouting.test passes with the errorcode-preserving wrapper. Full suite
at baseline (exec-14.3 only) under Tcl 9.0.3; probes suite also verified
under Tcl 8.6. Project version bumped to 0.2.2 with CHANGELOG entry per
the versioning policy (user-visible failure-mode change).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Move project-version helper procs (parse_punkproject_version,
read_punkproject_version, read_changelog_latest_version) out of make.tcl
inline definitions into a loadable punkboot::utils module so the parsing
logic is unit-testable.
- src/modules/punkboot/utils-999999.0a1.0.tm: new module (0.1.0) with
the 3 procs in punkboot::utils namespace, PUNKARGS argdoc blocks.
- src/modules/punkboot/utils-buildversion.txt: initial 0.1.0.
- src/tests/modules/punkboot/utils/testsuites/utils/utils.test: 19 tests
covering section tracking, missing fields, wrong sections, whitespace,
quote styles, changelog header parsing, pre-release suffixes. All pass.
- src/make.tcl: inline procs removed; projectversion block now does
package require punkboot::utils and calls punkboot::utils:: procs.
- Layout make.tcl files (punk.shell-0.1, punk.project-0.1): same edit;
punk.basic keeps inline procs (no bootsupport tree).
- include_modules.config: added punkboot::utils entry (main + 2 layouts).
- Bootsupport snapshots propagated via make.tcl bootsupport.
- AGENTS.md: project version bump 0.2.0 -> 0.2.1 (patch: new make.tcl
projectversion subcommand is part of the product surface); added
clarifying line that make.tcl command interface warrants at least a
patch bump; src/project_layouts/ added to directories agents should
not directly modify.
- CHANGELOG.md: 0.2.1 entry.
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai