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.
 
 
 
 
 
 

6.3 KiB

G-073 punk::args unavailable choices: displayed with notes and prefix-reserving, but rejected with a tailored message

Status: proposed Scope: src/modules/punk/args-999999.0a1.0.tm (spec key, choiceword_match pool, choices rendering in arg_error table+string renderers, validation message), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm ('string is' forward-class adoption + per-class virtual id), src/tests/modules/punk/args/testsuites/args/ (new suite + tclcoreparity.test exemption) Goal: an argument definition can declare choices that are recognised but not available in the current runtime (new key, candidate spelling -choiceunavailable ): they display among the choices with their ordinary -choicelabels notes (visually distinguished), participate in prefix disambiguation like -choiceprefixreservelist phantoms, and are rejected at parse with a tailored message identifying the unavailability rather than the generic listed-values error - adopted by the tclcore 'string is' model so that Tcl 8.6 shows the dict class annotated "(class not present in Tcl 8.6)", rejects 'string is dict' informatively, treats 'string is di' as ambiguous (deliberately stricter than real 8.6, preparing 8.6 users for 9.x where dict/digit/double collide), and documents 'i string is dict' via a per-class virtual id generated from the static description. Acceptance: a definition using the new key renders unavailable entries distinguishably among the choices (mechanism decided in the work: dedicated group heading, dim/warn styling, or marker) with their -choicelabels notes, in both the table and string renderers, and synopsis choice-literal display excludes them; parse/parse_status reject an unavailable choice word - exact, or a unique prefix resolving only to it - with a message identifying it as recognised-but-unavailable rather than the generic choice error; unavailable entries participate in prefix disambiguation via choiceword_match (a prefix shared between an available and an unavailable choice is rejected as ambiguous) and are honoured identically by the punk::ns doc walk and the G-071 allocation screen (parity free via the shared resolver - no second matching rule); value-in-effect/goodarg highlighting never marks an unavailable entry; the tclcore 'string is' definition adopts the key on runtimes lacking known forward classes (dict on 8.6; the forward list is explicitly curated in the definition - unicode deliberately excluded as never-released), with a per-class virtual id generated from the static description so 'i string is dict' documents the class with its note on 8.6; the deliberate strictness divergence (real 8.6 accepts 'string is di' as digit; the model rejects it as ambiguous) is recorded in tclcoreparity.test as a user-sanctioned exemption class rather than silently special-cased, with full-word behaviour staying parity-true on both versions; definitions without the key behave unchanged (full punk::args and punk::ns suites pass).

Context

User requirement 2026-07-12: forward compatibility for the 8.6-vs-9 'string is dict' difference. The G-054 harvest (archived) makes the class choices accurate per-runtime, so 8.6 neither displays nor mentions dict - but an 8.6 programmer who habituates to 'string is di' (unique prefix of digit on 8.6) writes code that breaks on 9.x where dict makes 'di' ambiguous. The requirement: 8.6 should SEE dict (with an unavailability note - the description text with its "(class not present in Tcl 8.6)" annotation already exists in the static string_is_class_descriptions dict, it just never renders on 8.6), have dict participate in prefix disambiguation (so 'di' is ambiguous on 8.6 too), and still reject dict as an actual argument (real 8.6 rejects it).

Capability assessment (2026-07-12/13): the display layer is ready - choicelabels notes, choicegroups headings and choiceinfo markers all exist; the gap is purely that choices-cell membership implies parse acceptance under -choicerestricted 1. The trie mechanics exist as -choiceprefixreservelist (phantom prefix-calculation members, G-040), but reservelist phantoms deliberately do not display (the punk::help c/to/tc phantoms must stay invisible) and reject with the generic choice error. Hence a first-class concept: ~80% parse semantics, ~20% display, riding the existing rendering pipeline.

Approach

  • New per-argument key (spelling decided in the work; candidate -choiceunavailable ). Entries must not also appear in -choices/-choicegroups (definition error). Notes come from ordinary -choicelabels entries keyed by the unavailable word.
  • choiceword_match: unavailable entries join the prefix pool like reservelist members, but a match landing on one returns a distinct indication so validation can emit the tailored message ("'dict' is a recognised choice but not available in this runtime - see its note") instead of the generic listed-values error. The shared-resolver principle keeps parse, parse_status, the G-071 allocation screen and the (G-051) doc walk automatically consistent.
  • Display: render unavailable entries among the choices with their notes, visually distinguished (group heading vs dim/warn styling vs marker - decided in the work with display-cost in mind); excluded from value-in-effect and goodarg marking; excluded from synopsis choice-literal alternates.
  • tclcore adoption: the 'string is' define-time harvest computes unavailable = curated_forward_list - live_classes (forward list: dict; unicode excluded). The per-class virtual id loop extends to unavailable classes using the static description so 'i string is dict' works as documentation on 8.6.
  • Parity: tclcoreparity.test gains a recorded exemption class for prefix-collision strictness (model stricter than real 8.6 for 'di'-shaped prefixes by user direction); full-word probes stay strict parity.

Notes

  • Related: G-040 (choiceword_match single-resolver principle and the reservelist precedent), G-051 (doc walk consumes the resolver - achieved 2026-07-13), G-071 (allocation screen consumes the resolver - achieved 2026-07-12), G-054 archived (harvest + the parity pins this goal amends), G-055 (version-adaptive definition technique; its workflow guidance gains the forward-class pattern once proven here).
  • The reject-message wording should steer the user: name the note/annotation and the version boundary, not just "invalid".