The Origin section now names the upstream by its remote and expects the
working checkout at ../zig/punkzip relative to the punkshell repo root,
stated as a layout convention with its fallback spelled out (clone the
remote there; the pinned Vendored state commit identifies the exact
upstream state in any clone). The re-vendor procedure now runs entirely
from the punkshell repo root (git -C ../zig/punkzip checks, relative cp
sources), making it copy-pasteable on any machine reproducing the
sibling layout and independent of the punkshell checkout's own
directory name (survives the G-022 rename).
PROVENANCE.md was the only operational document carrying the
machine-local path - the c:/repo/jn absolute references in goals,
archives and CHANGELOG are historical narrative and stay as-is. G-135
names this file's prose as the src/tools vendored-tree convention, so
the relative form is set while there is one instance to copy.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The catch-up checkin aborted on CRLF in files outside crlf-glob. Five real
offenders, in two classes needing opposite treatment:
- src/buildsuites/suite_tcl86/patches/* (three files) is vendored sdx source by
Matt Newman and Jean-Claude Wippler plus the patch derived from it. Upstream
CRLF, and the patch must stay byte-exact to apply. Added to crlf-glob, which
is what .fossil-settings/AGENTS.md prescribes for a payload tree that trips
the prompt - explicitly in preference to reaching for --no-warnings.
- src/modules/oolib-buildversion.txt and src/buildsuites/suite_tcl86/src/main.zig
are authored punkshell files (4 lines each, fully CRLF) that the root AGENTS.md
LF preference governs. Converted rather than globbed; globbing src/modules
would suppress the warning across authored territory.
punkzip's hamlet.txt was a false positive in the first scan - already covered by
the crlf-glob entry for its fixtures directory, where it is deliberately CRLF
byte-exact test data.
Converted in git first so the fossil checkin records identical content; a
content fix applied only fossil-side would desynchronise the two mirrors in the
middle of a catch-up.
Note oolib-buildversion.txt is build-generated, so a future regeneration may
reintroduce CRLF there.
Claude-Session: https://claude.ai/code/session_01YNjnq6oDzecknLg7AuWZgU
Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com
The module hard-required opt, cmdline and tepam at load time, so on a punkshell build
without tcllib 'package require argparsingtest' died outright with "can't find package
opt". A harness whose whole job is comparing parser libraries is useless precisely on the
runtime you wanted to measure, and the module already had the right pattern for this:
argp, parse_args and argparse degrade to a not_loaded row rather than taking the module
down with them. The three tcllib parsers were the odd ones out. Reproduced before the
change with punk module paths only and an emptied auto_path, which is where tcllib's
pkgIndex-based packages come from.
Every parser this module compares is now optional. Their roster entries become
{optional opt}, {optional cmdline} and {optional tepam}, so discover_parsers reports
not_loaded when the package is absent and nothing in the harness calls them. punk::args
and Tcl 8.6- remain hard requires - the harness itself is built on those.
The guard differs by package because the dependency does:
cmdline is only ever called INSIDE proc bodies, so the catch-guarded require at the top
is the whole story - the wrapper procs stay defined and are simply never invoked.
opt and tepam are used at DEFINITION time: tcl::OptProc and tepam::procedure are the
commands that create the wrapper procs. Their seven definition sites (four opt, across
opts/tkstyle/tclstyle/sandwich; three tepam) are each additionally guarded, so with the
package absent the proc does not exist at all.
Those sites test 'package present' rather than wrapping the definition in a bare catch.
Both tolerate absence, but a presence test still lets a genuine error in a spec raise
loudly on a runtime that does have the package, where a catch would swallow it. The
pre-existing argp registration site keeps its original catch idiom - working code, not
part of this change.
Pinned by module_loads_without_optional_parser_packages: a child interp with an emptied
auto_path that asserts the module loads, that each absent package's parser reports
not_loaded, and that the opt and tepam wrapper procs genuinely do not exist. Where a
runtime supplies one of the three as a .tm on a module path the test degrades to
asserting the module still loads, which is the main claim anyway.
Tests that name parsers explicitly (first_call_library_backed, first_call_positional_styles,
sandwich_strictness) now filter through a loaded_parsers helper, and the tclstyle
cmdline_typed status assertion derives its expectation from package presence: those
parsers can now legitimately be missing.
Verification: 26/26 pass under the 32-bit luck-test2 runtime with no skips, 24 pass +
2 skip under native Tcl 9.0.3. With opt and tepam hidden, the built 1.3.0 module loads,
reports both as not_loaded, and compare lists them in its "not loaded:" footer instead of
the whole module failing to load.
Claude-Session: https://claude.ai/code/session_01Q82VuBXzgeKsAwMYHrbpP1
Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com
3 days ago
7 changed files with 299 additions and 176 deletions
#1.3.0 - opt, cmdline and tepam are no longer module-level hard requires. A punkshell build without tcllib could not load the module at all ("can't find package opt"), which is useless on exactly the runtime you wanted to measure: every parser this module compares is now optional and a missing one degrades to a not_loaded row like argp/parse_args/argparse already did. Their roster entries become {optional opt} / {optional cmdline} / {optional tepam}
#1.3.0 - the guard differs by package because the dependency does: cmdline is only called inside proc bodies, so the catch-guarded require is the whole story, whereas tcl::OptProc and tepam::procedure DEFINE their wrapper procs - those 7 definition sites are additionally guarded by a 'package present' test, so the proc simply does not exist when the package is absent (and discover_parsers then never calls it). Presence test rather than catch, so a genuine spec error still raises loudly on a runtime that does have the package
#1.3.0 - regression pinned by module_loads_without_optional_parser_packages (child interp with an emptied auto_path); tests that name parsers explicitly now filter through a loaded_parsers helper
#1.2.0 - fourth calling style 'sandwich': 2 mandatory leading positionals, THEN the 10 keyed options (argvec now exercises the valueless -join flag alongside -flag value pairs), THEN 3 mandatory trailing positionals. The 2/3 split is asymmetric on purpose - an even split could be satisfied by a parser that just halves the positional block. New namespace argparsingtest::sandwich with manual_switch, punkargs, punkargs_by_id, punkargs_parsecache, opt and argparse wrappers; return contract is {{p1 p2} {p3 p4 p5} optsdict} (grouped rather than the flat {p1 p2 opts} of the other positional styles, since positionals sit on both sides of the options)
#1.2.0 - fourth calling style 'sandwich': 2 mandatory leading positionals, THEN the 10 keyed options (argvec now exercises the valueless -join flag alongside -flag value pairs), THEN 3 mandatory trailing positionals. The 2/3 split is asymmetric on purpose - an even split could be satisfied by a parser that just halves the positional block. New namespace argparsingtest::sandwich with manual_switch, punkargs, punkargs_by_id, punkargs_parsecache, opt and argparse wrappers; return contract is {{p1 p2} {p3 p4 p5} optsdict} (grouped rather than the flat {p1 p2 opts} of the other positional styles, since positionals sit on both sides of the options)
#1.2.0 - sandwich style: cmdline, tepam, argp and parse_args are 'unsupported' (N/A rows) - each verified to fail on the style's argvec. cmdline is the notable one: cmdline::getoptions returns WITHOUT error having parsed nothing, leaving every option at its default and the argument list untouched
#1.2.0 - sandwich style: cmdline, tepam, argp and parse_args are 'unsupported' (N/A rows) - each verified to fail on the style's argvec. cmdline is the notable one: cmdline::getoptions returns WITHOUT error having parsed nothing, leaving every option at its default and the argument list untouched
#1.2.0 - sandwich style: punk::args, opt and the manual baseline model the arrangement and reject options ahead of the leaders; argparse reaches the style only via -mixed, which permits switches anywhere and therefore parses the argvec correctly WITHOUT enforcing the arrangement (pinned by the sandwich_strictness / sandwich_argparse_is_permissive tests)
#1.2.0 - sandwich style: punk::args, opt and the manual baseline model the arrangement and reject options ahead of the leaders; argparse reaches the style only via -mixed, which permits switches anywhere and therefore parses the argvec correctly WITHOUT enforcing the arrangement (pinned by the sandwich_strictness / sandwich_argparse_is_permissive tests)
#0.1.4 - collection add duplicate-key error message no longer misnames the object as 'col_processors' (stray copy-paste in the generic collection class)
#0.1.4 - collection add duplicate-key error message no longer misnames the object as 'col_processors' (stray copy-paste in the generic collection class)
if {[dict get $result loaded] eq ""} { set ok "child did not load argparsingtest" }
if {$ok eq 1 && [llength [dict get $result stillcommands]] } { set ok "wrapper procs defined without their package: [dict get $result stillcommands]" }
if {$ok eq 1} {
foreach {pname status} [dict get $result statuses] {
if {$status ne "not_loaded"} { set ok "$pname reported $status with its package absent" ; break }
}
}
set ok
} -result 1
#added 2026-07-26 (agent) - opts-style external wrappers agree with the hand-rolled baseline's option values
#added 2026-07-26 (agent) - opts-style external wrappers agree with the hand-rolled baseline's option values
test parse_args_opts_values {Test that the parse_args opts wrapper returns the declared option values} -constraints parse_args_loaded -body {
test parse_args_opts_values {Test that the parse_args opts wrapper returns the declared option values} -constraints parse_args_loaded -body {
set r [argparsingtest::opts::parse_args -return object -x hello -y world -1 1 -2 2 -3 3]
set r [argparsingtest::opts::parse_args -return object -x hello -y world -1 1 -2 2 -3 3]
@ -204,7 +284,7 @@ namespace eval ::testspace {
test sandwich_strictness {Test that the modelling sandwich parsers reject options before the leaders} -body {
test sandwich_strictness {Test that the modelling sandwich parsers reject options before the leaders} -body {
set badvec {-x hello -join alpha beta gamma delta epsilon}
set badvec {-x hello -join alpha beta gamma delta epsilon}
if {![catch {argparsingtest::sandwich::$pname {*}$badvec} r]} {
if {![catch {argparsingtest::sandwich::$pname {*}$badvec} r]} {
set ok "$pname accepted options-before-leaders: $r"
set ok "$pname accepted options-before-leaders: $r"
break
break
@ -237,7 +317,7 @@ namespace eval ::testspace {
#added 2026-07-17 (agent)
#added 2026-07-17 (agent)
test first_call_library_backed {Test that first_call returns cold and warm float timings for library-backed parsers (warm-up scripts must run cleanly)} -body {
test first_call_library_backed {Test that first_call returns cold and warm float timings for library-backed parsers (warm-up scripts must run cleanly)} -body {
set r [argparsingtest::first_call -style opts -parsers {punkargs punkargs_by_id opt cmdline_untyped cmdline_typed tepam}]
set r [argparsingtest::first_call -style opts -parsers [loaded_parsers opts {punkargs punkargs_by_id opt cmdline_untyped cmdline_typed tepam}]]
set ok 1
set ok 1
foreach {pname sub} [dict get $r opts] {
foreach {pname sub} [dict get $r opts] {
foreach mode {cold warm} {
foreach mode {cold warm} {
@ -254,7 +334,7 @@ namespace eval ::testspace {
test first_call_positional_styles {Test first_call cold and warm for tkstyle, tclstyle and sandwich parsers} -body {
test first_call_positional_styles {Test first_call cold and warm for tkstyle, tclstyle and sandwich parsers} -body {