15 KiB
G-168 Selection-sound launch definitions: script/tclsh forms match dispatch reality
Status: achieved 2026-08-05
Scope: src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm ((script)::punkexe::script + ::tclsh form discrimination); src/lib/app-punkscript/punkscript.tcl (behaviour oracle, error-surface decision); src/vfs/_config/punkboot_core.tcl (only if the dispatch-parse decision adopts gating, + synced copies via established channels); src/tests/modules/punk/args/testsuites/args/ (new model-parity suite); src/tests/shell/testsuites/punkexe/launcherhelp.test (as touched)
Goal: The two multi-form launch-subcommand definitions are parse-discriminable with existing punk::args vocabulary - form auto-selection agrees with actual dispatch/app behaviour: the file form excludes exactly '-e' (-regexprefail {^-e$}, message naming ./-e), the oneliner form declares the '-e' word as a real element, and tclsh's scriptfile-vs-stdin split (leading-dash words stay argv; incomplete -encoding falls through, stock-style) is expressed through candidacy fall-through - so parse_status verdicts, noformmatch diagnoses and downstream consumers (help depth, completion) see the truth instead of a display-only model.
Acceptance: against the script definition, parse/parse_status select oneliner for {-e ...}, select file for { ...} where ne -e, and {-e} alone reports noformmatch with oneliner viable-incomplete (the app's "-e requires a script argument" reality); against tclsh, {file args} selects scriptfile, a leading-dash word selects stdin, and an incomplete -encoding tail falls through to stdin (stock parity); formcheck reports no unsanctioned findings; a real-vs-model probe matrix (G-055 method) against actual dispatch/app-punkscript behaviour is recorded in this file; whether DISPATCH adopts parse-gating for script/tclsh (tabled pointed errors replacing app-punkscript's plain ones) is decided and recorded with rationale and, if adopted, a measured script-launch latency delta; the new behaviour is pinned in the args testsuite and launcherhelp.test passes unchanged (or with deliberately updated pins named here).
Context
2026-08-05 discussion (formhint arc): 'punk9-dev -e' / 'script -e' error - word-1
'-e' is RESERVED by behaviour (app-punkscript punkscript.tcl:78-88; a file so
named needs ./-e), but the file form declares scriptfile as any string - the
definition is display-honest, parse-dishonest, and the forms cleanly double-match.
The blocker assumed earlier (G-072 negation) is not needed: -regexprefail is
honoured during form matching (verified in the G-143 make.tcl arc, achieved -
goals/archive/G-143-maketcl-multiform-help.md; the G-150 selection-sound hash
declaration is the dash-led-form-word precedent).
Notes
- Enacts the punkexe portion of the G-150 follow-on "sweep other flat multi-form
definitions for selection-soundness" (goals/archive/G-150-cmdhelp-form-narrowing.md);
that follow-on stays open - the sweep is broader than these two definitions.
- The G-144 follow-on (punkexe moduledoc @form -synopsis override set drops once
auto-synopsis serves - goals/archive/G-144-punkargs-synopsis-name-fallback.md,
disposition flipped to this goal) is decided in this work: keep the
placeholder overrides or adopt @cmd -name auto lines - recorded either way.
- Related: G-089 (proposed) - script's lib:name resolution text is consumed scope.
- Related: G-055 (proposed) - the real-vs-model probe method borrowed here.
- Related: G-072 (proposed) - deliberate non-dependency: negation NOT required.
- Related: G-170 (proposed) - consumer: its dry-run arm requires this goal's
parse-trueness.
- Overlap survey 2026-08-05: goals_xref paths over the Scope - G-089 (named
above) is the only substantive live intersection on punkboot_core.tcl;
moduledoc-sibling matches (G-055 G-068 G-072 G-083 G-084 G-166 G-167) are
parser/spec mechanisms or other moduledocs, no availability interaction.
- Activation survey 2026-08-05 (goals_xref score G-168, activation-freshness
rule): Related: G-169 (proposed) - back-pointer added at activation (G-169
already points here): the definitions this goal makes selection-sound are
the same ones G-169 re-homes under app ownership; G-169's handover carries
whatever fencing shape this goal lands. Other new hits (G-069 dev-lint)
are path-coincidental (same moduledoc/test directories), no mechanism
interaction.
- 2026-08-05 pre-test findings (engine primitives probed on tclsh 9.0.3 and
pinned green on tclsh90+tcl87: forms.test fencing/fall-through group,
formviability_dangling_optional_option_conservative,
errsel_dangling_option_trailing_value_overflow, formcheck_GAP_punkexe_*):
- A -regexprefail rejection on an OPTIONAL value slot is fatal to that form's
candidacy - the word does NOT re-land on a later permissive slot (contrast
optional-CHOICE rejection, errsel_rejected_word_lands_later) - so per-slot
fencing fences whole forms (forms_fence_optional_slot_no_reland).
- Unknown options, dangling option words and option-satisfied-but-values-unmet
tails each fail only their form: auto-selection falls through to a sibling
as a SUCCESSFUL parse, and an option VALUE's -regexprefail participates in
candidacy too - the stock "-encoding recognised only before a non-dash word"
primitive is expressible (forms_fallthrough_* pins).
- LIMIT for the tclsh model: a complete '-encoding ' line
wordwise-matches both a fenced scriptfile form and any dash-tolerant stdin
form; per-word constraints cannot express stock's "scriptfile wins when
complete" priority, so the no-silent-preference contract (G-041, achieved -
goals/archive/G-041-punkargs-form-matching.md) raises
multipleformmatches (forms_fenced_pair_residual_overlap). The tclsh
declaration must design around this class - form restructuring, an
-overlapallowed sanction (display-only; parse still raises), or the
dispatch-parse decision this goal already tables.
- The {-e}-alone viable-incomplete acceptance rides the re-probe of G-164
(achieved 2026-08-04 - goals/archive/G-164-viability-alternative-allocation.md),
which re-seats only words naming missing REQUIRED options: declare '-e' as a
required -type none flag plus a separate required script value (the
auto_exec::hash -r/-d shape), NOT as a value-taking option - a stolen word
naming a value-taking or OPTIONAL option keeps verdict invalid
(formviability_reprobe_value_taking_option_conservative and the new
formviability_dangling_optional_option_conservative twin).
- Before-state pinned as GAP: both punkexe multi-form definitions carry
unsanctioned structural formcheck findings and every primary launch line
double-matches ({-e X} / {file args} / {file} / {-encoding enc file});
empty input selects file/stdin uniquely (formcheck_GAP_punkexe_script_forms_overlap,
formcheck_GAP_punkexe_tclsh_forms_overlap,
formcheck_GAP_punkexe_primary_inputs_double_match - flipped deliberately
by this goal per its Acceptance).
Progress
Achieved 2026-08-05 in one session (user-directed activation and implementation
same day; punk::args::moduledoc::punkexe 0.7.0). The declarations follow the
pre-test findings exactly - no engine change was needed.
Mechanism landed ((script)::punkexe::script):
- file form: scriptfile gains -regexprefail {^-e$} with -regexprefailmsg naming
the ./-e reachability ("reserved for the -e one-liner form; a file literally
named -e is reachable as ./-e") - word-1 '-e' is the ONLY fenced word; every
other dash-led word remains a file path, matching app-punkscript's word-1
test.
- oneliner form: the '-e' word is now a real declared element - a required
-type none flag (@opts -e -optional 0, the auto_exec::hash -r/-d shape)
ahead of the existing required script value. A bare {-e} therefore reports
noformmatch with the oneliner form verdicting viable-incomplete (the G-164
re-probe re-seats the reservation-stolen '-e' as the flag it names and finds
pure exhaustion) and the file form invalid - the app's "-e requires a script
argument" reality.
Mechanism landed ((script)::punkexe::tclsh):
- scriptfile form: scriptfile gains -regexprefail {^-} (stock tclMain.c: a
dash-led word in script-file position means NO script) with a stock-parity
message.
- stdin form: the first word is now a declared fenced element 'firstarg'
(-regexprefail {^[^-]} - a NON-dash first word IS a script file), remaining
args unchanged; its -help states it is not an option and stays in ::argv.
- The complete '-encoding ' shape is the recorded LIMIT:
it word-shape-matches both forms (a dash-led encoding NAME included - the
engine consumes a dash-led option value, matching stock's unconstrained
argv[2]) and stock's arity-priority rule (scriptfile wins when the triple is
complete) is not expressible per-word - the pair is declared
@form -overlapallowed {scriptfile} on the stdin form (formcheck sanction)
and parse raises multipleformmatches naming both forms per the G-041
no-silent-preference contract. Dispatch never parses these ids, so the
ambiguity surfaces only in declaration-driven consumers (help dry-runs,
completion), where the two-form diagnosis is the honest answer.
Real-vs-model probe matrix (G-055 method; real side = bin/punk91 dispatch +
app-punkscript probes 2026-08-05, model side = parse/parse_status against the
0.7.0 definitions on tclsh90 - punkexemodel.test pins the model column):
script words
real behaviour
model selection
(empty)
piped-stdin script mode
file
-e 'puts one'
oneliner runs, exit 0
oneliner
-e 'expr 1+1'
oneliner, result echo '2'
oneliner
-e code x -y
oneliner, argv0=-e argv={x -y}
oneliner
-e
usage error "-e requires a script argument" 1
noformmatch, oneliner viable-incomplete
nosuchfile.tcl
file class: not-found, exit 1
file
-x
file class: not-found naming '-x', exit 1
file
hi.tcl a b
file runs, argv={a b}
file
./-e
file named -e runs
file
-e foo.tcl
oneliner evaluates the TEXT 'foo.tcl'
oneliner
tclsh words
real behaviour
model selection
(empty)
stdin (repl/piped)
stdin
hi.tcl a b
scriptfile runs, argv={a b}
scriptfile
-foo bar
stdin, ARGV=-foo bar
stdin
-encoding
stdin, ARGV=-encoding
stdin (scriptfile invalid)
-encoding utf-8
stdin, ARGV=-encoding utf-8
stdin (scriptfile incomplete)
-encoding utf-8 hi.tcl a
scriptfile with encoding, argv={a}
multipleformmatches (the LIMIT, sanctioned)
-encoding utf-8 -bar
stdin, ARGV=all three words
stdin
-encoding -foo hi.tcl
scriptfile attempt (unknown encoding error)
multipleformmatches (same LIMIT class)
Divergence summary: model matches real classification on every line except
the two complete-'-encoding'-shape rows, where the model deliberately raises
the sanctioned multipleformmatches instead of silently preferring scriptfile
(rationale above; the G-041 contract).
Decision - dispatch parse-gating: NOT adopted (punkboot_core.tcl untouched;
no latency delta applicable). Rationale: (a) script/tclsh are declared
PASSTHROUGH surfaces (G-032 decision) - their arguments belong to the launched
script/app, and a dispatch parse would add punk::args resolve+parse cost to
every script/tclsh launch on the boot path; (b) stock parity: the complete
-encoding line is stock-valid and dispatch-classified scriptfile, but the
model deliberately raises multipleformmatches on it - a parse gate would turn
a working stock line into a launch error, or need a priority override that
reintroduces the silent preference G-041 removed; (c) app-punkscript's error
surfaces are already honest and pinned (usage errors exit 1, no launch
chatter - G-077, launcherhelp.test); (d) the consumers that want the parse
(G-170 help-depth dry-run, G-044 completion) get parse-trueness without
gating.
Decision - G-144 follow-on (@form -synopsis overrides): KEPT. Neither the raw
(script) id nor @cmd -name ('punkexe tclsh') is an invocable name; the
placeholder matches the file's prose convention ("'' below
stands for any punk kit executable"), and the stdin form's override keeps the
user-facing '?arg ...?' line over the model's firstarg/arg fencing split. The
G-144 archived follow-on line's disposition flipped accordingly (make.tcl's
own override retirement had already landed in the 0.41.0 arc).
Verification evidence:
- New suite src/tests/modules/punk/args/testsuites/args/punkexemodel.test
(6 tests): both selection matrices, the {-e} viable-incomplete verdict
(ok 0 / status incomplete / form oneliner / formstatus {oneliner incomplete
file invalid}), the ./-e fence message, the -encoding fall-through statuses
(scriptfile invalid vs incomplete, stdin selected), and the LIMIT pins
(multipleformmatches naming {scriptfile stdin} for both complete shapes).
- formcheck.test GAP flips: formcheck_punkexe_script_forms_discriminated
(zero findings), formcheck_punkexe_tclsh_forms_sanctioned (unsanctioned
empty + the stdin form's -overlapallowed {scriptfile} pinned via get_spec),
formcheck_punkexe_primary_inputs_selection_sound (dispatch-true forms for
every primary line; the sanctioned LIMIT raise named).
- Full modules/punk/args subtree 337 passed / 3 pre-existing skips / 0 failed
on BOTH native tclsh90 (Tcl 9.0.3) and tclsh87.
- src/tests/shell/testsuites/punkexe/launcherhelp.test 24/24 unchanged (the
acceptance gate; its 'help script'/'help tclsh' pins glob only @cmd -name
strings and row additions do not disturb them - verified for post-bake
robustness too).
- Minted modules/punk/args/moduledoc/punkexe-0.7.0.tm via make.tcl modules;
kits/bootsupport carry 0.6.1 until the next promotion/bake cycle (the
moduledoc rides the established channels; no synced-copy obligations - the
punkboot_core.tcl copies were not touched).
- G-151 composition smoke: punk::args::parse_report over an accepted
'{-e {puts hi} x -y}' line renders the landing rows (-e received, script
received, arg received x2) - the G-170 dry-run arm's ingredients compose.
Follow-ons
Follow-on: stock's arity-priority for the complete '-encoding ' shape becomes expressible if punk::args grows a form-priority or arity-conditional fencing mechanism - the -overlapallowed sanction and the punkexemodel/formcheck LIMIT pins flip deliberately then => open
(The broader flat-multi-form selection-soundness sweep stays recorded as the
open follow-on in goals/archive/G-150-cmdhelp-form-narrowing.md - the punkexe
portion is enacted by this goal, per the Notes; no duplicate line here.)