8.6 KiB
G-179 punk::args discriminator shadowing - literal-backed forms shadow loose-typed clean matches
Status: proposed Scope: src/modules/punk/args-999999.0a1.0.tm (clean-match candidacy filter at the multipleformmatches seam, -formselection declaration + define-time validation, formstatus shadowed surface, form_literal_affinity reuse); src/modules/punk/ansi-999999.0a1.0.tm (punk::ansi::a? argdoc adopts literalshadow); src/modules/punk/ns-999999.0a1.0.tm (synopsis markforms + cmdhelp narrowing exclude shadowed forms); src/tests/modules/punk/args/testsuites/args/ (formselection candidacy/parse/parse_status pins); src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (deliberate G-174 presentation-pin flips) Goal: punk::args multiform definitions can declare once, at definition level (-formselection literalshadow on @cmd), that leading-discriminator agreement resolves clean-match ambiguity: a form whose leading discriminator run (literal/literalprefix alternates or restricted choice sets - form_literal_affinity's existing notion, leaders-then-values, shared choiceword_match resolution) agreed with the supplied words shadows forms that accepted the same word positions only via loose types - so else-branch dispatch commands like punk::ansi::a? are modelled without cross-referential type exclusions, punk::args::parse selects the discriminated form instead of raising multipleformmatches, help/synopsis narrowing follows, loose-vs-loose ambiguity stays honestly reported, shadowed forms remain introspectable in formstatus, and undeclared definitions keep today's G-041 behaviour byte-identically. Acceptance: with a fixture declaring -formselection literalshadow: punk::args::parse of a line cleanly matching a literal-discriminated form and a loose form selects the discriminated form (no multipleformmatches raise) and parse_status reports ok=1 with that form while the shadowed form remains introspectable in formstatus (surface shape decided and recorded in this file - additive field per the G-167 style, or a distinct status value - with punk::ns synopsis markforms and cmdhelp narrowing excluding shadowed forms either way); a mixed-alternate argument matched via its loose alternate does not shadow; a restricted-choice discriminator shadows like a literal; two loose forms matching together still report multipleformmatches; noformmatch lines keep the existing ranked candidate display; the incomplete-line interaction (G-152/G-164 viability probe) has a decided, pinned behaviour recorded in this file; an unknown -formselection value is a define/resolve-time error; definitions without the declaration parse and render byte-identically (full punk/args suite green, none weakened); punk::ansi::a? adopts the declaration so 'i a? web green' renders web's usage alone and 's a? web green' marks web alone, with the three cmdhelp.test G-174 presentation pins deliberately flipped while cmdflow.test's G-174 conservation pins (args_remaining={web green}) hold unchanged; the punk/args and punk/ns suites pass under the canonical runtests interpreter.
Context
Follow-through from G-174 (achieved 2026-08-09, goals/archive/G-174-cmdtraverse-word-accounting.md). With the walk conserving the tail, 'i a? web green' now presents the web/sample multipleformmatches ambiguity - technically honest against the DECLARATION, but false of the FUNCTION: a?'s implementation is if/elseif/else dispatch (literal discriminators first, treat-as-colourcodes as the else branch), so the sample form can never receive a first word equal to a sibling form's literal. The declaration over-admits. Patching sample's -type alternates to exclude sibling literals would be cross-referential and drift-prone (user-rejected direction, 2026-08-09): each new panel form would silently invalidate the loose form's type list.
Two machinery facts make a declarative fix cheap:
- form_literal_affinity (args tm ~7683) already classifies leading discriminator runs (literal/literalprefix alternates AND restricted choice sets, leaders-then-values, shared choiceword_match resolver) - today ranking/display-only by the G-041 decision ("candidacy itself always comes from real parse attempts").
- punk::args::parse raises multipleformmatches on several clean matches (~10738, "no silent preference"). The flag therefore has parse-level payoff, not just presentation: it is what would let a?-class procs adopt punk::args::parse for real dispatch.
G-167 (proposed) already carves a deliberate, declared exception to no-silent-preference at the same candidacy seam (available form beats unavailable form - "documentation never competes with reality"). G-179 is the discriminator-based sibling: declared literal agreement encodes dispatch reality.
Approach
- Filter at the clean-match seam only: where candidacy today lands in the multipleformmatches arms (parse raise ~10815, parse_status ~7297/~8473), apply the shadow rule when the definition declares literalshadow; noformmatch ranking and all single-match paths untouched. No additional by-id resolves (G-175 concern) and no cost on the common paths (G-177 concern) - the filter runs only when several forms matched cleanly.
- Reuse/extend the form_literal_affinity walk to report agreed word indexes and whether agreement came via a discriminator alternate (matched-alternate precision: literal(web)|string matched via string must not shadow).
- Declaration storage + define/resolve-time validation of the -formselection value (spelling decision recorded: @cmd flag vs a bare directive like @dynamic).
- Consumers: punk::ns::synopsis markforms and cmdhelp's -form narrowing exclude shadowed forms (shape depends on the formstatus surface decision).
- Adoption: a? argdoc declares literalshadow; flip the three cmdhelp.test G-174 presentation pins (dict ok/failureclass/formstatus keys, text section set, synopsis marking); cmdflow.test conservation pins unchanged.
- Same change-set as this draft: the G-174 archived Follow-on ("tighten a?'s loose 'sample' form" => declined) re-dispositioned to => goal G-179 - this goal enacts its intent by declaration-level means rather than the type-exclusion approach the decline was about.
Alternatives considered
- Per-form fallback tier (@form -matchtier fallback: form participates only when no normal-tier form matches) - deferred, composable later; models non-literal else-branches but encodes relative standing without cause and leaves literal-backed vs loose overlap among normal forms unsolved.
- Ordered choice (first clean match in declaration order wins) - rejected: silently prefers among ANY pair including genuinely ambiguous loose pairs; over-broad.
- Per-type exclusion lists on the loose form - rejected: cross-referential drift trap (the motivating complaint).
Notes
- Related: G-041 (archived - established no-silent-preference as the DEFAULT; this is an opt-in declared carve-out, default byte-identical). G-167 (proposed - sibling candidacy filter at the same seam with its own declared exception and the additive formstatus-field style; whichever lands second inherits a merge obligation at the filter seam). G-152/G-164 (archived - viability/incomplete-line semantics the filter must compose with; decision item). G-150 (archived - flag-led narrowing pins on the selection-sound hash declaration; same contract family). G-083 (archived - declaration-level relational semantics precedent incl define-time validation and render annotations). G-174 (archived - the consumer surface that exposed the mismatch; its cmdhelp presentation pins flip deliberately here). G-175 (proposed - render/resolve multiplicity: the filter must add no by-id fetches). G-177 (proposed - scale: filter cost confined to the ambiguity path). G-072 (proposed - compound clause types will extend the type-alternate vocabulary; the discriminator classification must stay coherent with it).
- Decision items to record during the work: formstatus surface shape (additive 'shadowed' field per G-167 style vs distinct status value - constraint: existing status-eq-valid consumers must not mark shadowed forms); -formselection spelling (@cmd flag vs bare directive); viability-probe interaction; optional usage-render annotation on shadowable loose forms (G-083 'mutually exclusive'-note style, e.g marking the else-branch form).
- Survey note: overlap survey run 2026-08-09 (goals_xref paths over the scope); surfaced args-scoped goals not listed above (G-044, G-050, G-055, G-056, G-068, G-069, G-075, G-088, G-178) reviewed and judged orthogonal (render/lint/registration concerns, no candidacy-semantics contact); G-050 relates only through the already recorded G-174 linkage.