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.
 
 
 
 
 
 

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.)