From 2339e4065e02b182c2b851e344432b50a344be4b Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Wed, 5 Aug 2026 20:22:17 +1000 Subject: [PATCH] G-168 achieved: selection-sound launch definitions - script/tclsh forms match dispatch reality (punkexe moduledoc 0.7.0) User-directed activation and implementation in one arc (proposed -> active at the user's word with the activation-freshness survey recorded, -> achieved + archived on acceptance). The declarations follow the 2026-08-05 pre-test findings exactly - no engine change was needed. (script)::punkexe::script: the file form fences scriptfile with -regexprefail {^-e$} and a -regexprefailmsg naming the ./-e reachability (word-1 '-e' is the ONLY fenced word - every other dash-led word remains a file path, matching app-punkscript's word-1 test); the oneliner form declares the '-e' word as a real element - a required -type none flag (the auto_exec::hash -r/-d shape) ahead of the existing required script value, so a bare {-e} reports noformmatch with the oneliner form verdicting viable-incomplete via the G-164 re-probe (the app's "-e requires a script argument" reality) and the file form invalid. (script)::punkexe::tclsh: scriptfile gains -regexprefail {^-} (stock tclMain.c: a dash-led word in script-file position means NO script) and the stdin form's first word is a declared fenced element 'firstarg' (-regexprefail {^[^-]} - a non-dash first word IS a script file; not an option, stays in ::argv). Selection now matches stock classification on every line except the recorded LIMIT: the complete '-encoding ' shape word-shape-matches both forms (a dash-led encoding NAME included - the engine consumes dash-led option values, matching stock's unconstrained argv[2]) and stock's arity-priority rule is not expressible per-word - the pair is declared @form -overlapallowed {scriptfile} on the stdin form (formcheck sanction) while 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. Decisions recorded in the archived detail file: dispatch parse-gating NOT adopted (punkboot_core.tcl untouched; script/tclsh stay declared passthrough surfaces - boot-path parse cost, stock-parity conflict with the LIMIT, app error surfaces already honest and pinned, and the declaration-driven consumers get parse-trueness without gating; no latency delta applicable); the @form -synopsis overrides KEPT (neither the raw (script) id nor @cmd -name is an invocable name; the stdin override keeps the user-facing '?arg ...?' line over the model's firstarg/arg split) - the G-144 archived follow-on flipped to declined accordingly (make.tcl's own override retirement had already landed in the 0.41.0 arc). A real-vs-model probe matrix (G-055 method; bin/punk91 dispatch + app-punkscript real side) is recorded in the detail file - model matches real classification on every line except the two complete-'-encoding' rows (the sanctioned LIMIT). Tests: new args/punkexemodel.test (6 - script/tclsh selection matrices against the real ids with the real-side oracle referenced, the bare-'-e' viable-incomplete verdict, the ./-e fence message, -encoding fall-through statuses (scriptfile invalid vs incomplete, stdin selected), the LIMIT pins naming {scriptfile stdin} for both complete shapes); formcheck.test punkexe GAPs flipped deliberately per the acceptance: formcheck_punkexe_script_forms_discriminated (zero findings), formcheck_punkexe_tclsh_forms_sanctioned (unsanctioned empty + the stdin form's -overlapallowed pinned via get_spec; the positional-chain witness derivation misses option-dependent overlaps - formcheck's documented miss direction - so the sanction also future-proofs improved derivation), formcheck_punkexe_primary_inputs_selection_sound (dispatch-true forms for every primary line; empty input still selects file/stdin uniquely). Full modules/punk/args subtree 337 passed / 3 pre-existing skips / 0 failed on BOTH native tclsh90 (9.0.3) and tclsh87; src/tests/shell/testsuites/punkexe/launcherhelp.test 24/24 unchanged per acceptance (its 'help script'/'help tclsh' pins glob only @cmd -name strings - row additions verified safe post-bake as well). 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. Goals: G-168 entry moved to GOALS-archive.md, detail file to goals/archive/ with the probe matrix, both decisions and verification evidence; one open Follow-on (arity-priority expressibility would flip the LIMIT pins; the broader selection-soundness sweep stays recorded in G-150's archive). Activation survey Related: line added (G-169 back-pointer). Reference sweep: G-169 coordination note updated (G-168 landed first - the re-homed definitions carry this fencing shape), G-170 dependency marked satisfied (its dry-run arm inherits the recorded LIMIT). src/tests/modules AGENTS.md punk/args bullet extended. goals_lint clean (82 active / 88 archived). Claude-Session: https://claude.ai/code/session_0156PuejSCGjgeGb7jiABrDU Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- GOALS-archive.md | 4 + GOALS.md | 4 - ...-168-selection-sound-launch-definitions.md | 73 ------ goals/G-169-app-owned-launch-docs.md | 10 +- goals/G-170-launcher-help-depth.md | 7 +- .../G-144-punkargs-synopsis-name-fallback.md | 2 +- ...-168-selection-sound-launch-definitions.md | 210 ++++++++++++++++++ .../args/moduledoc/punkexe-999999.0a1.0.tm | 56 ++++- .../args/moduledoc/punkexe-buildversion.txt | 3 +- src/tests/modules/AGENTS.md | 2 +- .../punk/args/testsuites/args/formcheck.test | 67 +++--- .../args/testsuites/args/punkexemodel.test | 165 ++++++++++++++ 12 files changed, 487 insertions(+), 116 deletions(-) delete mode 100644 goals/G-168-selection-sound-launch-definitions.md create mode 100644 goals/archive/G-168-selection-sound-launch-definitions.md create mode 100644 src/tests/modules/punk/args/testsuites/args/punkexemodel.test diff --git a/GOALS-archive.md b/GOALS-archive.md index f758041d..76b4f922 100644 --- a/GOALS-archive.md +++ b/GOALS-archive.md @@ -20,6 +20,10 @@ Do not edit archived records here except to fix a broken path. Archived detail f ## Archived goals +### G-168 [achieved 2026-08-05] Selection-sound launch definitions: script/tclsh forms match dispatch reality → detail: goals/archive/G-168-selection-sound-launch-definitions.md +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) +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). (Achieved at punkexe moduledoc 0.7.0: file form scriptfile -regexprefail {^-e$} + ./-e message, oneliner '-e' as required solo flag ('-e' alone verdicts viable-incomplete via the G-164 re-probe); tclsh scriptfile {^-} fence + stdin firstarg {^[^-]} fence with the complete '-encoding ' shape the recorded sanctioned LIMIT (@form -overlapallowed; parse raises multipleformmatches per G-041 no-silent-preference); dispatch parse-gating decided NOT adopted; probe matrix + decisions in the detail file; new punkexemodel.test + 3 formcheck GAP flips, args subtree green tclsh90+tclsh87, launcherhelp.test 24/24 unchanged.) + ### G-151 [achieved 2026-08-05] punk::args annotated success render (received-args landing report) → detail: goals/archive/G-151-punkargs-annotated-success-render.md Scope: src/modules/punk/args-999999.0a1.0.tm (parse-result presentation); src/tests/modules/punk/args/testsuites/args/ Acceptance: given a successful parse result (or the words plus definition id), a punk::args surface renders per-argument received-vs-default annotation covering leaders, opts and values including -multiple aggregation - the canonical demonstration being a flag-like word consumed as a VALUE (the 'punk91 -confirm 0'-as-three-values class) visibly attributed to its consuming argument; failure-side rendering is unchanged (existing arg_error pins pass); the behaviour is pinned in the args testsuite. make.tcl retiring its interim one-line 'dry-run: line accepted ...' report in favour of this render is recorded as consumer follow-through after a bootsupport promotion, not part of this acceptance. (Achieved as punk::args::parse_report - result-dict and words+withid entry forms, Argument|Source|Value landing table in table/tableobject/string/dict returns with the dict form never elided; type-aware always-marked display elision over the new ansistring TRUNCATE grapheme primitive (punk::ansi 0.2.0); caller -valuewidth > @cmd -reportvaluewidth > 48 width cascade; punk::args 0.18.0, new parsereport.test, args+ansi subtrees green on tclsh90 and tclsh87.) diff --git a/GOALS.md b/GOALS.md index 73244690..46306909 100644 --- a/GOALS.md +++ b/GOALS.md @@ -402,10 +402,6 @@ Detail: goals/G-166-cmdinfo-unavailable-surfacing.md Scope: src/modules/punk/args-999999.0a1.0.tm (form selection/candidacy availability filter + tailored raises, option-scan unavailable-name recognition, synopsis/table renderers - annotated form lines, unavailable option rows, automated other-shapes note, formstatus 'available' field, -form display-vs-parse split); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp -return text form-section heading annotation; form narrowing inherits the availability filter); src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (adoption on curated real 8.6-vs-9 deltas); src/tests/modules/punk/args/testsuites/args/ (new suite + formviability/tclcoreparity extensions) Detail: goals/G-167-punkargs-version-delta-availability.md -### G-168 [proposed] Selection-sound launch definitions: script/tclsh forms match dispatch reality -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) -Detail: goals/G-168-selection-sound-launch-definitions.md - ### G-169 [proposed] App-owned launch-subcommand documentation consumed by the launcher Scope: src/lib/app-punkscript/punkscript.tcl (carries its subcommand's punk::args definition via inert registration); src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm (single-definer handover); src/vfs/_config/punkboot_core.tcl (built-in help resolution via app-owned ids, *_main.tcl declaration surface documented, + synced copies via established channels); src/tests/shell/testsuites/punkexe/launcherhelp.test Detail: goals/G-169-app-owned-launch-docs.md diff --git a/goals/G-168-selection-sound-launch-definitions.md b/goals/G-168-selection-sound-launch-definitions.md deleted file mode 100644 index 26a2d3cd..00000000 --- a/goals/G-168-selection-sound-launch-definitions.md +++ /dev/null @@ -1,73 +0,0 @@ -# G-168 Selection-sound launch definitions: script/tclsh forms match dispatch reality - -Status: proposed -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. -- 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). diff --git a/goals/G-169-app-owned-launch-docs.md b/goals/G-169-app-owned-launch-docs.md index 78f7941d..460da977 100644 --- a/goals/G-169-app-owned-launch-docs.md +++ b/goals/G-169-app-owned-launch-docs.md @@ -18,8 +18,14 @@ core-owned (tclcore precedent: separation + parity pins). ## Notes -- Coordination with G-168: either order works; whichever lands second edits the - definition at its then-current home (never two definers). +- Coordination with G-168 (achieved 2026-08-05 - goals/archive/G-168-selection-sound-launch-definitions.md): + G-168 landed first, so THIS goal is the one that lands second and edits the + definitions at their then-current home (never two definers) - the re-homed + script definition carries G-168's fencing shape (file form scriptfile + -regexprefail {^-e$} + ./-e message, oneliner '-e' as a required solo flag) + and any tclsh re-home carries the {^-}/{^[^-]} fences plus the stdin form's + -overlapallowed {scriptfile} sanction; the punkexemodel.test / + formcheck.test pins guard the handover. - Related: G-075 (proposed) - sibling documentation-reachability surface. - Related: G-042/G-043 (proposed) - adjacent declaration-composition patterns; deliberate non-overlap (launch surface, not repl subshells). diff --git a/goals/G-170-launcher-help-depth.md b/goals/G-170-launcher-help-depth.md index 20e76f6c..43e58b08 100644 --- a/goals/G-170-launcher-help-depth.md +++ b/goals/G-170-launcher-help-depth.md @@ -14,7 +14,12 @@ builds the destination, then points at it. ## Notes -- Depends on G-168 (proposed) for the dry-run arm (narrowing arm is standalone). +- Depends on G-168 (achieved 2026-08-05 - goals/archive/G-168-selection-sound-launch-definitions.md) + for the dry-run arm (narrowing arm is standalone) - now satisfied: the + script/tclsh forms are parse-discriminable and the dry-run arm inherits one + recorded LIMIT (the complete '-encoding ' shape raises a + sanctioned multipleformmatches naming both forms - the honest two-form + diagnosis for that line; G-168's Progress records the rationale). - Related: G-169 (proposed) - the defs' home determines where templates land. - Related: G-151 (achieved 2026-08-05 - goals/archive/G-151-punkargs-annotated-success-render.md) - the dry-run arm's received-args report is G-151's punk::args-level annotated diff --git a/goals/archive/G-144-punkargs-synopsis-name-fallback.md b/goals/archive/G-144-punkargs-synopsis-name-fallback.md index 01c552a1..d9af7805 100644 --- a/goals/archive/G-144-punkargs-synopsis-name-fallback.md +++ b/goals/archive/G-144-punkargs-synopsis-name-fallback.md @@ -45,4 +45,4 @@ The goal text named "(shared)/(package)-style doc ids" alongside (script) as ben ## Follow-ons -Follow-on: make.tcl drops its per-definition @form -synopsis overrides and the punkexe moduledoc's override set once the @cmd -name fallback serves them => goal G-168 +Follow-on: make.tcl drops its per-definition @form -synopsis overrides and the punkexe moduledoc's override set once the @cmd -name fallback serves them => declined 2026-08-05 (decided in G-168, achieved - goals/archive/G-168-selection-sound-launch-definitions.md: the punkexe moduledoc override set is KEPT - neither the raw (script) id nor @cmd -name 'punkexe ' is an invocable name, the placeholder matches the file's prose convention, and the tclsh stdin form's override keeps the user-facing '?arg ...?' line over the G-168 firstarg/arg fencing split; make.tcl's own override retirement had already landed in the 0.41.0 G-143/G-144 arc) diff --git a/goals/archive/G-168-selection-sound-launch-definitions.md b/goals/archive/G-168-selection-sound-launch-definitions.md new file mode 100644 index 00000000..2e62741d --- /dev/null +++ b/goals/archive/G-168-selection-sound-launch-definitions.md @@ -0,0 +1,210 @@ +# 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.) diff --git a/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm b/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm index 7f67aa5d..aaca3a81 100644 --- a/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm +++ b/src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm @@ -278,6 +278,19 @@ tcl::namespace::eval punk::args::moduledoc::punkexe { interactive form (console stdin, no script) fails fast with a usage error instead of blocking in a console read." + #G-168 selection-sound forms: scriptfile is fenced against any leading + #'-' word (stock tclMain.c parity - such a word means NO script) and + #the stdin form's FIRST word is fenced against non-dash words (a + #non-dash first word IS a script file) - so form auto-selection + #matches stock classification for every line except the complete + #'-encoding ' shape, which word-shape-matches both + #forms: stock's arity-priority rule (scriptfile wins when the triple + #is complete) is not expressible per-word, so the pair is declared + #@form -overlapallowed and a parse of that shape raises + #multipleformmatches naming both forms (G-041 no-silent-preference; + #the recorded G-168 LIMIT - dispatch itself never parses this + #definition, so the ambiguity surfaces only in declaration-driven + #consumers such as help dry-runs). @form -form {scriptfile} -synopsis " tclsh ?-encoding name? scriptfile ?arg ...?" @leaders -min 0 -max 0 @opts @@ -288,16 +301,32 @@ tcl::namespace::eval punk::args::moduledoc::punkexe { -encoding form falls through to the no-script form (stock behaviour)." @values -min 1 -max -1 - scriptfile -type string -optional 0 -help -& - "Script file to source and exit. ::argv0 and 'info script' are + scriptfile -type string -optional 0 -& + -regexprefail {^-} -& + -regexprefailmsg -& + "a tclsh script file argument may not begin with '-' (stock parity: a line whose script-file position holds a dash-led word is the no-script form - all arguments stay in ::argv)" -& + -help -& + "Script file to source and exit. May not begin with '-' (stock + classification - a dash-led word in this position selects the + no-script form instead). ::argv0 and 'info script' are the (normalized) script path; remaining args are ::argv. The script may set ::tclsh(dorepl) 1 (piperepl runtimes) to enter the interactive repl after it completes." arg -type any -optional 1 -multiple 1 -help -& "Arguments passed to the script in ::argv." - @form -form {stdin} -synopsis " tclsh ?arg ...?" + @form -form {stdin} -synopsis " tclsh ?arg ...?" -overlapallowed {scriptfile} @values -min 0 -max -1 + firstarg -type string -optional 1 -& + -regexprefail {^[^-]} -& + -regexprefailmsg -& + "a non-dash first word after 'tclsh' is a script file (the scriptfile form applies) - the no-script form takes only a leading '-' word here" -& + -help -& + "First argument. In this no-script form it must begin with '-' + (stock classification: a non-dash first word is a script file + and selects the scriptfile form). It is not an option - it + stays in ::argv untouched, exactly like the remaining + arguments." arg -type any -optional 1 -multiple 1 -help -& "With no script file all arguments (including any leading '-' arguments) stay in ::argv; ::argv0 is the executable path. @@ -353,12 +382,29 @@ tcl::namespace::eval punk::args::moduledoc::punkexe { use ' help script' for this usage." @form -form {file} -synopsis " script ?scriptfile|lib:name? ?arg ...?" @values -min 0 -max -1 - scriptfile -type string -optional 1 -help -& + scriptfile -type string -optional 1 -& + -regexprefail {^-e$} -& + -regexprefailmsg -& + "the script subcommand's scriptfile may not be exactly '-e' (reserved for the -e one-liner form; a file literally named -e is reachable as ./-e)" -& + -help -& "Script file path, or lib:name for scriptlib resolution. - Omitted: the script is read from piped stdin." + Omitted: the script is read from piped stdin. + Any first word other than exactly '-e' is a file path here - + including other dash-led words ('-x' names a file, not an + option; G-168 selection-soundness: the fence on exactly '-e' + makes form auto-selection agree with dispatch)." arg -type any -optional 1 -multiple 1 -help -& "Arguments passed to the script in ::argv." @form -form {oneliner} -synopsis " script -e