From 543dd1df403fba0d7dba58830d79f07bf01e47e2 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sat, 1 Aug 2026 21:23:29 +1000 Subject: [PATCH] G-144 achieved: punk::args auto-synopsis @cmd -name fallback for (script) ids Auto-generated synopses for (script)-prefixed constructed definition ids now lead with the declared @cmd -name (the invocation name - e.g 'make.tcl modules' for (script)::punkboot::modules) instead of the raw id, across punk::args::synopsis full/summary renders and the usage/arg_error synopsis sections that funnel through them (punk::args 0.12.7 -> 0.13.0). The fallback is gated on the (script) prefix only (user-confirmed narrowing): (autodef) ids are excluded - their id-minus-tag IS the invocation (arg_error's existing tag strip surfaces it) while their @cmd -name is a display label ('destroy', 'Object: ::x'); (shared)/(package)/(default)/(widgetcommand) doc ids declare no invocation-style -name today and render the id as before. Real command ids render byte-identical (full args testsuite passes with no pin updates). Six new synopsis.test pins: (script) full/summary/multiform/ surfaces, no-name constructed id, autodef unchanged, real-id byte-identical. Goal tiers: index entry archived to GOALS-archive.md, detail file moved to goals/archive/ (G-143's pending-tense Related line rewritten to reflect achievement + follow-through pointer: make.tcl's @form -synopsis overrides are now optional, retirement under G-143 or a follow-on edit). Docs: @form -synopsis directive known-case text, synopsis -help, src/modules/AGENTS.md ergonomics bullet. Project 0.39.5 -> 0.39.6 (patch: latent capability, no shipped definition renders differently today). Bootsupport refresh (make.tcl modules + bootsupport): snapshot gains args-0.13.0.tm and picks up two lagging source syncs - mix/commandset::layout 0.2.0 -> 0.3.0, mix/commandset::project 0.4.0 -> 0.4.1, and the shellfilter 0.2.4 G-145 comment; superseded copies pruned. Assisted-by: harness=opencode; primary-model=openrouter/moonshotai/kimi-k3; api-location=openrouter.ai --- CHANGELOG.md | 12 ++ GOALS-archive.md | 4 + GOALS.md | 4 - goals/G-143-maketcl-multiform-help.md | 2 +- .../G-144-punkargs-synopsis-name-fallback.md | 24 --- .../G-144-punkargs-synopsis-name-fallback.md | 44 +++++ punkproject.toml | 2 +- .../punk/{args-0.12.7.tm => args-0.13.0.tm} | 42 ++++- .../{layout-0.2.0.tm => layout-0.3.0.tm} | 154 ++++++++++++++++-- .../{project-0.4.0.tm => project-0.4.1.tm} | 15 +- src/bootsupport/modules/shellfilter-0.2.4.tm | 8 + src/modules/AGENTS.md | 2 +- src/modules/punk/args-999999.0a1.0.tm | 36 +++- src/modules/punk/args-buildversion.txt | 3 +- .../punk/args/testsuites/args/synopsis.test | 131 +++++++++++++++ 15 files changed, 418 insertions(+), 65 deletions(-) delete mode 100644 goals/G-144-punkargs-synopsis-name-fallback.md create mode 100644 goals/archive/G-144-punkargs-synopsis-name-fallback.md rename src/bootsupport/modules/punk/{args-0.12.7.tm => args-0.13.0.tm} (99%) rename src/bootsupport/modules/punk/mix/commandset/{layout-0.2.0.tm => layout-0.3.0.tm} (79%) rename src/bootsupport/modules/punk/mix/commandset/{project-0.4.0.tm => project-0.4.1.tm} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc58fc5c..94c9c216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.39.6] - 2026-08-01 + +- punk::args 0.13.0 (G-144): auto-generated synopses for `(script)`-prefixed + constructed definition ids now lead with the declared `@cmd -name` (the + invocation name - e.g `make.tcl modules` for `(script)::punkboot::modules`) + instead of the raw id, across the punk::args::synopsis full/summary renders + and the usage/arg_error synopsis sections that funnel through them. + `(autodef)` and other constructed id families are deliberately unchanged + (their id-minus-tag is already the invocation, or no invocation-style + -name exists); explicit `@form -synopsis` overrides still win, so make.tcl + and punkexe help render as before - retiring those overrides is a follow-on. + ## [0.39.5] - 2026-08-01 - Project-layout template payloads no longer self-censor in git (G-012): layout diff --git a/GOALS-archive.md b/GOALS-archive.md index 500ba1bd..d3730164 100644 --- a/GOALS-archive.md +++ b/GOALS-archive.md @@ -271,6 +271,10 @@ Acceptance: a mapvfs toml file parsed via tomlish (no ad-hoc toml parsing) drive Scope: src/make.tcl (vfs assembly); src/runtime/vendorlib_vfs.toml (existing per-package declaration surface - fold/supersede settled in the work); src/vfs/ (per-.vfs declaration files + README); punk::mix machinery as touched; src/project_layouts (seeding for derived projects); coordinates with G-067 (artifact sources); G-006 (consent); G-004 (binary-free committed tree) Acceptance: a punkshell kit .vfs (or demonstration .vfs) builds from a toml declaration reproducing its payload on a clean tree, with declared binary content arriving via consented retrieval or local build products; an undeclared dropped-in file survives re-materialization per the documented precedence; a .vfs with NO declaration builds exactly as today (pure drop-in mode unchanged); the declaration format and precedence rules are documented; the vendorlib_vfs.toml relationship is settled with rationale; the layout store seeds the convention for derived projects. +### G-144 [achieved 2026-08-01] punk::args auto-synopsis: @cmd -name fallback for non-command ids → detail: goals/archive/G-144-punkargs-synopsis-name-fallback.md +Scope: src/modules/punk/args-999999.0a1.0.tm (synopsis renderer + usage/arg_error synopsis display); src/tests/modules/punk/args/testsuites/args/ (synopsis pins) +Acceptance: a definition with @id -id (script)::x::sub and @cmd -name 'make.tcl sub' auto-renders synopsis lines beginning 'make.tcl sub' across the punk::args::synopsis, usage and arg_error surfaces (table and string renderers), with every form line of a multi-form definition carrying the name; definitions whose id equals the invocation name render byte-identical to today (existing synopsis/usage/arg_error suites pass unchanged apart from deliberately updated pins); a definition with neither a command-path id nor @cmd -name renders the id as today; the new behaviour is pinned in the args testsuite. (Achieved with the fallback gated on the (script) prefix only - user-confirmed narrowing 2026-08-01: the (shared)/(package) families the Goal statement names declare no invocation-style -name today, and (autodef)'s -name is a display label that must not lead a synopsis line. Survey and evidence in the detail file.) + ### G-145 [achieved 2026-08-01] Remnant-free piped usage tables: root-cause split ANSI fragments → detail: goals/archive/G-145-piped-usage-ansi-remnants.md Scope: src/make.tcl (::punkboot::ansistrip transform); src/modules/textblock-999999.0a1.0.tm + src/modules/punk/ansi-999999.0a1.0.tm (ANSI-aware cell/width handling as implicated); src/tests/shell/testsuites/punkexe/maketclcolour.test (remnant pin) Acceptance: piped 'tclsh src/make.tcl help ' for every declared subject plus bare 'make.tcl -help' yields output with no ESC bytes and no CSI-remnant substrings (regex {\[[0-9;:]*m}; the help corpus carries no such literal text), pinned in maketclcolour.test; the root cause and chosen fix layer are recorded in this file; existing colour-policy pins (tty/forced/nocolor modes) pass unchanged. diff --git a/GOALS.md b/GOALS.md index 43e8e738..7a859e34 100644 --- a/GOALS.md +++ b/GOALS.md @@ -398,10 +398,6 @@ Detail: goals/G-142-punkbin-listing-manifests.md Scope: src/make.tcl (::punkboot::argdoc definitions + help dispatch); src/tests/shell/testsuites/punkexe/ (new piped help characterization test) Detail: goals/G-143-maketcl-multiform-help.md -### G-144 [proposed] punk::args auto-synopsis: @cmd -name fallback for non-command ids -Scope: src/modules/punk/args-999999.0a1.0.tm (synopsis renderer + usage/arg_error synopsis display); src/tests/modules/punk/args/testsuites/args/ (synopsis pins) -Detail: goals/G-144-punkargs-synopsis-name-fallback.md - ### G-146 [proposed] Fossil binary-glob seeding for generated projects (initial commit completes) Scope: src/project_layouts/ (.fossil-settings payloads); src/make.tcl (thin-layout sync, if derivation chosen); src/modules/punk/mix/commandset/project-999999.0a1.0.tm (generation-time fossil commit path) Detail: goals/G-146-fossil-binary-glob-seeding.md diff --git a/goals/G-143-maketcl-multiform-help.md b/goals/G-143-maketcl-multiform-help.md index 8fa17b20..554d27d4 100644 --- a/goals/G-143-maketcl-multiform-help.md +++ b/goals/G-143-maketcl-multiform-help.md @@ -19,7 +19,7 @@ G-030 (achieved 2026-07-14 - goals/archive/G-030-maketcl-punkargs.md) made make. ## Notes -- Related: G-144 (sibling proposal) - auto-synopsis @cmd -name fallback; until it ships, per-form synopsis lines are hand-written (@form -synopsis, bracket notation preferred) or would display definition ids. +- Related: G-144 (achieved 2026-08-01 - see goals/archive/G-144-punkargs-synopsis-name-fallback.md) - auto-synopsis @cmd -name fallback shipped in punk::args 0.13.0: (script)-prefixed ids now auto-render per-form synopsis lines with the declared @cmd -name in bracket notation, so this goal's multi-form definitions need no @form -synopsis overrides for the name, and make.tcl's existing hand-written overrides become optional (G-144 recorded their retirement as this goal's consumer follow-through, or a small standalone edit if this goal is not active). - Related: G-025 (proposed) - the future version-report subcommand should be born to these conventions (carried from G-030's notes). - Overlap survey 2026-08-01: goals_xref paths src/make.tcl scores no intersects (make.tcl is a df-damped ubiquitous anchor); manual index sweep - the live make.tcl-scoped goals (G-005 G-006 G-012 G-019 G-023 G-026 G-027 G-028 G-029 G-063 G-065 G-067) are build/vendor mechanics with no help/dispatch overlap; punkexe-test-scoped goals (G-077 G-131 G-141) unrelated. - Archived-goal references in this file: G-030 achieved 2026-07-14 (goals/archive/G-030-maketcl-punkargs.md); G-045 achieved 2026-07-12 (goals/archive/G-045-punkargs-authoring-ergonomics.md); G-104 achieved 2026-07-25 (goals/archive/G-104-maketcl-buildsuite-surface.md) - buildsuite surface whose passthrough contract this preserves; G-126 achieved 2026-07-27 (goals/archive/G-126-punkzip-accelerator.md) - origin of the tool subcommand. diff --git a/goals/G-144-punkargs-synopsis-name-fallback.md b/goals/G-144-punkargs-synopsis-name-fallback.md deleted file mode 100644 index 014f6ccd..00000000 --- a/goals/G-144-punkargs-synopsis-name-fallback.md +++ /dev/null @@ -1,24 +0,0 @@ -# G-144 punk::args auto-synopsis: @cmd -name fallback for non-command ids - -Status: proposed -Scope: src/modules/punk/args-999999.0a1.0.tm (synopsis renderer + usage/arg_error synopsis display); src/tests/modules/punk/args/testsuites/args/ (synopsis pins) -Goal: Auto-generated synopses render their invocation name from @cmd -name whenever the definition id is not the command's invocation name (script-level ids like (script)::punkboot::modules and (shared)/(package)-style doc ids), falling back to the id only when no @cmd -name is declared - so consumers get the automatic bracket-notation synopsis with the real command name, and explicit @form -synopsis overrides become optional rather than mandatory for such ids. -Acceptance: a definition with @id -id (script)::x::sub and @cmd -name 'make.tcl sub' auto-renders synopsis lines beginning 'make.tcl sub' across the punk::args::synopsis, usage and arg_error surfaces (table and string renderers), with every form line of a multi-form definition carrying the name; definitions whose id equals the invocation name render byte-identical to today (existing synopsis/usage/arg_error suites pass unchanged apart from deliberately updated pins); a definition with neither a command-path id nor @cmd -name renders the id as today; the new behaviour is pinned in the args testsuite. - -## Context - -The synopsis generator produces the preferred bracket-notation lines but names the definition id verbatim: verified 2026-08-01 on punk::args 0.12.7 - '(script)::toy::flat [-test ] [toolname]...'. For script-level definitions (make.tcl's (script)::punkboot::) and shared/package doc ids, that forced the explicit @form -synopsis workaround adopted in G-030 and documented in G-045's authoring ergonomics; make.tcl hand-writes Tcl-style ?...? synopses for this reason alone, while the user-preferred style is the automatic punk::args bracket notation. - -## Approach - -- Display-only change: parsing, ids, get_spec and spec caching are untouched. -- Rule to settle in the work: when to prefer @cmd -name over id (candidates: ids carrying a parenthesised origin prefix like (script)/(shared)/(autodef), or any id differing from -name beyond a leading ::). Hard constraint: existing real-command definitions (id == command path) must render unchanged. -- Consumer follow-through: make.tcl drops its per-definition @form -synopsis lines once this ships (under G-143 if active, else a small follow-on edit). - -## Notes - -- Origin: G-030 achieved 2026-07-14 (goals/archive/G-030-maketcl-punkargs.md) recorded the id-vs-name synopsis finding and mooted this exact nicety; G-045 achieved 2026-07-12 (goals/archive/G-045-punkargs-authoring-ergonomics.md) documents the explicit @form -synopsis lesson this retires for constructed/script ids. -- Related: G-050 (proposed) - synopsis validity marking; same renderer surface, independent mechanism. -- Related: G-056 (proposed) - display-time help wrapping; same renderer file, independent mechanism. -- Related: G-143 (sibling proposal) - make.tcl consumer of the fallback (auto bracket-notation synopses). -- Overlap survey 2026-08-01: goals_xref paths src/modules/punk/args-999999.0a1.0.tm lists the punkargs family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088); besides G-050/G-056 named above, these target parser/spec mechanisms, not synopsis name display. diff --git a/goals/archive/G-144-punkargs-synopsis-name-fallback.md b/goals/archive/G-144-punkargs-synopsis-name-fallback.md new file mode 100644 index 00000000..d9e639ba --- /dev/null +++ b/goals/archive/G-144-punkargs-synopsis-name-fallback.md @@ -0,0 +1,44 @@ +# G-144 punk::args auto-synopsis: @cmd -name fallback for non-command ids + +Status: achieved 2026-08-01 +Scope: src/modules/punk/args-999999.0a1.0.tm (synopsis renderer + usage/arg_error synopsis display); src/tests/modules/punk/args/testsuites/args/ (synopsis pins) +Goal: Auto-generated synopses render their invocation name from @cmd -name whenever the definition id is not the command's invocation name (script-level ids like (script)::punkboot::modules and (shared)/(package)-style doc ids), falling back to the id only when no @cmd -name is declared - so consumers get the automatic bracket-notation synopsis with the real command name, and explicit @form -synopsis overrides become optional rather than mandatory for such ids. +Acceptance: a definition with @id -id (script)::x::sub and @cmd -name 'make.tcl sub' auto-renders synopsis lines beginning 'make.tcl sub' across the punk::args::synopsis, usage and arg_error surfaces (table and string renderers), with every form line of a multi-form definition carrying the name; definitions whose id equals the invocation name render byte-identical to today (existing synopsis/usage/arg_error suites pass unchanged apart from deliberately updated pins); a definition with neither a command-path id nor @cmd -name renders the id as today; the new behaviour is pinned in the args testsuite. + +## Context + +The synopsis generator produces the preferred bracket-notation lines but names the definition id verbatim: verified 2026-08-01 on punk::args 0.12.7 - '(script)::toy::flat [-test ] [toolname]...'. For script-level definitions (make.tcl's (script)::punkboot::) and shared/package doc ids, that forced the explicit @form -synopsis workaround adopted in G-030 and documented in G-045's authoring ergonomics; make.tcl hand-writes Tcl-style ?...? synopses for this reason alone, while the user-preferred style is the automatic punk::args bracket notation. + +## Approach + +- Display-only change: parsing, ids, get_spec and spec caching are untouched. +- Rule settled in the work (2026-08-01): the fallback is gated on the `(script)` prefix only - the one constructed-id family whose `@cmd -name` is by convention the invocation name. See Notes for the prefix-family survey behind the choice. Hard constraint held: existing real-command definitions (id == command path) render byte-identical (full args testsuite passes with no pin updates). +- Consumer follow-through: make.tcl drops its per-definition @form -synopsis lines once this ships (under G-143 if active, else a small follow-on edit). + +## Notes + +- Origin: G-030 achieved 2026-07-14 (goals/archive/G-030-maketcl-punkargs.md) recorded the id-vs-name synopsis finding and mooted this exact nicety; G-045 achieved 2026-07-12 (goals/archive/G-045-punkargs-authoring-ergonomics.md) documents the explicit @form -synopsis lesson this retires for constructed/script ids. +- Related: G-050 (proposed) - synopsis validity marking; same renderer surface, independent mechanism. +- Related: G-056 (proposed) - display-time help wrapping; same renderer file, independent mechanism. +- Related: G-143 (sibling proposal) - make.tcl consumer of the fallback (auto bracket-notation synopses). +- Overlap survey 2026-08-01: goals_xref paths src/modules/punk/args-999999.0a1.0.tm lists the punkargs family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088); besides G-050/G-056 named above, these target parser/spec mechanisms, not synopsis name display. +- Activation freshness re-run 2026-08-01 (goals_xref score G-144): no new related goals since drafting - the strongest unlinked pair is G-055 (0.68, tclcore regen; its ids are real command paths, untouched by the fallback), and the one-directional punkargs-family pairs are all parser/spec mechanisms already covered by the drafting survey. No new Related lines required. + +### Prefix-family survey (2026-08-01, the settled rule's basis) + +Every parenthesised id family in-tree was inventoried and probed for its @cmd -name convention: + +- `(script)` - script-level dispatch surfaces: (script)::punkboot::* (make.tcl), (script)::punkexe::* (punkexe moduledoc), (script)::runtests, (script)::runtestmodules, (script)punkargs. Every one carries an invocation-style @cmd -name ("make.tcl modules", "punkexe tclsh", "runtests", ...). The id names a namespace inside the script's dispatch, not the invocation. FALLBACK APPLIES (the goal's core case). +- `(autodef)` - generated by punk::ns::generate_autodef from REAL commands. id-minus-tag IS the invocation name; arg_error's existing tag strip (args-999999.0a1.0.tm, the `(autodef)*` match in the synopsis section) already surfaces it. The @cmd -name is a display LABEL, not an invocation: "(autodef) destroy" has -name "destroy"; "(autodef)" has -name "Object: ::x"; only new/create/method autodefs carry -name == id-minus-tag. Rendering the label would regress the usage display (probe: 'destroy' / 'Object: ::toy::obj method...' instead of '::toy::obj destroy' / '::toy::obj method...'). EXCLUDED - behaviour preserved byte-identical (pinned). +- `(shared)` - shared doc fragments: (shared)::package carries NO @cmd -name (falls back to the id, per the acceptance's third clause); (shared)::namespace ensemble carries a prose label ("shared definition namespace ensemble create/configure"). EXCLUDED. +- `(package)` / `(default)` - package-level / tclcore default doc ids: no @cmd -name at all ((package) uses @package -name). Fallback would be a no-op. EXCLUDED. +- `(widgetcommand)` - tkcore moduledoc: -name is either the id echoed or a decorated label ("Tk widget: (widgetcommand)Class_Button"). EXCLUDED. + +The goal text named "(shared)/(package)-style doc ids" alongside (script) as beneficiaries; the survey shows those families declare no invocation-style -name today, so the (script)-only gate satisfies the goal's intent with zero collateral rendering change. If another family later adopts invocation-style -name conventions, the gate widens by extending the prefix match in punk::args::synopsis. User flagged pre-implementation that the goal under-considered (autodef) vs (script) - the survey above is the consideration record. + +## Progress + +- 2026-08-01 (landed): punk::args 0.13.0 - the auto-synopsis name fallback in punk::args::synopsis (formline + summary path; usage/arg_error inherit through their per-form synopsis calls). Doc updates in-module (@form -synopsis directive known-case text, synopsis -help) and in src/modules/AGENTS.md (authoring ergonomics bullet). Six new pins in src/tests/modules/punk/args/testsuites/args/synopsis.test: (script) full/summary, multi-form name-on-every-form-line, usage/arg_error/table surfaces, no-name constructed id renders id, (autodef) unchanged, real-command id byte-identical. +- Verification: synopsis.test 22/22; full modules/punk/args/*** + modules/punk/ns/*** suites green apart from two PRE-EXISTING nsprimitives.test failures (nseval_ifexists_weird, nstree_raw_pins - weird colon-run namespace handling, fail identically with the module change stashed; unrelated). tcl87 runner. make.tcl modules + bootsupport rebuilt (bootsupport snapshot now args-0.13.0.tm; the run also propagated two lagging source bumps - commandset::layout 0.2.0->0.3.0, commandset::project 0.4.0->0.4.1, and the shellfilter 0.2.4 G-145 comment - and pruned the superseded copies). 'make.tcl help modules' renders unchanged (explicit @form -synopsis overrides still win). Project version 0.39.5 -> 0.39.6 (patch: latent capability, no currently-shipped definition renders differently). +- Remaining for acceptance: none - the Acceptance clauses are all met and pinned. Achieved 2026-08-01: the user confirmed the settled (script)-only gate against the Goal statement's (shared)/(package) mention (the survey shows those families declare no invocation-style -name today, so there is nothing to render differently; widening later is a one-line prefix-match extension in punk::args::synopsis). +- Follow-on (not part of this goal's acceptance): make.tcl drops its per-definition @form -synopsis overrides - under G-143 if active, else a small follow-on edit; the punkexe moduledoc's six overrides are the same candidate set. diff --git a/punkproject.toml b/punkproject.toml index b7f749f0..67dee462 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,6 +1,6 @@ [project] name = "punkshell" -version = "0.39.5" +version = "0.39.6" license = "BSD-2-Clause" url = "https://www.gitea1.intx.com.au/jn/punkshell" #packager: declared identity for published artifacts (declarative, not proof - diff --git a/src/bootsupport/modules/punk/args-0.12.7.tm b/src/bootsupport/modules/punk/args-0.13.0.tm similarity index 99% rename from src/bootsupport/modules/punk/args-0.12.7.tm rename to src/bootsupport/modules/punk/args-0.13.0.tm index 0b52a7c3..74e8eb73 100644 --- a/src/bootsupport/modules/punk/args-0.12.7.tm +++ b/src/bootsupport/modules/punk/args-0.13.0.tm @@ -8,7 +8,7 @@ # (C) 2024 # # @@ Meta Begin -# Application punk::args 0.12.7 +# Application punk::args 0.13.0 # Meta platform tcl # Meta license # @@ Meta End @@ -18,7 +18,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin punkshell_module_punk::args 0 0.12.7] +#[manpage_begin punkshell_module_punk::args 0 0.13.0] #[copyright "2024"] #[titledesc {args parsing}] [comment {-- Name section and table of contents description --}] #[moddesc {args to nested dict of opts and values}] [comment {-- Description at end of page heading --}] @@ -835,10 +835,14 @@ tcl::namespace::eval punk::args { auto-calculated line use -typesynopsis on the arguments concerned instead of overriding wholesale (see the notation legend in punk::args::synopsis). - A known case where an override is warranted: script-level - or constructed definition ids, where the auto-synopsis - renders the raw id (e.g '(script)::punkboot::modules') - rather than an invocable command name. + Constructed definition ids whose auto-synopsis would render + the raw id rather than an invocable command name remain a + case where an override is warranted - but note (script) + ids are covered without one: since G-144 the auto-synopsis + leads with the declared @cmd -name for (script)-prefixed + ids (e.g 'make.tcl modules' for (script)::punkboot::modules), + so an override is only needed for a custom notation style + there, not for the name itself. The -overlapallowed value names other forms this form is KNOWN to overlap with (an argument list can cleanly match both - e.g 'after cancel ' where real Tcl @@ -12106,6 +12110,12 @@ tcl::namespace::eval punk::args { text* / *text value must start / end with 'text' Custom @form -synopsis overrides are passed through verbatim and may use any style (see punk::args::define). + + The leading word(s) of an auto-generated line name the command: + normally the definition id. For a (script)-prefixed id (a + script-level dispatch surface such as (script)::punkboot::modules) + the declared @cmd -name leads instead, since the id is not the + invocation name (G-144). " @opts -noheader -type none @@ -12218,6 +12228,20 @@ tcl::namespace::eval punk::args { } dict set SYND COMMAND $cmd_info + #G-144: a (script)-prefixed constructed id is not the command's invocation + #name (it names a script-level dispatch surface, e.g (script)::punkboot::modules + #for 'make.tcl modules') - lead auto-generated synopsis lines with the declared + #@cmd -name (the invocation) when one is present. All other ids lead with the + #id as before: real command paths (byte-identical), (autodef) ids (the id minus + #its tag IS the invocation - arg_error strips the tag for display - and the + #autodef @cmd -name is a display label such as 'destroy' or 'Object: ::x', not + #an invocation), and (shared)/(package)/(default)/(widgetcommand) doc ids + #(none declare an invocation-style @cmd -name today). + set synname $id + if {[string match "(script)*" $id]} { + set synname [Dict_getdef $spec cmd_info -name $id] + } + #leading "# " required (punk::ns::synopsis will pass through) if {![dict exists $received -noheader]} { set syn "# [Dict_getdef $spec cmd_info -summary ""]\n" @@ -12240,7 +12264,7 @@ tcl::namespace::eval punk::args { #override can replace it wholesale (G-041 adjacent fix - the override was #documented and stored in the spec but ignored by this renderer; arg_error's #synopsis section already honoured it) - set formline "$id" + set formline $synname set FORMARGS [list] foreach argname [dict get $forminfo LEADER_NAMES] { @@ -12530,7 +12554,7 @@ tcl::namespace::eval punk::args { continue } set arglist [dict get $arginfo args] - append summary $id + append summary $synname set class_state leader set option_count 0 set value_count 0 @@ -14089,7 +14113,7 @@ package provide punk::args [tcl::namespace::eval punk::args { tcl::namespace::path {::punk::args::lib ::punk::args::system} variable pkg punk::args variable version - set version 0.12.7 + set version 0.13.0 }] return diff --git a/src/bootsupport/modules/punk/mix/commandset/layout-0.2.0.tm b/src/bootsupport/modules/punk/mix/commandset/layout-0.3.0.tm similarity index 79% rename from src/bootsupport/modules/punk/mix/commandset/layout-0.2.0.tm rename to src/bootsupport/modules/punk/mix/commandset/layout-0.3.0.tm index f8ba5e98..7dc51b93 100644 --- a/src/bootsupport/modules/punk/mix/commandset/layout-0.2.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/layout-0.3.0.tm @@ -7,7 +7,7 @@ # (C) 2023 # # @@ Meta Begin -# Application punk::mix::commandset::layout 0.2.0 +# Application punk::mix::commandset::layout 0.3.0 # Meta platform tcl # Meta license # @@ Meta End @@ -232,6 +232,19 @@ namespace eval punk::mix::commandset::layout { # layout_materialize is the entry point; project.new copies from its result path. # -- --- --- --- --- --- --- --- --- --- --- + # -- --- --- --- --- --- --- --- --- --- --- + # G-012: inert VCS-config payloads. A payload file stored under a tail name that is a + # key of materialize_renames would be LIVE configuration under its real name (git + # treats any .gitignore in the tree as live), so layouts store it under the inert key + # name and it is renamed to the materialized value name when the layout's effective + # content is composed (layout_stage_chain). layout_materialize withholds the in-place + # fast path from folders carrying inert payloads, so the rename only ever happens in + # the disposable staging dir - never in the store. .anti markers target STORE names + # (renames run after every layer is composed). The map is the extension point for + # future live-config payload classes (e.g .gitattributes). + # -- --- --- --- --- --- --- --- --- --- --- + variable materialize_renames {gitignore.in .gitignore} + punk::args::define { @id -id ::punk::mix::commandset::layout::lib::layout_resolve_chain @cmd -name punk::mix::commandset::layout::lib::layout_resolve_chain\ @@ -334,7 +347,11 @@ namespace eval punk::mix::commandset::layout { deleting it. Returns a dict: path (stagingdir), chain, deletions (relative paths removed - by .anti markers), copied (number of files copied)." + by .anti markers), copied (number of files copied), materialized (list of + {from to} relative-path pairs of inert VCS-config payloads renamed to + their live names - G-012: gitignore.in stored inert in the store becomes + .gitignore in the composed folder; .anti markers always target store + names, since the rename runs after every layer is composed)." @values -min 2 -max 2 chain -type list -help\ "Ordered list of layout source folders, lowest priority first @@ -376,7 +393,9 @@ namespace eval punk::mix::commandset::layout { } incr copied [_merge_copy_into $sourcefolder $stagingdir] } - return [dict create path $stagingdir chain $chain deletions $deletions copied $copied] + #G-012: with every layer composed under store names, rename inert payloads to their live names + set materialized [_apply_materialize_renames $stagingdir] + return [dict create path $stagingdir chain $chain deletions $deletions copied $copied materialized $materialized] } punk::args::define { @@ -388,12 +407,15 @@ namespace eval punk::mix::commandset::layout { "Resolves the named layout's overlay chain (layout_resolve_chain) within its store and returns a folder holding the effective layout content. - When the chain is a single folder containing no .anti markers (the common - case - e.g the thin punk.project layout, or any module-carried layout) that - folder is returned as-is (staged 0) and no copying occurs. Otherwise the - chain is composed into a staging directory (layout_stage_chain) and the - staged folder is returned (staged 1) - the caller may delete it when done - (the default location is deterministic, so a later call recomposes it). + When the chain is a single folder containing no .anti markers AND no inert + VCS-config payloads (G-012: files named as keys of + layout_materialize_renames, e.g gitignore.in) that folder is returned + as-is (staged 0) and no copying occurs. Otherwise the chain is composed + into a staging directory (layout_stage_chain) and the staged folder is + returned (staged 1) - the caller may delete it when done (the default + location is deterministic, so a later call recomposes it). Inert payloads + always force staging: their rename to live names (.gitignore) must never + touch the store. Layout folders whose store position does not follow the /{vendor,custom}// shape (including any @@ -406,7 +428,9 @@ namespace eval punk::mix::commandset::layout { 0 if it is the store folder itself chain - ordered contributing folders, lowest priority first deletions - relative paths removed by .anti markers (staged only) - copied - number of files copied into staging (staged only)" + copied - number of files copied into staging (staged only) + materialized - {from to} relative-path pairs of inert payloads renamed + to live names (staged only; G-012)" @leaders -min 1 -max 1 layoutname -type string -optional 0 -help\ "Visible layout name as shown by 'dev project.layouts' (e.g punk.project)" @@ -452,16 +476,21 @@ namespace eval punk::mix::commandset::layout { } } } - #fast path - a single-source chain with no .anti markers is usable in place - if {[llength $chain] == 1 && ![llength [_find_anti_relpaths [lindex $chain 0]]]} { - return [dict create path [lindex $chain 0] staged 0 chain $chain deletions [list] copied 0] + #fast path - a single-source chain with no .anti markers and no inert payloads is + #usable in place. Inert VCS-config payloads (G-012) force staging: their rename to + #live names must never touch the store. + if { [llength $chain] == 1 + && ![llength [_find_anti_relpaths [lindex $chain 0]]] + && ![llength [_find_inert_payload_relpaths [lindex $chain 0]]] + } { + return [dict create path [lindex $chain 0] staged 0 chain $chain deletions [list] copied 0 materialized [list]] } if {$opt_stagingdir eq ""} { set sanitized [regsub -all {[^A-Za-z0-9_.-]} $layoutname _] set opt_stagingdir [file join [punk::lib::tempdir] punk_layout_staging $sanitized] } set stageinfo [layout_stage_chain $chain $opt_stagingdir] - return [dict create path [dict get $stageinfo path] staged 1 chain $chain deletions [dict get $stageinfo deletions] copied [dict get $stageinfo copied]] + return [dict create path [dict get $stageinfo path] staged 1 chain $chain deletions [dict get $stageinfo deletions] copied [dict get $stageinfo copied] materialized [dict get $stageinfo materialized]] } punk::args::define { @@ -531,6 +560,101 @@ namespace eval punk::mix::commandset::layout { return $antis } + punk::args::define { + @id -id ::punk::mix::commandset::layout::lib::layout_materialize_renames + @cmd -name punk::mix::commandset::layout::lib::layout_materialize_renames\ + -summary\ + "Return the inert-payload rename map applied at layout materialization"\ + -help\ + "Returns a dict of {inertTail materializedTail} (G-012). Layout payload + files stored under an inert tail name - so they are not live + configuration in the layout store (git never reads gitignore.in) - are + renamed to their materialized tail name (.gitignore) when a layout's + effective content is composed by layout_stage_chain. The map is the + extension point for future live-config payload classes (e.g + .gitattributes stored as gitattributes.in)." + @values -min 0 -max 0 + } + proc layout_materialize_renames {} { + variable materialize_renames + return $materialize_renames + } + + punk::args::define { + @id -id ::punk::mix::commandset::layout::lib::_find_inert_payload_relpaths + @cmd -name punk::mix::commandset::layout::lib::_find_inert_payload_relpaths\ + -summary\ + "Recursively collect inert-payload files below a layout folder"\ + -help\ + "Internal helper for the overlay merge primitive. Returns the relative + paths of files whose tail is an inert-payload store name (a key of + layout_materialize_renames) anywhere below basefolder." + @values -min 1 -max 2 + basefolder -type string + prefix -type string -optional 1 + } + proc _find_inert_payload_relpaths {basefolder {prefix ""}} { + #manual args - see PUNKARGS ::punk::mix::commandset::layout::lib::_find_inert_payload_relpaths + variable materialize_renames + if {$prefix eq ""} { + set here $basefolder + } else { + set here [file join $basefolder $prefix] + } + set found [list] + foreach entry [_dir_entries $here] { + if {$prefix eq ""} { + set rel $entry + } else { + set rel [file join $prefix $entry] + } + if {[dict exists $materialize_renames $entry]} { + lappend found $rel + } elseif {[file isdirectory [file join $here $entry]]} { + lappend found {*}[_find_inert_payload_relpaths $basefolder $rel] + } + } + return $found + } + + punk::args::define { + @id -id ::punk::mix::commandset::layout::lib::_apply_materialize_renames + @cmd -name punk::mix::commandset::layout::lib::_apply_materialize_renames\ + -summary\ + "Rename inert-payload files in a composed folder to their live names"\ + -help\ + "Internal helper for the overlay merge primitive. Renames every + inert-payload file below folder (tail matching a key of + layout_materialize_renames) to its materialized tail name in place, and + returns the list of applied {from to} relative-path pairs. Errors when + both the inert name and its materialized name exist at the same location + - a layout carrying both is contradictory (the materialized name is live + VCS config and must never be store content)." + @values -min 1 -max 1 + folder -type string + } + proc _apply_materialize_renames {folder} { + #manual args - see PUNKARGS ::punk::mix::commandset::layout::lib::_apply_materialize_renames + variable materialize_renames + set applied [list] + foreach relpath [_find_inert_payload_relpaths $folder] { + set newtail [dict get $materialize_renames [file tail $relpath]] + set relbase [file dirname $relpath] + if {$relbase eq "."} { + set tgtrel $newtail + } else { + set tgtrel [file join $relbase $newtail] + } + set tgt [file join $folder $tgtrel] + if {[file exists $tgt]} { + error "layout materialize: both inert payload '$relpath' and materialized name '$tgtrel' are present - the materialized name is live VCS config and must never be layout store content" + } + file rename -- [file join $folder $relpath] $tgt + lappend applied [list $relpath $tgtrel] + } + return $applied + } + punk::args::define { @id -id ::punk::mix::commandset::layout::lib::_merge_copy_into @cmd -name punk::mix::commandset::layout::lib::_merge_copy_into\ @@ -681,6 +805,6 @@ namespace eval punk::mix::commandset::layout { ## Ready package provide punk::mix::commandset::layout [namespace eval punk::mix::commandset::layout { variable version - set version 0.2.0 + set version 0.3.0 }] return diff --git a/src/bootsupport/modules/punk/mix/commandset/project-0.4.0.tm b/src/bootsupport/modules/punk/mix/commandset/project-0.4.1.tm similarity index 99% rename from src/bootsupport/modules/punk/mix/commandset/project-0.4.0.tm rename to src/bootsupport/modules/punk/mix/commandset/project-0.4.1.tm index 7f36384f..6935ba54 100644 --- a/src/bootsupport/modules/punk/mix/commandset/project-0.4.0.tm +++ b/src/bootsupport/modules/punk/mix/commandset/project-0.4.1.tm @@ -7,7 +7,7 @@ # (C) 2023 # # @@ Meta Begin -# Application punk::mix::commandset::project 0.4.0 +# Application punk::mix::commandset::project 0.4.1 # Meta platform tcl # Meta license # @@ Meta End @@ -17,7 +17,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin punkshell_module_punk::mix::commandset::project 0 0.4.0] +#[manpage_begin punkshell_module_punk::mix::commandset::project 0 0.4.1] #[copyright "2023"] #[titledesc {dec commandset - project}] [comment {-- Name section and table of contents description --}] #[moddesc {deck CLI commandset - project}] [comment {-- Description at end of page heading --}] @@ -529,6 +529,15 @@ namespace eval punk::mix::commandset::project { puts $fd "\[project\]" puts $fd "name = \"$projectname\"" puts $fd "version = \"0.1.0\"" + puts $fd "#url: project home/repository url. Consumed by buildsuite artifact" + puts $fd "#emission (punkbin-artifact.toml 'project_url' field; -Dprojecturl" + puts $fd "#overrides). Uncomment and set before publishing artifacts." + puts $fd "#url = \"https://example.com/$projectname\"" + puts $fd "#packager: declared identity for published artifacts (declarative, not" + puts $fd "#proof - signing is the verification layer). Resolution: -Dpackager >" + puts $fd "#PUNKBIN_PACKAGER env > this field > git identity > fossil user default >" + puts $fd "#unrecorded." + puts $fd "#packager = \"Your Name \"" close $fd puts stdout "seeded punkproject.toml (\[project\] name '$projectname' version 0.1.0)" } @@ -1612,6 +1621,6 @@ namespace eval punk::mix::commandset::project { ## Ready package provide punk::mix::commandset::project [namespace eval punk::mix::commandset::project { variable version - set version 0.4.0 + set version 0.4.1 }] return diff --git a/src/bootsupport/modules/shellfilter-0.2.4.tm b/src/bootsupport/modules/shellfilter-0.2.4.tm index d060aced..0bf17fd8 100644 --- a/src/bootsupport/modules/shellfilter-0.2.4.tm +++ b/src/bootsupport/modules/shellfilter-0.2.4.tm @@ -747,6 +747,14 @@ namespace eval shellfilter::chan { method finalize {transform_handle} { my destroy } + #G-145 warning (2026-08-01): the Tcl core delivers the transform 'clear' op + #between ordinary write flush-downs (observed before every 'write' on + #file/pipe channels, tclsh 8.7 and 9.0.3) - it is not a rare seek/truncate + #event. Any future 'clear' method here must therefore NOT discard o_encbuf + #(held partial multi-byte character) or other stream state: dropping held + #state on 'clear' corrupts content split across write chunks. This is the + #defect class behind the ::punkboot::ansistrip carry-drop remnant bug in + #src/make.tcl - see goals/G-145-piped-usage-ansi-remnants.md. #method clear {transform_handle} { # return #} diff --git a/src/modules/AGENTS.md b/src/modules/AGENTS.md index 6c0cffca..28711680 100644 --- a/src/modules/AGENTS.md +++ b/src/modules/AGENTS.md @@ -343,7 +343,7 @@ Before writing or generating punk::args definitions, know the mechanisms punk::a - **`-unindentedfields {-help}`** (on argument lines and the `@cmd` directive) supports left-margin authoring as an alternative; supported and pinned by tests, but the preferred style for constructed defs is indented block form + `@normalize` (G-045 increment 2 decision). - **`-&` record continuation** (unquoted trailing token) continues a record across lines *inside constructed strings*, where backslash-newline is consumed by the building code's own quoting; brace a literal `{-&}` to escape. Byte-identical assembly to the backslash equivalent. Equally valid inside braced file-style definitions: make.tcl's `::punkboot::argdoc` blocks are the in-tree exemplar - braced defs whose `-&` records pull SUMMARIES/HELPTEXTS/option fragments via tstr placeholders, with the display-field help bodies keeping their source indentation (the centred Description style, no `@normalize`). - **Container quoting rules** (braced values fully literal; double-quoted values get Tcl backslash semantics with `$`/`[]` still literal; `\$\{...\}` for a literal placeholder) are documented in the `punk::args::define` -help — consult it rather than experimenting. -- **Script-level/constructed ids want an explicit `@form -synopsis`**: the auto-generated synopsis currently renders the definition id (e.g `(script)::punkboot::modules`), not `@cmd -name` — supply the real invocation string (G-030 lesson). +- **Script-level ids render `@cmd -name` automatically (G-144, punk::args 0.13.0)**: for a `(script)`-prefixed id the auto-generated synopsis leads with the declared `@cmd -name` (e.g `make.tcl modules` for `(script)::punkboot::modules`) - no `@form -synopsis` override is needed for the name itself. Other constructed id families (`(autodef)`, `(shared)`, `(package)`, `(default)`, `(widgetcommand)`) still render the raw id: supply an explicit `@form -synopsis` when one of those needs an invocable display (the G-030 lesson), or when a custom notation style is wanted. - For choice-restricted leaders/values in dispatch-style definitions, `-optional 0` is no longer needed for pointed errors: the 0.12.0 regression (a non-matching word for an *optional* choice arg fell through to a generic overflow error instead of a pointed choiceviolation - recorded in goals/archive/G-030-maketcl-punkargs.md) is fixed in punk::args 0.12.1 (G-082, achieved 2026-07-14 - goals/archive/G-082-punkargs-error-selection.md): the specific choiceviolation/typemismatch is reported when an optional argument's rejected word overflows. `-optional 0` remains semantically sound where the calling code only parses when a word is present (make.tcl's dispatch definitions keep it - they also run against bootsupport punk::args, not the fixed source module); only definitions that must parse pointedly under a snapshot of the regressed 0.12.0 still need the workaround. ## Verification diff --git a/src/modules/punk/args-999999.0a1.0.tm b/src/modules/punk/args-999999.0a1.0.tm index d63a6ba8..d8ae0502 100644 --- a/src/modules/punk/args-999999.0a1.0.tm +++ b/src/modules/punk/args-999999.0a1.0.tm @@ -835,10 +835,14 @@ tcl::namespace::eval punk::args { auto-calculated line use -typesynopsis on the arguments concerned instead of overriding wholesale (see the notation legend in punk::args::synopsis). - A known case where an override is warranted: script-level - or constructed definition ids, where the auto-synopsis - renders the raw id (e.g '(script)::punkboot::modules') - rather than an invocable command name. + Constructed definition ids whose auto-synopsis would render + the raw id rather than an invocable command name remain a + case where an override is warranted - but note (script) + ids are covered without one: since G-144 the auto-synopsis + leads with the declared @cmd -name for (script)-prefixed + ids (e.g 'make.tcl modules' for (script)::punkboot::modules), + so an override is only needed for a custom notation style + there, not for the name itself. The -overlapallowed value names other forms this form is KNOWN to overlap with (an argument list can cleanly match both - e.g 'after cancel ' where real Tcl @@ -12106,6 +12110,12 @@ tcl::namespace::eval punk::args { text* / *text value must start / end with 'text' Custom @form -synopsis overrides are passed through verbatim and may use any style (see punk::args::define). + + The leading word(s) of an auto-generated line name the command: + normally the definition id. For a (script)-prefixed id (a + script-level dispatch surface such as (script)::punkboot::modules) + the declared @cmd -name leads instead, since the id is not the + invocation name (G-144). " @opts -noheader -type none @@ -12218,6 +12228,20 @@ tcl::namespace::eval punk::args { } dict set SYND COMMAND $cmd_info + #G-144: a (script)-prefixed constructed id is not the command's invocation + #name (it names a script-level dispatch surface, e.g (script)::punkboot::modules + #for 'make.tcl modules') - lead auto-generated synopsis lines with the declared + #@cmd -name (the invocation) when one is present. All other ids lead with the + #id as before: real command paths (byte-identical), (autodef) ids (the id minus + #its tag IS the invocation - arg_error strips the tag for display - and the + #autodef @cmd -name is a display label such as 'destroy' or 'Object: ::x', not + #an invocation), and (shared)/(package)/(default)/(widgetcommand) doc ids + #(none declare an invocation-style @cmd -name today). + set synname $id + if {[string match "(script)*" $id]} { + set synname [Dict_getdef $spec cmd_info -name $id] + } + #leading "# " required (punk::ns::synopsis will pass through) if {![dict exists $received -noheader]} { set syn "# [Dict_getdef $spec cmd_info -summary ""]\n" @@ -12240,7 +12264,7 @@ tcl::namespace::eval punk::args { #override can replace it wholesale (G-041 adjacent fix - the override was #documented and stored in the spec but ignored by this renderer; arg_error's #synopsis section already honoured it) - set formline "$id" + set formline $synname set FORMARGS [list] foreach argname [dict get $forminfo LEADER_NAMES] { @@ -12530,7 +12554,7 @@ tcl::namespace::eval punk::args { continue } set arglist [dict get $arginfo args] - append summary $id + append summary $synname set class_state leader set option_count 0 set value_count 0 diff --git a/src/modules/punk/args-buildversion.txt b/src/modules/punk/args-buildversion.txt index cf922e6e..65691f15 100644 --- a/src/modules/punk/args-buildversion.txt +++ b/src/modules/punk/args-buildversion.txt @@ -1,6 +1,7 @@ -0.12.7 +0.13.0 #First line must be a semantic version number #all other lines are ignored. +#0.13.0 - G-144: auto-generated synopses for (script)-prefixed constructed ids now lead with the declared @cmd -name (the invocation name - e.g 'make.tcl modules' for (script)::punkboot::modules) instead of the raw id, across punk::args::synopsis full/summary renders and the usage/arg_error synopsis sections (table and string) that funnel through them; explicit @form -synopsis overrides still win, so make.tcl/punkexe displays are unchanged today (their override retirement is a follow-on). The fallback is gated on the (script) prefix only: (autodef) ids are deliberately excluded - their id-minus-tag IS the invocation (arg_error's existing tag strip surfaces it) while their @cmd -name is a display label ('destroy', 'Object: ::x') that must not lead a synopsis line; (shared)/(package)/(default)/(widgetcommand) doc ids declare no invocation-style -name today and render the id as before (a prefixed id with no -name falls back to the id). Real command ids render byte-identical. Retires the 'open generation deficiency' recorded at 0.12.5 (head-render preference for @cmd -name); define's @form -synopsis directive doc and synopsis -help updated. New synopsis.test pins: script-id full/summary/multiform/surfaces, no-name constructed id, autodef unchanged, real-id byte-identical. #0.12.7 - update_definitions: tag-prefixed id 'namespaces' ((autodef)::x plus script-level id families like (script)::punkboot / (script)::punkexe - the make.tcl G-030 convention, now also the launch-dispatch doc home per G-118 item 10) are handled first-class in the nslist walk. Previously only the (autodef) tag was stripped; other tags fell through to the unqualified-ns branch, emitting a spurious "warning: update_definitions received unqualified ns: (script)" on stderr for EVERY id query and probing a garbage ::(script)... namespace - and, decisively, the namespace_docpackages association was never consulted with the TAGGED key the scan records, so real_id/usage on a registered-but-not-yet-loaded script-level id returned ""/no-such-id even though get_spec resolved it. Now: docpackages lookup with the original tagged key (loads the documenting package - punk::args::usage works for (script)::punkexe::* ids on first query), then generic tag strip for the real-namespace probes, with a bare/tag-only qualifier skipping them silently. Real unqualified namespaces still warn. #0.12.6 - arg_error error-message placement (user direction 2026-07-18): the supplied error message and any per-form match detail it carries ("Bad arguments ... Candidate forms ..." + per-form failure lines) now render exactly once, BELOW the usage display, for both the table and string return forms. Previously the block rendered above the usage AND was appended again below it (the historical "put original error at bottom of table too"), duplicating potentially long multi-form detail. The bottom append is also now suppressed when the message is empty (usage-only calls no longer gain a trailing newline + empty line). tableobject returns are unchanged (they never carried the message). Also (same user direction, format explicitly not frozen - no external consumers yet): the string renderer's argument rows are now single-line 'name TYPE:... ?DEFAULT:...? ?MULTI:yes?' - the multi-line table-cell type constraints (-minsize/-maxsize/-range(s), previously line-broken mid-row so DEFAULT:/MULTI: landed glued to the last constraint line, e.g 'i -return string dict filter' arg vars) parenthesize onto the base type as TYPE:list(-minsize 2 -maxsize 2), empty DEFAULT/MULTI fields are omitted instead of rendering empty labels (MULTI shows 'yes' rather than the check glyph), and whitespace-only help blocks no longer emit indented filler lines. The /arbitrary-flag indicator rows get the same DEFAULT-omission. Choices also gain structure in the string renderer (previously a flat join left choice names and multi-line choicelabels at inconsistent depths, machine-indistinguishable once ANSI is stripped - only the table/ANSI prefix highlighting disambiguated): each choice renders as a markdown-subset bullet '- `choice`' at fixed indent 6 (the backticked token may carry a trailing choiceinfo marker glyph), the label's first line follows the closing backtick on the bullet line after ' - ', label continuations indent to 8 (2 past the bullet dash), and groups head their bullets as "Group 'name':". The resulting structural contract for parsers: column-0 lines bearing TYPE: are argument rows, indented lines are documentation, '- `...`' bullets are choices. Table cells unchanged (construction shared, restructure applied at the string-path consumption site). Feeds punk::ns 0.9.0 cmdhelp -return text (LLM-oriented plain text). Pinned in rendering.test (rendering_stringmode_singleline_argrows, rendering_stringmode_choice_bullets); errorselection.test message pins gained leading * (message no longer starts the raised string); cmdhelp.test message pins sample the last line. No API change. #0.12.5 - eg usability: punk::args::eg resolves relative/unqualified command names (via punk::ns::cmdinfo from the calling context when punk::ns is loaded - handles subcommand words too - else global qualification). Rationale: in a namespace where punk::args::eg shadows the global 'eg' alias (e.g whilst in ::punk::args itself) 'eg list' hit the raw-id API and returned empty while 'i list' resolved - Tcl name resolution, not an alias deficiency. Unknown ids now return a message naming the id instead of empty; punk::ns::eg hands the typed name through when resolution yields no docid so the message can name it. The usage-table 'Example: eg ' header row (the short indication an @examples block exists) now uses scheme colour CLR(check) instead of hardcoded white - the raw id is kept (not @cmd -name, which is display text and not always invocable e.g 'Autoloading Built-in: parray'); a frame-embedded bottom-right marker awaits textblock table footer-block support (candidate goal). New examples.test pins: relative-name resolution, unknown-id message. diff --git a/src/tests/modules/punk/args/testsuites/args/synopsis.test b/src/tests/modules/punk/args/testsuites/args/synopsis.test index 0ce25abd..40d62794 100644 --- a/src/tests/modules/punk/args/testsuites/args/synopsis.test +++ b/src/tests/modules/punk/args/testsuites/args/synopsis.test @@ -424,6 +424,137 @@ namespace eval ::testspace { list [string match "*Tcl style:*" $eg] [string match "*punk::args:*" $eg] }\ -result {1 1} + + #added 2026-08-01 (agent, G-144) - (script)-prefixed constructed ids lead auto-generated + #synopsis lines with the declared @cmd -name (the invocation); other id families render + #the id as before ((autodef) keeps the raw id + arg_error tag-strip, no-name constructed + #ids keep the id, real command ids stay byte-identical). + test synopsis_scriptid_cmdname_fallback {auto-generated synopsis lines for a (script)-prefixed id lead with @cmd -name, not the raw id (full and summary returns)}\ + -setup $common -body { + punk::args::define { + @id -id (script)::testspace::toy::flat + @cmd -name "toy.tcl flat" -summary "flat script surface" + @opts + -test -type boolean + -other -type boolean + @values + toolname -optional 1 -multiple 1 + } + lappend result [punk::ansi::grepstr -h + -return matched -v {^##} [punk::args::synopsis (script)::testspace::toy::flat]] + lappend result [punk::args::synopsis -return summary (script)::testspace::toy::flat] + }\ + -cleanup { + punk::args::undefine (script)::testspace::toy::flat 1 + }\ + -result [list\ + "# flat script surface\ntoy.tcl flat \[-test [a+ italic][a+ noitalic]\] \[-other [a+ italic][a+ noitalic]\] \[[a+ italic]toolname[a+ noitalic]\]..."\ + "# flat script surface\ntoy.tcl flat \[OPTIONS (2 defined)\] \[[a+ italic]toolname[a+ noitalic]\]..."\ + ] + + #added 2026-08-01 (agent, G-144) + test synopsis_scriptid_cmdname_fallback_multiform {every form line of a multi-form (script) definition carries the @cmd -name}\ + -setup $common -body { + punk::args::define { + @id -id (script)::testspace::toy::multi + @cmd -name "toy.tcl multi" -summary "multiform script surface" + @form -form {alpha beta} + @form -form alpha + @values -min 1 -max 1 + aname -type string -optional 0 + @form -form beta + @values -min 0 -max 0 + } + lappend result [punk::ansi::ansistrip [punk::args::synopsis (script)::testspace::toy::multi]] + }\ + -cleanup { + punk::args::undefine (script)::testspace::toy::multi 1 + }\ + -result [list\ + "# multiform script surface\n## FORM 0 alpha\ntoy.tcl multi aname\n## FORM 1 beta\ntoy.tcl multi"\ + ] + + #added 2026-08-01 (agent, G-144) + test synopsis_scriptid_cmdname_fallback_surfaces {usage and arg_error render the @cmd -name in their Synopsis sections (string and table renderers)}\ + -setup $common -body { + punk::args::define { + @id -id (script)::testspace::toy::flat + @cmd -name "toy.tcl flat" -summary "flat script surface" + @opts + -test -type boolean + -other -type boolean + @values + toolname -optional 1 -multiple 1 + } + set usage_str [punk::ansi::ansistrip [punk::args::usage -return string (script)::testspace::toy::flat]] + lappend result [lindex [lsearch -all -inline -glob [split $usage_str \n] "toy.tcl flat*"] 0] + set err_str [punk::ansi::ansistrip [punk::args::arg_error "boom" [punk::args::get_spec (script)::testspace::toy::flat] -aserror 0 -return string]] + lappend result [lindex [lsearch -all -inline -glob [split $err_str \n] "toy.tcl flat*"] 0] + set table_str [punk::ansi::ansistrip [punk::args::usage -return table (script)::testspace::toy::flat]] + lappend result [expr {[string first {toy.tcl flat [-test ]} $table_str] >= 0}] + }\ + -cleanup { + punk::args::undefine (script)::testspace::toy::flat 1 + }\ + -result [list\ + "toy.tcl flat \[-test \] \[-other \] \[toolname\]..."\ + "toy.tcl flat \[-test \] \[-other \] \[toolname\]..."\ + 1\ + ] + + #added 2026-08-01 (agent, G-144) + test synopsis_constructed_id_no_cmdname_renders_id {a constructed id with no @cmd -name declared renders the id as before}\ + -setup $common -body { + punk::args::define { + @id -id (shared)::testspace::toy::frag + @values + requirement -type string -multiple 1 -optional 1 + } + lappend result [punk::ansi::ansistrip [punk::args::synopsis (shared)::testspace::toy::frag]] + }\ + -cleanup { + punk::args::undefine (shared)::testspace::toy::frag 1 + }\ + -result [list\ + "# \n## FORM 0 _default\n(shared)::testspace::toy::frag \[requirement\]..."\ + ] + + #added 2026-08-01 (agent, G-144) + test synopsis_autodef_id_unchanged {(autodef) ids keep the raw-id synopsis line and the arg_error tag-strip - their @cmd -name is a display label, not an invocation}\ + -setup $common -body { + punk::args::define { + @id -id "(autodef)::testspace::toy::obj destroy" + @cmd -name "destroy" -summary "delete object instance" + @values -min 0 -max 0 + } + lappend result [punk::ansi::ansistrip [punk::args::synopsis {(autodef)::testspace::toy::obj destroy}]] + set usage_str [punk::ansi::ansistrip [punk::args::usage -return string {(autodef)::testspace::toy::obj destroy}]] + lappend result [lindex [lsearch -all -inline -glob [split $usage_str \n] "::testspace::toy::obj destroy*"] 0] + }\ + -cleanup { + punk::args::undefine {(autodef)::testspace::toy::obj destroy} 1 + }\ + -result [list\ + "# delete object instance\n## FORM 0 _default\n(autodef)::testspace::toy::obj destroy"\ + "::testspace::toy::obj destroy"\ + ] + + #added 2026-08-01 (agent, G-144) + test synopsis_realcmd_id_byteidentical {a real command id renders the id even when @cmd -name drops the leading ::}\ + -setup $common -body { + punk::args::define { + @id -id ::testspace::toy::real + @cmd -name testspace::toy::real -summary "real command" + @values + x -type string -optional 1 + } + lappend result [punk::ansi::ansistrip [punk::args::synopsis ::testspace::toy::real]] + }\ + -cleanup { + punk::args::undefine ::testspace::toy::real 1 + }\ + -result [list\ + "# real command\n## FORM 0 _default\n::testspace::toy::real \[x\]"\ + ] } tcltest::cleanupTests ;#needed to produce test summary line.