8.5 KiB
G-152 punk::args form-failure suffix-viability verdict (completion-grade classification)
Status: achieved 2026-08-02 Scope: src/modules/punk/args-999999.0a1.0.tm (multiform failure classification + noformmatch errorcode/formstatus consumer contract + render marking); src/tests/modules/punk/args/testsuites/args/ Goal: A multiform parse failure reports a SOUND suffix-viability verdict per candidate form - a form is marked viable if and only if every supplied word was validly consumed as a prefix of that form and the failure is pure exhaustion at end-of-input (nothing already supplied contradicts the form) - exposed through a documented, stable machine-readable contract (the noformmatch errorcode classes / formstatus records) so consumers (cmdhelp narrowing, make.tcl help, G-044 completion) can select the still-satisfiable form, and marked distinguishably in the rendered per-form failure report. Acceptance: against a tool-shaped multiform definition (literal action leaders; the info form requiring a value), parse of {info} reports form info viable and list/build/test non-viable; parse of {frobnicate} reports NO form viable (today's false 'incomplete' on the info form is gone); parse of {build -bogus} reports no form viable; the class vocabulary and errorcode/formstatus shape carrying the verdict are documented as a consumer contract in the module's own argdoc/doc surface; the rendered noformmatch output visibly distinguishes viable form(s) from impossible ones; existing args-suite pins pass unchanged apart from deliberately updated classification pins; the new verdicts are pinned in the args testsuite.
Context
Raised by user review of 'make.tcl help tool info' output (2026-08-02, during the follow-on session to G-143, achieved 2026-08-01 - goals/archive/G-143-maketcl-multiform-help.md): the one form still on track to be satisfied by supplying MORE args should be differentiable from forms that are impossible regardless of what is appended - feeding form selection and command-line completion. Probes (bootsupport 0.13.0 and dev module) found the channel largely present: the noformmatch throw carries -errorcode {PUNKARGS VALIDATION {noformmatch forms {...} classes {...}}} with forms RANKED best-first plus full -argspecs in the return options; the dev module adds per-form formstatus records (status/failureclass/badarg/message), rank_form_failures (literal-affinity + incomplete-first ordering) and the non-throwing parse_status surface. The GAP is semantic soundness, witnessed: parse {info} classes the info form 'incomplete' (correct - exhaustion), but parse {frobnicate} produces the identical classes, also granting info 'incomplete' although no suffix can satisfy it (the wrong word occupies the required leader slot). 'incomplete' today means "a required element was missing when parsing stopped", not "the supplied words are a valid prefix" - the classifier can grant it after mis-seating a contradicting word. Secondary gaps: the class vocabulary is not documented as a stable contract, and the rendered report differentiates only in prose kind, without marking viability.
Notes
- Related: G-044 (proposed) - punk::args-driven interactive completion; this verdict is its core enabler (viable forms -> what tokens may come next).
- Related: G-150 (proposed) - cmdhelp form narrowing; a narrowing consumer of the same verdict for failed/incomplete lines (its accepted-line narrowing needs only parse success).
- Related: G-050 (proposed) - synopsis argument-validity marking; sibling status-aware render surface for the marking half.
- Related: G-074 (achieved 2026-07-13 - goals/archive/G-074-punkargs-multiform-ambiguity-lint.md) - static form-overlap analysis (formcheck); this goal is the RUNTIME per-invocation counterpart over supplied words, sharing the parse_status machinery.
- Overlap survey 2026-08-02: goals_xref paths src/modules/punk/args-999999.0a1.0.tm - the punkargs family as recorded in G-149's survey (G-044 G-050 named above; G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088 are unrelated parser/spec/render mechanisms); session siblings G-149/G-151 are render-surface work, this is parse-classification.
- Probe evidence (2026-08-02, bootsupport 0.13.0): {info} ->
classes {incomplete invalid invalid invalid}over rankedforms {info list build test}; {frobnicate} -> identical (the false positive); {build -bogus} -> all invalid. Dev classifier: rank_form_failures orders by form_literal_affinity then incomplete-first. - Conservatism (documented in both argdoc contracts): input ending INSIDE a multi-member type clause (e.g
coord -type {int int}supplied one word) reports invalid - clause allocation walks reversed member types and cannot affirm partial words. Candidate refinement if G-044 needs it: a forward partial-member check at the in-loop missing-required raise sites. - Recorded for G-044 (completion): a trailing DEFINED flag word before an unconstrained optional value is consumed as a VALUE (the engine's trailing-flag-as-value rule), so such lines PARSE (toolname swallows '-test') and no verdict question arises; a trailing flag before a RESTRICTED choice value seats as that value and fails its choice gate -> verdict invalid, even though appending the flag's value word would re-seat it as an option ({bake -confirm} vs {bake -confirm 0}). The verdict is deliberately seating-based per this goal's Goal wording (and the G-143-era declaration-authoritative choice-gate decision); a completion consumer wanting flag-pending-value awareness needs its own lookahead.
- Bootsupport punk::args remains 0.13.0 after this goal: make.tcl help dry-run output gains the verdict marking only when bootsupport is next refreshed (maintenance/G-150-era step; no acceptance dependency - acceptance ran against the src module via runtests).
Progress
- 2026-08-02 IMPLEMENTED (punk::args 0.14.0, punkshell 0.41.3): verdict computed by a viability-probe mode on the single-form engine (
private::get_dict_formoptional 4th arg) rather than by remapping failure classes. The probe suppresses exactly the pure-exhaustion raises - below-minimum leading/trailing count shortfalls and missing-required leader/option/value checks, each position-guarded (leaders only with no option/value words consumed; options only before value words and any eopts--; values always), plus an adhoc option's value lying beyond the supplied words - and lets every contradiction raise stand INCLUDING final validation of the consumed words (the soundness backstop; the engine validates types/choices only AFTER the count/required checks, which is exactly why the raw failure classes could not carry the verdict, and why the G-071 screen asymmetry - basic types screened even for required args, choices deliberately unscreened - made {frobnicate} class identically to {info}). Multiform candidacy confirms each classify-'incomplete' per-form failure with one probe re-parse before recording it (contradiction-classed failures skip the probe); parse_status's single-form formstatus record gets the same confirmation; parse_status's top-level status keeps the engine classification for single-form parses (display-scheme driver) - the split is documented in the argdoc. - 2026-08-02 VERIFIED (native tclsh 8.7 via runtests.tcl): acceptance scenarios pinned in new
src/tests/modules/punk/args/testsuites/args/formviability.test(10 tests: {info} -> classes {incomplete invalid invalid invalid} with the viable info form ranked first; {frobnicate} -> all invalid, overall parse_status status now 'invalid'; {build -bogus} -> all invalid; -formerrors/formstatus agreement; success-path verdicts; required-option position guards; single-form formstatus soundness incl the engine-vs-verdict split on a type-screen rejection; clause-partial conservatism; message marking). Contract documented in the parse argdoc ('Multiform failure contract' section: errorcode shape + class vocabulary) and the parse_status argdoc (formstatus verdict semantics). Rendered noformmatch marks each form line '(viable - needs more arguments)' / '(not viable)' after the stable "form '':" anchor (anchor pinned unchanged). args subtree green (28 files + the new suite); full source-tree run at exact parity with a stashed-baseline run (the same 21 pre-existing environment failures on this box - twapi-less tcl 8.7a6 child SIGILLs in repl consolebackends/nscurrent, core exec.test, argparsingtest, ansi grepstr, lib compat lpop edge, ns nslist/nsprimitives, zip zipreader - none args-related). forms.test verdict pins deliberately updated: formstatus ms/idle for {cancel after#1} 'incomplete'->'invalid' (words failing their type screens are contradictions).