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-173 punk::path machine-parsable returns (text/dict/json) with stable conflict verdict

Status: achieved 2026-08-07 Scope: src/modules/punk-999999.0a1.0.tm (punk::path proc - add -return modes + -context); src/tests/modules/punk/path/testsuites/tests/ (return-shape + round-trip + ansi-freeness tests) Goal: punk::path results are consumable by programs and LLMs as well as humans - a -return option adds text, dict and json choices producing ansi-free output with a documented structure (ordered PATH entries; per-entry executables with overshadowing; TCL-context conflicts), the default table output is unchanged for existing users, and the JSON form uses tcllib json::write as the preferred encoder (the runtests json_emit investigation confirmed json::write is robust where a hand-rolled emitter carried a defect class - misclassified numbers emitted bare, unescaped C0 controls). Acceptance: -return gains text, dict and json choices whose output contains no ansi escapes regardless of -highlight; the returned structure is documented in the command's punk::args definition; the default table output is byte-unchanged for existing users (existing path.test cases stay green); -return json round-trips the same data as -return dict (decode via tcllib json, recompare); the conflict verdict is stable independent of how the consumer invoked the proc via a -context option (default the caller's namespace); new tests pin dict/json shape, ansi-freeness, and dict/json round-trip parity; a duplicate-path entry and an overshadowed executable each appear in the dict/json with the fields a machine consumer needs (idx, is_duplicate, duplicate_of, overshadowed, overshadowed_by, tcl_conflicts with match=exact|nocase).

Context

punk::path currently renders only an ANSI textblock::table; its conflict detection keys off the caller's namespace (uplevel 1 {namespace current}), meaningful interactively but fragile for a machine consumer invoked through a wrapper. G-049 established -return dict on punk::ns::cmdhelp (the in-repo machine-parsable convention); G-064 applies it to lib.search and adds json for agent consumers. This goal applies the same pattern to punk::path. The runtests json_emit hardening (done 2026-08-07, separate work) closed a defect class in a hand-rolled JSON emitter (string is entier -strict misclassified 007/+5/0x10 as bare JSON numbers; the string escaper left most C0 controls raw); that investigation named tcllib json::write as the robust preferred encoder, which this goal adopts.

Approach

Split punk::path into a collect phase (the existing d_path_info/d_bin_info/d_index_executables dicts, unchanged) plus a per-mode render/serialise branch: table (current textblock::table output, unchanged), text (fixed-key plain-ASCII layout, one line per executable, no colour/layout), dict (return the structure), json (serialise via tcllib json::write). Add -context (default uplevel 1) so the conflict verdict is explicit and stable for machine consumers, and -conflicts 0|1 (default 1) to skip it for a perf win.

Alternatives considered

  • Strip ANSI from the existing table for the text mode: rejected - column wrapping and box glyphs are layout-dependent and ambiguous to an LLM; a fixed-key plain layout is cleaner.
  • Hand-roll the JSON encoder for punk::path: rejected - the runtests emitter showed the defect class; json::write is already in the build (argparsingtest uses it) and is correct.

Notes

Related: G-064 (sibling surface - same -return dict/json pattern applied to lib.search; this goal's json-writer decision resolves G-064's open "json serialization mechanism" question by adopting tcllib json::write). Related: G-049 (achieved 2026-07-10 - established the -return dict machine-parsable pattern on punk::ns::cmdhelp that this goal extends to punk::path and adds json). Related: G-016 (sibling -return json round-trip consumer goal for projects.work; shares the json round-trip-parity acceptance idiom). overlap survey (activation-freshness 2026-08-07): G-042/G-019/G-061/G-110/G-167/G-171 surfaced as keyword/path-only with no surface overlap - deliberate non-overlap (checked: src/modules/punk-999999.0a1.0.tm, src/tests/modules/punk/path, src/tests/testsupport/json_emit.tcl). G-042 touches the same punk module file but a different surface (help topics via punk::config).

Progress

2026-08-07 (activated, implemented): ::punk::path gained -return text|dict|json (default table byte-unchanged), -context (default the caller's namespace - stable conflict verdict for machine consumers), and -conflicts 0|1 (default 1). The collect phase (d_path_info/d_bin_info/d_index_executables) is shared with the table render; the structured modes build a parallel ordered entry list and serialise without ANSI/textblock. JSON is emitted via tcllib json::write (the preferred encoder settled by the runtests json_emit hardening, G-173 Context) so booleans/counts are bare JSON numbers/1-0 and the decoded form equals the dict form byte-for-byte. Conflict verdicts use a clean exact|nocase classification (path_conflicts_for_exe) independent of the table render's ANSI path. New tests: src/tests/modules/punk/pathcmd/testsuites/tests/pathcmd.test (7 tests: dict shape, duplicate/overshadowed fields, json ansi-freeness + round-trip parity, text ansi-freeness, -context stability, -conflicts 0, default-table still ANSI). punk module buildversion 0.2.8 -> 0.3.0; punkproject.toml 0.57.1 -> 0.58.0. 2026-08-07 (acceptance verified, achieved): all acceptance criteria confirmed on tclsh9.0.5-punk (Tcl 9) and tclsh86ts (Tcl 8.6) - existing path.test 67/67 green (table output byte-unchanged), pathcmd.test 7/7 (dict/json shape, ansi-freeness, round-trip parity, -context stability), full runner/testsuites/parser suite 23/23. A closeout pass removed the line-continuation backslashes the G-173 helper procs had been authored with (dict-create + dict-set / json::write accumulator idioms per src/modules/AGENTS.md) and added scriptlib/developer/linecont_lint.tcl to enforce the rule going forward; the punk::args doc block was converted to -& record-continuation markers. These are quality/refactor items, not acceptance-bearing - acceptance was met by the implementation above.

Follow-ons

Follow-on: Apply linecont_lint to the 46 remaining pre-existing line-continuation findings in src/modules/punk-999999.0a1.0.tm (tech debt predating G-173, outside the G-173 edit sites) => open