Browse Source

G-174 drafted (proposed): cmd_traverse word accounting - the doc-lookup walk never drops argument words

Root-caused from 'i a? web green' presenting the loose sample form (ordinal 5)
instead of web (ordinal 3): punk::args form narrowing is sound (parse_status on
the full tail reports the web/sample ambiguity correctly); punk::ns::cmd_traverse
drops all but the last trailing word for commands whose definition's first form
is zero-arg (a?'s sgr_overview) - the dropped words land in neither args_resolved
nor args_remaining, so cmdhelp's advisory parse narrows on a one-word tail and
cleanly selects the wrong form with no failure signal.

The goal records the probe evidence, the four-step drop mechanism (first-form-only
inspection, ensemble-shape test, the '#todo - return?' empty-leader break, the
code-10 last-snapshot fallthrough), the consumer sensitivity split (docid-only vs
args_remaining-sensitive), and the coverage determination: cmdflow/cmdhelp pin
the choice-walk parity and post-resolution narrowing arcs, but nothing asserts
the conservation invariant, no fixture has the zero-arg-first-form shape, and the
code-7/code-10 lanes are unwitnessed. Approach: characterise first (defect
witnesses pinned, flipped by the fix commit), minimal fix at the annotated sites,
consumer verification, with first-form-only inspection and the ansi-side
sample-form tightening held as recorded decision items.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 3 days ago
parent
commit
c1f64ca0ab
  1. 4
      GOALS.md
  2. 118
      goals/G-174-cmdtraverse-word-accounting.md

4
GOALS.md

@ -400,3 +400,7 @@ Detail: goals/G-167-punkargs-version-delta-availability.md
Scope: src/make.tcl (bake/bakehouse/vfslibs - VFSPAYLOAD + PACKAGES phases, kit assembly, new explicit materialize/tidy commands); src/make.tcl sync_layouts phase (the mint-side tracked-path write) with its targets src/project_layouts/ and src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/ plus the workflow_text TERMINOLOGY/DIAGRAM 1b stage-verb assignment; src/vfs/ (payload declarations, _targets staging generalization, README.md + AGENTS.md precedence spec); src/_bake (assembly staging); bin/packages (lib-tier consumption); .gitignore + .fossil-settings/ignore-glob (staging ignore coverage); src/tests/shell/testsuites/punkexe/ (kill-probe + precedence characterization)
Detail: goals/G-171-bake-writes-land-untracked.md
### G-174 [proposed] cmd_traverse word accounting - the doc-lookup walk never drops argument words
Scope: src/modules/punk/ns-999999.0a1.0.tm (cmd_traverse/cmdinfo doc-lookup walk, cmdhelp advisory parse + synopsis consumers); src/tests/modules/punk/ns/testsuites/ns/{cmdflow,cmdhelp}.test (conservation characterisation + consumer pins); src/modules/punk/ansi-999999.0a1.0.tm (punk::ansi::a? argdoc - real-world witness, edits only if the sample-form tightening decision lands)
Detail: goals/G-174-cmdtraverse-word-accounting.md

118
goals/G-174-cmdtraverse-word-accounting.md

@ -0,0 +1,118 @@
# G-174 cmd_traverse word accounting - the doc-lookup walk never drops argument words
Status: proposed
Scope: src/modules/punk/ns-999999.0a1.0.tm (cmd_traverse/cmdinfo doc-lookup walk, cmdhelp advisory parse + synopsis consumers); src/tests/modules/punk/ns/testsuites/ns/{cmdflow,cmdhelp}.test (conservation characterisation + consumer pins); src/modules/punk/ansi-999999.0a1.0.tm (punk::ansi::a? argdoc - real-world witness, edits only if the sample-form tightening decision lands)
Goal: the doc-lookup walk behind punk::ns::cmdinfo has a stated, characterised word-accounting contract - every supplied trailing word lands in exactly one of args_resolved or args_remaining - so args_remaining-sensitive consumers (cmdhelp's advisory parse and G-041 form narrowing, synopsis form marking) always operate on the full unconsumed tail, and a command whose definition's first form is zero-arg (the punk::ansi::a? class) no longer has its literal discriminator words silently dropped before form narrowing runs.
Acceptance: characterisation tests in cmdflow.test assert the conservation invariant (args_resolved plus args_remaining account for every supplied trailing word, each word in exactly one) across the walk's return lanes - including an a?-shaped fixture (plain proc, multiform definition, zero-arg first form) with multi-word tails, plus witnesses for the previously unwitnessed code-7 and code-10 return lanes; punk::ns::cmdhelp for 'a? web green' runs its advisory parse on the full tail and presents the web/sample multipleformmatches ambiguity (web's argument table rendered first, both forms marked in the synopsis) rather than a clean sample-only render, and punk::ns::synopsis marks the same forms for the same words; the first-form-only inspection (fid = first of form_names) is either extended or pinned as a recorded limit with its own witness, with the decision recorded in this file; the punk::ns suite passes under the canonical runtests interpreter with no regressions in cmdflow/cmdhelp.
## Context
Root-caused 2026-08-07 from the user observation that 'i a? web green' presents the
help for the loose 'sample' form (ordinal 5, first value typed
string|stringstartswith(...)) instead of the 'web' form (ordinal 3, first value
literal(web)) - "why don't the literals win out?".
The form-narrowing machinery is NOT at fault. Parsed directly,
`punk::args::parse_status {web green} withid ::punk::ansi::a?` correctly reports
BOTH 'web' and 'sample' as clean matches (multipleformmatches, no silent
preference - the G-041 decision; achieved 2026-07-13, see
goals/archive/G-041-punkargs-form-matching.md), web first in declaration order, so cmdhelp would
render web's argument table and mark both forms. The literal-affinity ranking
(form_literal_affinity) only orders candidates in the no-form-matches failure
display and plays no role here.
The defect is one layer up: punk::ns::cmd_traverse (sole caller: cmdinfo's
coroutine spawn) drops all but the LAST trailing word for commands whose
definition's first form is zero-arg. Mechanism (line anchors are point-in-time,
ns-999999.0a1.0.tm as at 2026-08-07):
1. Each outer-loop iteration re-snapshots queryargs/queryargs_untested to
`[lrange $args $i end]` (~ns:5013) - a fresh tail per word.
2. The walk inspects only the spec's FIRST form - `set fid [lindex $form_names 0]`
(~ns:5036); the multiform-discrimination block beside it is commented out
("multiform discrimination not yet supported").
3. a?'s first form is sgr_overview (zero-arg overview form): no opts, no values,
so the ensemble-shape test `![llength $optnames] && ![llength $valnames]`
(~ns:5063) routes into the subcommand walk - but the form has no leaders
either, so leadernames_matched is empty and the foreach immediately hits
`if {$lname eq ""} break` (~ns:5073, annotated `#todo - return?` - the author
already suspected the spot). Nothing is consumed, returned, or restored.
4. Control falls back to the outer for, which just advances i and re-snapshots.
At loop exhaustion the code-10 fallthrough return (~ns:5309, annotated
`#REVIEW!!!`) reports the LAST snapshot: with N trailing words, N-1 are
silently discarded - present in NEITHER args_resolved nor args_remaining.
Probe evidence (punk91 src, 2026-08-07):
cmdinfo punk::ansi::a? web -> args_remaining={web}
cmdinfo punk::ansi::a? web green -> args_remaining={green} ("web" vanished)
cmdinfo punk::ansi::a? term pastel-> args_remaining={pastel} ("term" vanished)
cmdhelp's advisory parse then legitimately single-matches 'sample' on the
remaining word ('green' fails web's required literal(web) first value, matches
sample's string) - a CLEAN ok=1 narrow, so the mispresentation carries no failure
signal anywhere.
Consumer sensitivity split (all cmdinfo consumers in the dev tree):
- docid-only, immune to the drop: punk::ns::forms, punk::ns::eg, punk::args::eg,
punk::lib::tclscript_info, punk::args::ensemble_subcommands_definition.
- args_remaining-sensitive, inherit the corruption: punk::ns::cmdhelp (the 'i'
advisory parse / G-041 narrowing - the observed defect), punk::ns::synopsis and
synopsis_raw ('s' form marking from unresolved_args), cmdtrace/cmdtracebasic,
and cmdinfo's own curried-notfound recursion.
Coverage determination (2026-08-07 review of both suites): cmdflow.test (26 tests)
pins the choice-walk arcs - parse-parity per G-040 (achieved 2026-07-08,
goals/archive/G-040-punkargs-choicealiases.md) covering
denylist/reservelist/aliases/prefix/nocase, space-form docid jumps per G-051
(achieved 2026-07-13, goals/archive/G-051-cmdinfo-pseudo-and-prefix.md),
unavailable attribution per G-166 (achieved 2026-08-06,
goals/archive/G-166-cmdinfo-unavailable-surfacing.md), ensemble -parameters
traversal. cmdhelp.test (~50 tests) pins rendering and
POST-resolution multiform narrowing (the G-041/G-143/G-164 arcs; G-143 achieved
2026-08-01, goals/archive/G-143-maketcl-multiform-help.md; G-164 achieved
2026-08-04, goals/archive/G-164-viability-alternative-allocation.md), doconly/'string is'
real-world pins, oo methods. Many tests assert args_remaining values, but always
through subcommand-walk or single-trailing-word scenarios. Nothing asserts the
conservation invariant; no fixture has the zero-arg-first-form shape; the code-7
lane's own source comment names its test ('i namespace which -v x') but no such
test exists; code-10 has none; the first-form-only inspection is unpinned. The
defect lives precisely in the uncovered lanes.
## Approach
1. Characterise first, fix second (defect witnesses included): add cmdflow.test
coverage asserting the conservation invariant across the walk's return lanes,
an a?-shaped fixture (plain proc, multiform definition, zero-arg first form)
exercised with multi-word tails, and witnesses for the code-7 and code-10
lanes. Pin the CURRENT dropping behaviour as the defect witness, flipped by
the fix commit.
2. Fix minimally at the annotated sites: return the remaining tail from the
empty-leader break (code-8-like return honouring `#todo - return?`), and/or
tighten subcommand-walk eligibility so a level is only walkable when the
inspected form actually has a choices-bearing leader. Choose the smallest
change that restores conservation without disturbing the characterised
parity/ensemble/unavailable lanes.
3. Consumer verification: cmdhelp ('i a? web green' ambiguity presentation,
-return text parity) and synopsis form marking on the same words.
4. Decision item - first-form-only inspection (fid = form_names[0]): default is
pin-as-recorded-limit with a witness; cross-form discrimination during
traversal is its own follow-on goal if wanted.
5. Decision item (optional, ansi-side): whether a?'s 'sample' form stays
string-loose so discriminator words genuinely ambiguate (default: accept -
the marked-ambiguity presentation IS the G-041 contract), or the argdoc is
tightened. No ansi edit unless this lands the other way.
## Notes
- The a? argdoc (ansi-999999.0a1.0.tm ~3213-3279) is the trigger and real-world
witness, not an edit target: forms sgr_overview/term/tk/web/x11/sample, with
term/tk carrying their literals as @leaders while web/x11 carry them as
@values - both shapes should appear in the fixture.
- Single-word tails survive today only because the last word is always the final
snapshot - 'i a? web' working is luck, not contract.
- args-side channels are already adequate: parse_status reports
multipleformmatches with per-form formstatus (all-valid keys) and noformmatch
with ranked -formerrors; cmdhelp consumes them correctly. No punk::args change
is expected under this goal.
Loading…
Cancel
Save