Browse Source

GOALS: two-tier index restructure - summary-only GOALS.md, canonical Goal/Acceptance in detail files, archive-on-flip

- GOALS.md collapsed to summary entries (ID/status/title/Scope/detail pointer): 104.6KB -> 23KB so a full read stays cheap; Scope lines kept verbatim so path-grep goal discovery still works
- canonicality split: index canonical for ID/status/title/Scope, detail file canonical for Goal:/Acceptance:; all 61 detail-file headers reconciled from the index (fixes stale copies, e.g. G-036 detail acceptance predated the achieved rework), bodies preserved untouched
- detail files created for the three index-only goals G-017, G-034, G-054; every goal now has one
- 10 achieved goals (G-007 G-015 G-036 G-037 G-040 G-046 G-049 G-054 G-058 G-059) archived: summary records appended to GOALS-archive.md, detail files moved to goals/archive/; archive-on-flip replaces the ~30-achieved-entries trigger
- GOALS.md preamble/rules reworked: read workflow (read index in full, then detail files of scope-intersecting goals before editing), contract spans both tiers with proposal-first unchanged, next-free-ID counts the archive, achieved flip judged against the detail-file Acceptance and includes archiving
- progress convention codified from G-001 increment-1 practice: index entries carry status only, never progress; incremental progress lives in the detail file's ## Progress section (landed/remaining); achieved flip requires the remaining-work list resolved - a partial increment never flips a goal
- goals/AGENTS.md rewritten for the split (required 4-field header, one detail file per goal, orphan rules covering archive/); root AGENTS.md Child DOX Index + Repo-wide Notes updated; GOALS-archive.md preamble updated
- stale detail-file paths updated to goals/archive/ in src/lib/app-punkscript/punkscript.tcl (comment), runtimebash_wsl.test, wslprobe.tcl and the G-042 detail file; propagated copies (root lib/, src/vfs/_vfscommon.vfs, src/_build) left to refresh via normal build steps
- no project version bump: governance docs and source comments only, no user-visible shell behaviour

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 4 days ago
parent
commit
ae04b3f8c9
  1. 8
      AGENTS.md
  2. 48
      GOALS-archive.md
  3. 213
      GOALS.md
  4. 45
      goals/AGENTS.md
  5. 3
      goals/G-001-pluggable-console-backends.md
  6. 1
      goals/G-002-non-nested-subshell.md
  7. 1
      goals/G-003-subshell-resource-limits.md
  8. 1
      goals/G-004-no-committed-binaries.md
  9. 1
      goals/G-005-zig-build-infrastructure.md
  10. 1
      goals/G-006-prebuilt-artifact-download.md
  11. 1
      goals/G-008-scoped-console-state.md
  12. 1
      goals/G-009-themed-subshell-profiles.md
  13. 1
      goals/G-010-subshell-tree-navigation.md
  14. 1
      goals/G-011-console-stderr-semantics.md
  15. 1
      goals/G-012-template-payload-safety.md
  16. 3
      goals/G-013-raw-mode-default.md
  17. 3
      goals/G-014-punk-config-toml.md
  18. 3
      goals/G-016-projects-work-git-discovery.md
  19. 6
      goals/G-017-agent-project-discovery.md
  20. 3
      goals/G-018-zig-plain-tclsh-kits.md
  21. 3
      goals/G-019-dependency-scan-module-trimming.md
  22. 3
      goals/G-020-screencap-input-module.md
  23. 3
      goals/G-021-agent-visual-verification.md
  24. 3
      goals/G-022-fossil-rename-punkshell.md
  25. 3
      goals/G-023-version-named-binaries.md
  26. 3
      goals/G-024-mapvfs-toml.md
  27. 3
      goals/G-025-exe-selfreport.md
  28. 3
      goals/G-026-vendor-provenance-policy.md
  29. 3
      goals/G-027-derived-project-pull-updates.md
  30. 3
      goals/G-028-file-locker-identification.md
  31. 3
      goals/G-029-testmodules-from-srctests.md
  32. 3
      goals/G-030-maketcl-punkargs.md
  33. 3
      goals/G-031-componentized-kit-boot.md
  34. 3
      goals/G-032-launcher-punkargs.md
  35. 3
      goals/G-033-proj-mode-cwd-project.md
  36. 6
      goals/G-034-modpod-codeinterp-tcl86.md
  37. 3
      goals/G-035-mixed-tm-pkgindex-provision.md
  38. 1
      goals/G-038-piped-session-continuity.md
  39. 1
      goals/G-039-orphan-console-spin.md
  40. 1
      goals/G-041-punkargs-form-matching.md
  41. 3
      goals/G-042-subshell-help-topics.md
  42. 1
      goals/G-043-subshell-definition-plugins.md
  43. 1
      goals/G-044-repl-command-completion.md
  44. 1
      goals/G-045-punkargs-authoring-ergonomics.md
  45. 1
      goals/G-047-declared-primary-vcs.md
  46. 1
      goals/G-048-textblock-table-punkargs.md
  47. 3
      goals/G-050-synopsis-validity-marking.md
  48. 3
      goals/G-051-cmdinfo-pseudo-and-prefix.md
  49. 3
      goals/G-052-oo-method-autodef.md
  50. 3
      goals/G-053-punkargs-multiple-ranges.md
  51. 3
      goals/G-055-tclcore-regen-workflow.md
  52. 3
      goals/G-056-punkargs-word-wrapping.md
  53. 3
      goals/G-057-kit-icon-embedding.md
  54. 3
      goals/G-060-qemu-test-matrix.md
  55. 3
      goals/G-061-pseudoconsole-expect.md
  56. 1
      goals/archive/G-007-console-location-transparency.md
  57. 5
      goals/archive/G-015-script-subcommand-piped-stdin.md
  58. 3
      goals/archive/G-036-tcl9-udp-console-worker-wedge.md
  59. 1
      goals/archive/G-037-vendorlib-vfs-propagation.md
  60. 1
      goals/archive/G-040-punkargs-choicealiases.md
  61. 1
      goals/archive/G-046-punkargs-deferred-help-and-fixes.md
  62. 3
      goals/archive/G-049-punkargs-parse-status-model.md
  63. 6
      goals/archive/G-054-tclcore-stringis-harvest.md
  64. 3
      goals/archive/G-058-static-runtime-packages.md
  65. 3
      goals/archive/G-059-wsl-test-driving.md
  66. 2
      src/lib/app-punkscript/punkscript.tcl
  67. 2
      src/tests/shell/testsuites/binscripts/runtimebash_wsl.test
  68. 2
      src/tests/testsupport/wslprobe.tcl

8
AGENTS.md

@ -178,9 +178,9 @@ The project version is fully independent of module versions. A module bump (even
- `src/testansi/` — Sample ANSI art files (do not modify)
- `bin/` — Built punk shell executables, launch package modes (incl. `src` mode for working-tree verification), plain runtime kits (see bin/AGENTS.md)
- `.fossil-settings/` — Versioned fossil settings and the git+fossil dual-VCS coexistence contract: ignore-sync rules, files neither system may track (see .fossil-settings/AGENTS.md)
- `GOALS.md` — Top-level technical goal index; required read for non-trivial work (no child AGENTS.md; the file documents its own format and the agent goal-authoring workflow)
- `GOALS-archive.md`One-line records of achieved goals moved out of the active index (historical context only)
- `goals/`Optional detail prose for goals needing more than a one-line summary (see goals/AGENTS.md)
- `GOALS.md` — Top-level technical goal index, summary-only (ID, status, title, scope, detail pointer per goal); required read for non-trivial work (no child AGENTS.md; the file documents its own format, read workflow, and the agent goal-authoring workflow)
- `GOALS-archive.md`Summary records of achieved goals moved out of the active index (historical context only)
- `goals/`Per-goal detail files carrying the canonical Goal/Acceptance contract prose plus supporting detail (see goals/AGENTS.md)
- `goals/archive/` — Detail files for achieved/archived goals
- Directories agents should not directly modify (no child DOX needed):
- `callbacks/` — Experimental shellspy features, user-only
@ -200,4 +200,4 @@ The project version is fully independent of module versions. A module bump (even
- Source-tree build, testing, linting, and file-resolution workflow lives in `src/AGENTS.md`.
- Tcl module authoring conventions live in `src/modules/AGENTS.md` and closer module child docs.
- If AGENTS.md conflicts with CLAUDE.md, AGENTS.md wins.
- Technical project goals live in root `GOALS.md` (index) with optional detail prose in `goals/G-<id>-<slug>.md`. Goals are user-owned: agents add or edit goal entries only at the user's request, proposal-first (show the proposed wording, get explicit approval before writing - see the `GOALS.md` maintenance rules); never on their own initiative. Suggesting candidate goals when work surfaces something goal-worthy is welcome and encouraged - as a flagged proposal, not a file edit. Detail-file updates from user-directed work need only be reported in the completion summary. Agents auto-flip a goal to `achieved <date>` when its stated acceptance criterion is met as part of the DOX closeout pass, and must flag `proposed`→`active` transitions in their completion report for user confirmation. See `GOALS.md` for the full workflow, including how to author a new goal when asked.
- Technical project goals live in root `GOALS.md` (summary index: ID, status, title, scope, detail pointer per goal) with the canonical Goal/Acceptance contract prose in `goals/G-<id>-<slug>.md` detail files - read the detail file of every goal whose scope intersects paths being edited. Goals are user-owned: agents add or edit goal contract elements (in either tier) only at the user's request, proposal-first (show the proposed wording, get explicit approval before writing - see the `GOALS.md` maintenance rules); never on their own initiative. Suggesting candidate goals when work surfaces something goal-worthy is welcome and encouraged - as a flagged proposal, not a file edit. Non-contract detail-file updates from user-directed work need only be reported in the completion summary. Agents auto-flip a goal to `achieved <date>` when its detail-file acceptance criterion is met as part of the DOX closeout pass - the flip includes archiving the entry to `GOALS-archive.md` and the detail file to `goals/archive/` - and must flag `proposed`→`active` transitions in their completion report for user confirmation. See `GOALS.md` for the full workflow, including how to author a new goal when asked.

48
GOALS-archive.md

@ -1,21 +1,59 @@
# Achieved Goals Archive
This file holds one-line records of goals that have been achieved and moved out of the active `GOALS.md` index to keep that file lean. Records here are historical context only — they explain why code exists in its current shape and are useful when future agents refactor or revisit the same area.
This file holds summary records of goals that have been achieved and moved out of the active `GOALS.md` index to keep that file lean. Goals are archived as part of the achieved flip (see the `GOALS.md` maintenance rules). Records here are historical context only — they explain why code exists in its current shape and are useful when future agents refactor or revisit the same area.
## Format
Each archived goal is one line, preserving its original ID and acceptance criterion so it remains traceable:
Each archived goal is one compact record, preserving its original ID and acceptance criterion so it remains traceable:
```
### G-<id> [achieved <YYYY-MM-DD>] <short title> → detail: goals/archive/G-<id>-<slug>.md
Scope: <as in original index>
Acceptance: <as in original index>
Acceptance: <as achieved>
```
If a goal had no detail file, omit the `→ detail:` clause.
The full record (including the Goal statement) lives in the archived detail file.
Do not edit archived entries except to fix a broken path. If an archived goal is reopened, move it back to `GOALS.md` with a new ID and mark the old entry `superseded by G-<new id>`.
## Archived goals
_None yet._
### G-007 [achieved 2026-07-05] Location-transparent punk::console across repl and code interps → detail: goals/archive/G-007-console-location-transparency.md
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Acceptance: from a running punk session's code interp, without `repl eval`: `console_fact_get` returns the same values the parent sees and a fact set in the parent is immediately visible; a terminal query (e.g. `get_cursor_pos` or `dec_get_mode`) against the default console succeeds and cooperates with the repl reader (no lost or garbled input); a console constructed and owned by code-interp code is operated on locally (no round-trip to the parent); the existing console test suites pass and single-interp (non-repl) usage is unchanged.
### G-015 [achieved 2026-07-07] Punk executable `script` subcommand: reliable non-interactive piped/script execution → detail: goals/archive/G-015-script-subcommand-piped-stdin.md
Scope: src/vfs/_config/punk_main.tcl, src/lib/app-punkshell/punkshell.tcl (script path or a leaner dedicated app package)
Acceptance: piping commands to `<punkexe> script` runs them and terminates at stdin EOF with no trailing `exit` required, exit code 0 on success; a failing piped command terminates the process with a nonzero exit code and the error on stderr, never landing in an interactive shell regardless of console availability or PUNK_PIPE_EOF; `<punkexe> script <file> [<args>...]` executes the file with conventional ::argv0/::argv and propagates its error status the same way; the script path installs none of the `shell` subcommand's shellfilter stacks/transforms and the launch plumbing itself emits nothing on stdout/stderr (the current stub's stderr diagnostics removed) so exec-style callers see only the script's own output; the motivating example works with no package require boilerplate: piping `dev projects.work *<name>*` to `<punkexe> script` emits the matching-project table and exits 0, because the script interp carries the default punk shell module/alias environment.
### G-036 [achieved 2026-07-08] Root-cause the Tcl 9 console+udp worker-thread event-loop wedge; minimal repro for possible upstream reporting → detail: goals/archive/G-036-tcl9-udp-console-worker-wedge.md
Scope: src/modules/shellthread-999999.0a1.0.tm, src/bootsupport/modules/shellthread-1.6.2.tm, src/modules/shellfilter-999999.0a1.0.tm (as characterised - no product-code changes required by this goal; the punkshell mitigations are separate fixes)
Acceptance: (reworked 2026-07-08 after the root cause was found) the wedge mechanism is identified and written up in the detail file - DONE: bundled tcludp 1.0.12's Windows per-thread UDP_ExitProc closes the process-global synchronization events, proven by dump handle-table analysis plus a live CloseHandle breakpoint and confirmed by the upstream 1.0.12->1.0.13 diff, which already fixes it (so no upstream report is required; the standalone minimal repro originally required here is waived as moot by the user); the tcl9 kits bundle tcludp >= 1.0.13 with the in-context batch harness baseline resolved - DONE 2026-07-08 (run-2 syslog workers alive vs the 4/4-wedged 1.0.12 baseline); DONE 2026-07-08 (punk::lib 0.3.0 has_libbug_udp_threadexit, surfaced via 'help tcl' in punk 0.2.1): a has_bug-style detection in the punkshell check machinery (in the vein of punk::lib::check::has_tclbug_* / punk::console::check::has_bug_*, surfaced through the same reporting as 'help tcl'/'help console') reports the vulnerable combination - simple version-based detection (loaded/bundled tcludp < 1.0.13 on Tcl 9 Windows) is sufficient, no behavioural probe needed; loose-end decisions (punk8win 8.6 kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses) recorded in the detail file when made (open as of 2026-07-08 - non-gating).
### G-037 [achieved 2026-07-08] Propagate platform vendor libraries into kit vfs lib_tcl trees via make.tcl → detail: goals/archive/G-037-vendorlib-vfs-propagation.md
Scope: src/make.tcl (new or extended step), src/vendorlib_tcl8 + src/vendorlib_tcl9 (sources), src/vfs/<kit>.vfs/lib_tcl8 + lib_tcl9 (targets), punkcheck tracking
Acceptance: with a newer package version placed under src/vendorlib_tcl9/<platform>, one documented make.tcl invocation updates the participating src/vfs/*/lib_tcl9 trees - installing the new package and removing or explicitly retiring the superseded version (no silent mixed-version provision, per the G-035 concerns) - with punkcheck-tracked provenance; which vfs folders participate is explicitly declared per kit rather than blanket-copied (kit vfs package sets may intentionally differ), with the declaration mechanism recorded (candidate home: the G-024 mapvfs toml); a subsequent `make.tcl project` yields kits loading the new version (provable via the tcludp case: built punk902z reports `package require udp` == 1.0.13 with no udp1.0.12 folder remaining in its vfs); the lib_tcl8 tree gets the same treatment or an explicit exclusion rationale in the goal record.
### G-040 [achieved 2026-07-08] punk::args choice aliasing (-choicealiases) with parse normalization, display folding, and doc-lookup parity → detail: goals/archive/G-040-punkargs-choicealiases.md
Scope: src/modules/punk/args-999999.0a1.0.tm (parse + usage rendering), src/modules/punk/ns-999999.0a1.0.tm (cmdinfo/cmd_traverse choice resolution parity), src/modules/punk-999999.0a1.0.tm (punk::help topic argdoc as first consumer), src/tests/modules/punk/args/testsuites/, src/tests/modules/punk/ns/testsuites/
Acceptance: a definition using -choicealiases parses an alias (and an alias prefix where -choiceprefix allows) to its canonical choice in the parse result, with -choicerestricted 0 passthrough and the deny/reserve lists honoured unchanged; usage display shows one entry per canonical choice with aliases folded (no duplicate rows; -choicelabels attach to the canonical); punk::ns::cmdinfo/cmd_traverse resolve subcommand words to docids with the same outcome as the parser for alias, prefix, denied, reserved and unknown words (the pre-goal characterization tests updated from pinned-GAP to fixed); punk::help's topic definition adopts the feature so `i help` lists one entry per registered topic while `help h`/`help e` still fall through to command lookup; definitions without -choicealiases behave unchanged (existing punk::args and punk::ns suites pass).
### G-046 [achieved 2026-07-10] punk::args deferred -help resolution (parse-time performance + reentrancy) and rendering/value-shape fixes → detail: goals/archive/G-046-punkargs-deferred-help-and-fixes.md
Scope: src/modules/punk/args-999999.0a1.0.tm (resolve/get_dict: display-field deferral, dynamic-cache subst path, prefix writeback, string renderer, cmdhelp-facing messages), src/modules/punk/ansi-999999.0a1.0.tm (mark_columns argdoc as the reentrancy/perf testbed), src/tests/modules/punk/args/testsuites/ (GAP tests flip; perf verification)
Acceptance: parsing/argument resolution provably skips -help expansion (a definition whose -help contains a ${[...]} that would error or record its invocation shows the substitution did NOT run during a parse-only path, only for help display); first parse of punk::ansi::mark_columns drops from ~4s to well under a second with 'i punk::ansi::mark_columns' still rendering the embedded example, and a -help that parses its OWN definition id resolves or errors cleanly rather than looping; first-parse timing improves for at least one other heavily documented command (recorded in the detail file); rendering_atdynamic_multiline_help_insertion_GAP flips to all-aligned; choicegroups_imap_prefix_listwrap_GAP flips to shape-identical (prefix input yields the same plain string as exact input); the -return string renderer's cmd-help continuations align under the first line with relative indents preserved (rendering_string_renderer_characterization updated); the 'Bad number of leading values...' prefix shown by goodargs parsing in 'i string is'-style output is reworded or suppressed for the usage-display path; full punk::args and punk::ns suites pass with no non-GAP expectations weakened.
### G-049 [achieved 2026-07-10] punk::args parse-status data model with machine-parsable cmdhelp returns → detail: goals/archive/G-049-punkargs-parse-status-model.md
Scope: src/modules/punk/args-999999.0a1.0.tm (arg_error, parse error dispatch, colour-scheme handling), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp), src/tests/modules/punk/args/testsuites/args/usagemarking.test, src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test
Acceptance: cmdhelp -return dict distinguishes an incomplete, a fully-valid and an invalid argument set via per-argument statuses (received/ok/bad + overall scheme/message/form) with the structure documented; the table and string renderers derive their marking from that same structure, with rendered output unchanged except where the pinned GAP tests flip: badarg marking covers type/allocation failures not just choice violations (cmdhelp_GAP_no_badarg_marking_for_failed_typed_value), an explicit -scheme is honoured on the parse-failure path (cmdhelp_GAP_explicit_scheme_ignored_on_failure), the failure message names the queried command instead of cmdhelp's internal parse source line (cmdhelp_GAP_errormsg_leaks_internal_source), and scheme rendering no longer depends on or mutates shared colour state - the documented -scheme choice value 'nocolour' takes effect and repeated renders of the same call are identical regardless of prior scheme renders (usagemarking_GAP_scheme_nocolour_renders_with_leftover_colours, usagemarking_GAP_dash_nocolour_leaks_into_shared_array, usagemarking_GAP_dash_nocolour_leak_affects_later_info_render); all non-GAP characterization tests in usagemarking.test and cmdhelp.test pass unchanged.
### G-054 [achieved 2026-07-11] tclcore moduledoc: runtime-harvested 'string is' class choices with cross-version behavioural parity pins → detail: goals/archive/G-054-tclcore-stringis-harvest.md
Scope: src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (+ tclcore-buildversion.txt), src/tests/modules/punk/args/testsuites/args/ (new parity test), TEMP_REFERENCE/tcl9 (read-only reference)
Acceptance: parse/parse_status against ::tcl::string::is and its per-class virtual ids agrees with the real interpreter's error-vs-ok outcome for a pinned probe matrix (missing args, trailing flag-like str word, option/class unique-prefix acceptance and ambiguity rejection, unknown option/class, -failindex var consumption leaving no str, per-version class presence: dict, unicode) on Tcl 9.0.x and 8.6; the rendered choices show only classes the running interp accepts; the parity test derives expectations from the live interpreter (not version arithmetic) and passes under both; existing args/tclcore suites pass; tclcore buildversion bumped with changelog.
### G-058 [achieved 2026-07-10] Boot honours statically-linked runtime packages (static baseline seeding + packagepreference static-awareness) → detail: goals/archive/G-058-static-runtime-packages.md
Scope: src/vfs/_config/punk_main.tcl (boot auto_path/tm path filtering), src/modules/punk/packagepreference-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm + src/modules/punk/repl/codethread-999999.0a1.0.tm (code interp / codethread bootstrap), src/modules/shellthread-999999.0a1.0.tm (punkshell-created worker threads) as applicable, src/tests/ (un-gated unit tests + constraint-gated shell/kit integration tests), punkbin artifact repo (separate git repo, local checkout c:/repo/jn/punkbin - pinned runtime additions)
Acceptance: a punk91-style kit (tclsfe-x64 + punk9win.vfs, no thread dll in the vfs) boots to a working repl with no "can't find package Thread" - punk::console loads in the code interp, and package require Thread succeeds there and in a punkshell-created worker thread, resolving to the static version; in the same kit, twapi resolves per the documented policy (no repeat of the observed static-Twapi-masked-by-older-vfs-twapi-5.0.2 double load; a genuinely newer bundled copy remains reachable by that policy); dll-based kits (punk902z) boot and pass their existing shell test baseline unchanged, as does a plain tclsh dev launch; the seeding mechanism is generic - driven by the captured baseline, no runtime-specific package naming - and the boot-time static baseline is introspectable at the repl; the seeding/preference logic is covered by un-gated unit tests against simulated baselines (runnable under plain tclsh), while kit-boot integration tests are gated behind a capability-probed tcltest constraint (a built kit whose baseline shows static entries including Thread) that skips cleanly when no such kit is present; the runtimes used for verification (tclsfe-x64.exe at minimum) are added to the punkbin artifact repository under win32-x86_64 with sha1sums.txt updated, so the constraint is satisfiable on other machines via the existing runtime-retrieval path; the punk91 code-interp vfs/vfs::zip load failure is re-diagnosed after the fix and either resolved or recorded as a distinct issue/candidate goal.
### G-059 [achieved 2026-07-11] WSL detection and suitability probing for driving unix-side tests from Windows → detail: goals/archive/G-059-wsl-test-driving.md
Scope: src/tests/ (capability probe helpers + constraint-gated cases in existing suites, e.g the unix sh-payload execution test in modules/punk/mix/testsuites/scriptwrap/multishell.test and a runtime.bash behaviour test), src/tests/AGENTS.md (enablement notes)
Acceptance: a documented probe helper yields a wsl_linux_available constraint whose checks are capability-based (distro launches and answers uname/tool probes; staging into a native tempdir works) and which cannot misfire on wsl.exe-present-but-unusable installs (no distro, WSL1 limitations, broken interop); the currently unix-gated multishell sh-payload execution test runs green via WSL on a suitable machine and still skips cleanly elsewhere; at least one runtime.bash behaviour test (active/use/run resolution against a fixture runtime folder) runs inside WSL - all such tests executing from a native-filesystem staging dir with the shared path used only for one-way copy-in/out; the Windows checkout's git and fossil state is untouched by a WSL-gated run (verifiable: git status/fossil changes identical before and after); suite results on a WSL-less machine are unchanged (skips, not failures); enablement/limitations and the staging pattern recorded in src/tests/AGENTS.md.

213
GOALS.md

@ -1,417 +1,266 @@
# Project Goals
This file is the canonical, harness-agnostic index of technical project goals for ShellSpy. It is referenced from the root `AGENTS.md` Child DOX Index and is a required read for any non-trivial work, so that agents can discover goals whose scope intersects their work.
This file is the canonical, harness-agnostic index of technical project goals for punkshell. It is referenced from the root `AGENTS.md` Child DOX Index and is a required read for any non-trivial work, so that agents can discover goals whose scope intersects their work. It is deliberately summary-only so that reading it in full stays cheap: each entry carries ID, status, title and Scope plus a detail-file pointer, and the goal's full contract prose (Goal statement and Acceptance criterion) lives in its detail file under `goals/` (see `goals/AGENTS.md`).
Detail prose for goals that need it lives in `goals/G-<id>-<slug>.md` (see `goals/AGENTS.md`). The index entry is canonical; a detail file only elaborates and never contradicts its index entry.
## How agents use this index
1. Read this file in full (it is small by design).
2. Identify every entry whose Scope intersects the paths or module areas being inspected or edited. Scope lines name real repo paths, so path search works (e.g. searching this file for `punk/repl` finds the goals touching that module).
3. Before editing those paths, read the detail file of each intersecting goal.
## Canonicality
- The index entry (this file) is canonical for ID, status, title and Scope.
- The detail file is canonical for the Goal statement and the Acceptance criterion.
- The tiers must not contradict: `Status:` and `Scope:` repeated in a detail file mirror the index. On disagreement the index wins for status/title/Scope and the detail file wins for Goal/Acceptance; fix the stale copy.
## Format
Each goal is one block:
Each goal is one compact entry:
```
### G-<id> [<status>] <short title>
Scope: <repo paths or module areas this goal touches>
Goal: <one line what done looks like, self-contained>
Detail: goals/G-<id>-<slug>.md <- optional, omit if absent
Acceptance: <measurable, verifiable pass/fail criterion>
Detail: goals/G-<id>-<slug>.md
```
Every goal has a detail file; it holds the canonical Goal and Acceptance plus any supporting prose (structure in `goals/AGENTS.md`). The index entry must stay safe standalone: title plus Scope alone must be enough that an agent who reads no detail file still does no harm.
### Status tags
- `proposed` — not yet started; awaiting user confirmation to go `active`
- `active` — in progress
- `achieved <YYYY-MM-DD>` — done; kept as a one-line record
- `achieved <YYYY-MM-DD>` — done. The flip includes archiving: the entry moves to `GOALS-archive.md` as a summary record and its detail file to `goals/archive/`. Achieved entries do not accumulate in this file.
- `abandoned` — dropped; one line on why stays in the entry
- `superseded by G-<id>` — replaced; do not delete the old entry
### Maintenance rules
- Goals are user-owned. Agents add or edit goal entries only at the user's request or with the user's explicit approval - never on their own initiative, and never in bulk from an agent's own survey of what "should" be goals.
- Suggesting is always allowed and encouraged when grounded in the work at hand: a discovered gap, a recurring manual step, a deferred design decision, or a natural follow-on that fits what is currently being worked on. Flag it as a candidate goal in conversation or the completion report, optionally with a drafted block ready for approval. A suggestion is not an edit - nothing is written to this file or `goals/` until the user approves per the proposal-first rule.
- Proposal-first: before writing a new goal entry or changing an existing entry's contract (title, status tag, Scope, Goal, Acceptance), show the user the proposed wording - the full block for a new goal, the changed clause(s) for an edit - and get explicit approval. If the user already supplied or approved the exact wording this session, apply it and report what was written.
- Exception (sanctioned autonomous edit): an agent whose work satisfies a goal's `Acceptance:` must flip that goal to `achieved <date>` as part of its DOX closeout pass, and report the flip in its completion summary.
- Suggesting is always allowed and encouraged when grounded in the work at hand: a discovered gap, a recurring manual step, a deferred design decision, or a natural follow-on that fits what is currently being worked on. Flag it as a candidate goal in conversation or the completion report, optionally with a drafted entry ready for approval. A suggestion is not an edit - nothing is written to this file or `goals/` until the user approves per the proposal-first rule.
- The goal contract spans both tiers: the index entry (title, status tag, Scope) and the detail file's `Goal:` and `Acceptance:` lines.
- Proposal-first: before writing a new goal or changing any contract element in either tier, show the user the proposed wording - the full entry plus detail-file header for a new goal, the changed clause(s) for an edit - and get explicit approval. If the user already supplied or approved the exact wording this session, apply it and report what was written.
- Exception (sanctioned autonomous edit): an agent whose work satisfies a goal's `Acceptance:` (judged against the detail file's criterion, never the index entry alone) must flip that goal to `achieved <date>` as part of its DOX closeout pass, archive it (entry to `GOALS-archive.md` per that file's format, detail file to `goals/archive/`), and report the flip in its completion summary. If the detail file carries a `## Progress` section, the flip additionally requires its remaining-work list to be resolved — empty, or each item verified satisfied; a partial increment never flips a goal.
- Agents must not flip `proposed``active`. They flag it in their completion report for the user to confirm.
- Detail files under `goals/` may be updated without pre-approval when recording findings, decisions, or verification artifacts from work the user directed on that goal or its subject matter; report such updates in the completion summary. The index entry is canonical - a detail edit must never contradict it.
- The `Goal:` line must stay self-contained enough that an agent who skips the detail file still does no harm. Detail files are enrichment, not load-bearing for safety.
- When the achieved section grows past ~30 entries, the oldest are moved to `GOALS-archive.md` and their detail files to `goals/archive/`.
- If a goal cannot be safely summarized in one line, that is a signal it is really two goals — split it.
- Index entries carry no progress: the status tag is the only state the index records. Incremental progress on an `active` goal (what landed, what remains) is recorded in the detail file's `## Progress` section, never as annotations on the index entry.
- Detail files may be updated without pre-approval when recording findings, decisions, or verification artifacts from work the user directed on that goal or its subject matter; report such updates in the completion summary. Edits to a detail file's `Goal:` or `Acceptance:` lines are contract changes and follow proposal-first.
- If a goal cannot be summarized safely by its title and Scope alone, that is a signal it is really two goals — split it.
## Authoring a new goal (for agents)
When the user asks to "write a goal for X" or "help me draft a goal for Y", do the following:
1. Read this file in full so you know the format and can pick the next free `G-<id>`.
1. Read this file in full and pick the next free `G-<id>`. IDs are never reused: the next free ID is one past the highest ID across `GOALS.md` **and** `GOALS-archive.md` (achieved goals leave this file, so ID gaps here are normal).
2. Ask the user only the questions below. Do not invent answers; ask them one at a time or batched if the user prefers. Stop asking once every required field has a real answer.
- **Scope:** Which repo paths or module areas does this goal touch? (paths are preferred; module names are acceptable if paths are not yet known)
- **Goal:** In one sentence, what does done look like? Push for an outcome, not an activity ("X compiles to bytecode ≤ 1.10× cost of Y", not "improve compiler performance").
- **Goal:** In one or a few sentences, what does done look like? Push for an outcome, not an activity ("X compiles to bytecode ≤ 1.10× cost of Y", not "improve compiler performance").
- **Acceptance:** What is the measurable, verifiable pass/fail criterion an agent can check against? If the user cannot state one, propose 2-3 candidate criteria and ask them to pick or refine.
- **Status:** Default to `proposed` unless the user says it is already in progress (`active`).
- **Detail file?** Only if the goal has non-obvious rationale, a multi-phase plan, alternatives worth recording, or needs more than ~3 lines of prose to state properly. If yes, propose a slug and offer to draft the detail file too. If no, omit the `Detail:` line.
3. Draft the goal block in this file's format and show it to the user for review, applying it only after approval (per the maintenance rules). Do not set it `active` unless the user confirms.
4. If a detail file is warranted and the user approves, create `goals/G-<id>-<slug>.md` using the structure in `goals/AGENTS.md`.
5. Do not delete or rewrite existing goals to make room for a new one. Append with the next free ID.
3. Draft the index entry (this file's format) and the detail file (header block per `goals/AGENTS.md`, plus Context/Approach/etc. sections only when there is real content for them) and show both to the user for review, applying them only after approval (per the maintenance rules). Do not set it `active` unless the user confirms.
4. Do not delete or rewrite existing goals to make room for a new one. Append with the next free ID.
## Goals
<!-- Append new goals below using the format above. Keep the list ordered by G-<id>. -->
<!-- Append new goals below using the format above. Keep the list ordered by G-<id>. IDs are never reused; achieved goals move to GOALS-archive.md, so ID gaps are normal. -->
### G-001 [active] Pluggable console backends for non-detectable terminals
Scope: src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/lib/app-punkshell/punkshell.tcl
Detail: goals/G-001-pluggable-console-backends.md
Goal: an interactive REPL can be launched against a non-detectable terminal-like device (ssh channel, tk text widget) via an ::opunk::Console subclass, with no edits to the base class or punk::console.
Acceptance: a subshell started with an ssh-channel-backed and a tk-widget-backed ::opunk::Console subclass runs an interactive REPL that reads/writes through that console; size, at_eof, and can_respond are answered by the subclass overrides; the base ::opunk::Console and punk::console module are unchanged.
### G-002 [proposed] Non-nested subshell with console targeting and inter-subshell comms
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Detail: goals/G-002-non-nested-subshell.md
Goal: a subshell can target a named console (default or non-default) and run without blocking the parent, replacing the synchronous nested interp-eval model.
Acceptance: a parent REPL launches a subshell against a named console and continues processing its own input while the subshell runs; the parent can signal/query the running subshell; thread::send -async dispatched from within the subshell's code interp arrives at that interp (so packages like promise work when thread features aren't disabled); the "first subshell asymmetry" TODO at repl-999999.0a1.0.tm:3130 is resolved; existing synchronous `subshell punk`/`safe`/`safebase`/`punksafe` behaviour is preserved as a default mode.
### G-003 [proposed] Configurable resource limits and sandboxing on subshell interps
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Detail: goals/G-003-subshell-resource-limits.md
Goal: a subshell's code interp can be launched with configurable resource limits (command-count, time) and sandboxing features, building on the resolved first-subshell asymmetry from G-002.
Acceptance: a subshell can be launched with at least one resource limit (command-count via `interp limit -command`, or time via `interp limit -time`) and one sandboxing feature (e.g. `interp hide` of a command, or full safe-interp restrictions) applied to its code interp, enforceable regardless of subshell nesting depth; a subshell can be configured anywhere on the spectrum from unrestricted to fully safe via expose/hide of commands; the existing default subshell behaviour (no limits, no extra sandbox beyond the existing safe/safebase/punksafe types) is preserved when no limits are configured.
### G-004 [proposed] No executable binaries committed to the repository
Scope: repo-wide (bin/, src/vfs/, src/vendorlib/, src/vendormodules/, src/bootsupport/)
Detail: goals/G-004-no-committed-binaries.md
Goal: the committed repository contains no executable binaries; zip-based .tm modules are allowed but not if they embed executables.
Acceptance: a scan of the committed tree finds no executable binaries (shared libs, .exe, native .so/.dll/.dylib, bare ELF/Mach-O); any zip-based .tm modules present contain no embedded executables; the binary artifacts previously committed are retrievable via G-005 (build from source) or G-006 (pre-built download) so their removal does not break builds.
### G-005 [proposed] Zig-based build infrastructure for binary dependencies from source
Scope: src/runtime/, build.zig / build.zig.zon (new), src/make.tcl integration
Detail: goals/G-005-zig-build-infrastructure.md
Goal: a zig-based build system retrieves and builds binary dependencies (including Tcl9) from source, replacing the committed-binary approach for the vendored/native components.
Acceptance: running the zig build produces the binary artifacts the repo previously committed (at minimum: Tcl9 library for one target platform); existing Tcl9-zig experiments brought into the project; `tclsh src/make.tcl` integrates with the zig build so a normal project build retrieves/builds binaries via zig when not present; no binary artifacts need to be committed for the build to succeed on a clean checkout with the zig toolchain available.
### G-006 [proposed] Optional pre-built binary artifact download with consent gating
Scope: src/runtime/, src/make.tcl integration, user-config (consent flags)
Detail: goals/G-006-prebuilt-artifact-download.md
Goal: pre-built binary artifacts can be downloaded from a separate related binary-artifacts repository or user-configured sources, gated by explicit user consent/configuration by default.
Acceptance: a download mechanism fetches binary artifacts (the same set the zig build produces) from a configured source on demand; by default the download is gated behind explicit user consent (a config flag or interactive prompt) and does not occur silently; a user-configured source URL overrides the default binary-artifacts repo; downloaded artifacts satisfy the same build requirements as zig-built artifacts so `tclsh src/make.tcl project` succeeds with downloaded artifacts in place of built ones.
### G-007 [achieved 2026-07-05] Location-transparent punk::console across repl and code interps
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Detail: goals/G-007-console-location-transparency.md
Goal: punk::console presents one API in every interp/thread of a punk session - code-interp callers see the same console facts and can perform the same queries/operations as the parent, routed to the console-owning context via a punk-side ownership registry, with no `repl eval` required.
Acceptance: from a running punk session's code interp, without `repl eval`: `console_fact_get` returns the same values the parent sees and a fact set in the parent is immediately visible; a terminal query (e.g. `get_cursor_pos` or `dec_get_mode`) against the default console succeeds and cooperates with the repl reader (no lost or garbled input); a console constructed and owned by code-interp code is operated on locally (no round-trip to the parent); the existing console test suites pass and single-interp (non-repl) usage is unchanged.
### G-008 [proposed] Scoped console state for same-console subshells (activatable state sets)
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Detail: goals/G-008-scoped-console-state.md
Goal: a subshell on the shared console can opt into scoped console state - its terminal mutations (tabstops, modes, cursor style, palette, title) are captured as an activatable per-subshell state set and the prior state is re-established on quit or switch-away, with today's shared/persistent behaviour remaining the default.
Acceptance: with a scoped subshell: tabstops, at least one DEC/ANSI mode and at least one palette entry changed inside the subshell are restored for the parent on quit (verified by terminal query where queryable) and the facts store matches the terminal; the default (unscoped) launch behaviour is unchanged; activation is set-based, proven by applying state set A, activating set B, then re-activating A and observing A's state; irreversible outputs (cleared screen/scrollback, emitted text) are documented as out of scope.
### G-009 [proposed] Themed subshell profiles binding poshinfo schemes to behavioural configuration
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/poshinfo-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/island-999999.0a1.0.tm
Detail: goals/G-009-themed-subshell-profiles.md
Goal: a subshell can be launched from a named profile binding a poshinfo-enumerated scheme to behavioural aspects (e.g. G-003 interp limits/hidden commands, punk::island filesystem access) so that restricted subshells are visually distinct, with the theme's terminal effects and associated profile data riding the G-008 state set.
Acceptance: a named profile associating a poshinfo scheme with at least one restriction aspect (an interp limit, a hidden command, or island-restricted filesystem access) launches by name; the scheme's visual state applies on entry (at minimum prompt styling plus one underlying terminal aspect such as a palette change) and is removed/restored on quit via the G-008 mechanism; profile-associated non-terminal data is scoped to the subshell's lifetime; an unthemed launch is unchanged.
### G-010 [proposed] Subshell suspend/resume and tree navigation
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Detail: goals/G-010-subshell-tree-navigation.md
Goal: subshells form a navigable tree - a subshell can be suspended rather than quit, listed, resumed, and the console switched to any live subshell in the tree (e.g. grandchild to grandparent, or across branches) with each subshell's console state re-applied via its G-008 state set, building on the non-nested subshell model of G-002.
Acceptance: from a grandchild subshell a single switch command reaches the grandparent without unwinding through the intermediate parent; switching between subshells on different branches preserves each subshell's session state and re-applies its console state set on activation; suspended subshells can be listed and resumed; `quit` still unwinds to the launching parent as today; a subshell whose switch commands are hidden/restricted cannot initiate switches.
### G-011 [proposed] Optional per-console err channel with defined stderr semantics
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm
Detail: goals/G-011-console-stderr-semantics.md
Goal: a console optionally carries an err channel as an attribute of its canonical {in out} identity - {in out err} specs accepted everywhere -console is, err resolving to process stderr for the default console and to the console's out channel elsewhere - so diagnostics and emit-to-err are first-class per-console operations instead of raw puts stderr.
Acceptance: console_spec_resolve and every -console site accept an {in out err} spec (err optional; existing pair/instance-name/object spec forms unchanged); opunk::Console exposes the err channel (nullable, additive base-class change); an unset err resolves to stderr for the default console and to the console's own out channel otherwise; punk::console's own warnings/diagnostics emitted while operating on a resolvable console go to that console's err (raw puts stderr remains only where no console is in play); an emit-to-err path exists and is exercised by at least one real consumer (e.g punk::repl); the effective err is discoverable from any thread/interp via console_fact_get (fact key err, returning the effective err channel name); ownership/fact/mode-cache keys remain canonical {in out}; the existing console test suites pass unchanged.
### G-012 [proposed] Template system: inert VCS-config payloads and explicit layout refresh
Scope: src/project_layouts/, src/make.tcl, src/modules/punk/mix/ (layout instantiation), fauxlink module (bootsupport 0.1.1 - promoted if chosen as mechanism)
Detail: goals/G-012-template-payload-safety.md
Goal: project layouts carry no live nested VCS-config files - template .gitignore payloads are stored inert (renamed, or fauxlink-encoded) and materialized at project generation - and src/make.tcl has an explicit punkcheck-tracked step that refreshes layout payloads from their canonical sources.
Acceptance: a scan of src/project_layouts finds no file named .gitignore, and git check-ignore --no-index over every layout file matches only root-.gitignore rules (nested rules provably inert); a project generated from each affected layout receives a working .gitignore whose content matches the canonical payload/target; after editing a canonical source (e.g. root .gitignore), the make.tcl template-refresh step updates the derived layout payloads (punkcheck-tracked), covering vendor/punk layouts as well as custom/_project; the previously hidden template files (layout READMEs, vendored TODO-class files) remain git-tracked without per-file force-add exceptions.
### G-013 [proposed] Raw mode as the repl's default input mode
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm
Detail: goals/G-013-raw-mode-default.md
Goal: a repl launched without explicit mode configuration starts in raw input mode with a clean display - every per-keystroke debug emission behind its own repl-operable toggle - and raw editing covers the line-mode essentials (history navigation, cursor movement).
Acceptance: a default launch lands in raw mode with line mode still selectable; with debug toggles at their defaults (off unless stored configuration via ::punk::config says otherwise - see G-014), typing/editing/submitting a command emits no cursor-positioned debug output; the per-keystroke add_chunk frame and the right-hand live editbuf view are gated separately, each toggleable from within a running repl; arrow-key history navigation and left/right cursor movement work in raw mode (current stubs replaced); the marked-line debugrepl output form is retained and works on terminals without cursor addressing (e.g. vt52); the debugrepl first-word activation mechanism is reviewed and the keep/replace outcome (e.g. a proper Tcl command that interp hide can restrict in subshells) is recorded in the detail file; on tcl 8.6 a background-initiated terminal query at an idle raw-mode prompt succeeds (the residue scenario fail-fast-guarded in punk::console 0.7.1).
### G-014 [proposed] ::punk::config stored configuration: toml files with named-subshell scoping
Scope: src/modules/punk/config-0.1.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm, src/vendormodules/tomlish-*.tm (vendored; canonical source in the external tomlish project space)
Detail: goals/G-014-punk-config-toml.md
Goal: ::punk::config loads stored configuration from toml files in the XDG-located config dir - parsed via the vendored tomlish module, never an ad-hoc parser - and consumers resolve settings with per-named-subshell overrides, so features like the G-013 debug-view startup defaults read declared user configuration instead of hardcoded fallbacks.
Acceptance: a setting declared in a toml file under the XDG-located config dir is visible through the punk::config API at repl startup, and with no config files present built-in defaults apply with no errors beyond the existing missing-dir notice; a named subshell resolves its own overriding value for a key also defined at the parent/default scope, and a subshell with no override inherits the outer value (proven with at least one real key); at least one shipped feature (the G-013 editbuf-view startup default is the natural first) reads its default through this path rather than a hardcoded value; all toml reading/writing in punk::config goes through the tomlish module, and the tomlish API procs punk::config consumes carry punk::args (PUNKARGS) documentation - added upstream in the tomlish project and re-vendored here before punk::config implementation proceeds.
### G-015 [achieved 2026-07-07] Punk executable `script` subcommand: reliable non-interactive piped/script execution
Scope: src/vfs/_config/punk_main.tcl, src/lib/app-punkshell/punkshell.tcl (script path or a leaner dedicated app package)
Detail: goals/G-015-script-subcommand-piped-stdin.md
Goal: `<punkexe> script [<scriptname>] [<args>...]` executes a script file or piped stdin content (scriptname optional when input is piped) in an interp preloaded with the basic punk modules and aliases a punk shell provides by default, and always terminates with the script's success/failure as its exit code - without the `shell` subcommand's shellfilter channel transforms/logging stacks and without ever dropping into an interactive shell - so agents can reliably make piped script calls to punk executables.
Acceptance: piping commands to `<punkexe> script` runs them and terminates at stdin EOF with no trailing `exit` required, exit code 0 on success; a failing piped command terminates the process with a nonzero exit code and the error on stderr, never landing in an interactive shell regardless of console availability or PUNK_PIPE_EOF; `<punkexe> script <file> [<args>...]` executes the file with conventional ::argv0/::argv and propagates its error status the same way; the script path installs none of the `shell` subcommand's shellfilter stacks/transforms and the launch plumbing itself emits nothing on stdout/stderr (the current stub's stderr diagnostics removed) so exec-style callers see only the script's own output; the motivating example works with no package require boilerplate: piping `dev projects.work *<name>*` to `<punkexe> script` emits the matching-project table and exits 0, because the script interp carries the default punk shell module/alias environment.
### G-016 [proposed] `projects.work` discovers git-based projects alongside fossil
Scope: src/modules/punk/mix/commandset/project-999999.0a1.0.tm, src/modules/punk/repo-999999.0a1.0.tm
Detail: goals/G-016-projects-work-git-discovery.md
Goal: `dev projects.work <glob>` lists git-based project checkouts as well as fossil-based ones, each result identifying its VCS - fossil discovery stays central-config-db based, and git discovery uses a defined enumeration source (git has no central registry; the chosen mechanism is recorded in the detail file); output gains a `-return table|dict|json` option (default table, unchanged): dict is the canonical machine form (per-checkout records incl the VCS field, documented in the argdoc), json a faithful rendering of it for out-of-process consumers.
Acceptance: with a git-only project on disk registered in the chosen enumeration source and matching the glob, `projects.work` lists its working directory and identifies it as git; existing fossil results are unchanged apart from any added VCS-identifying column; a project that is both git and fossil (e.g. this repo) appears with both indicated rather than duplicated; glob matching remains case-insensitive; `-return dict` yields the documented record structure containing everything the table shows (incl VCS identification), `-return json` round-trips the same data, and the default table output is unchanged for existing users.
### G-017 [proposed] Agents locate local projects via piped `projects.work` calls, not filesystem scanning
Scope: AGENTS.md (root) or a child doc it indexes (guidance content only - no code)
Goal: once G-015 makes piped script calls reliable, repository guidance directs agents asked to locate another local project to query it via a piped `projects.work` call to a punk executable instead of grepping/globbing the wider filesystem.
Acceptance: root AGENTS.md (or a child doc indexed from it) records the exact recommended invocation - executable, subcommand, glob usage, and `-return json` as the parse target with its record fields - and states when filesystem scanning remains appropriate (projects not registered in any discovery source); the guidance is added only after G-015 is achieved (and notes the fossil-only limitation until G-016); following the documented pattern, an agent locates a named sibling project's checkout dir with a single piped call.
Detail: goals/G-017-agent-project-discovery.md
### G-018 [proposed] Zig-built plain tclsh kits: self-contained zip-based executables without punk infrastructure
Scope: build.zig / build.zig.zon (per G-005), src/runtime/, src/make.tcl integration
Detail: goals/G-018-zig-plain-tclsh-kits.md
Goal: developers can use the G-005 zig build system to produce self-contained zip-based tclsh executables that carry no punk-specific infrastructure (no punk boot layer, punk modules, or punk apps) - plain tclsh kits usable independently of the punkshell product.
Acceptance: a documented zig invocation on a clean checkout (zig toolchain available) produces a zip-based tclsh executable for at least one target platform that runs conventional tclsh invocations (`<exe> script.tcl args`, piped stdin) on a machine with no Tcl installation; a listing of the kit's mounted/zip contents shows stock Tcl (plus any declared stock runtime deps) and no punk namespaces, punk boot files, or punkshell apps; the punk-flavoured executables remain producible alongside.
### G-019 [proposed] Dependency-scan-driven module trimming for punk-based executables
Scope: src/make.tcl, src/modules/punk/lib-999999.0a1.0.tm (tclparser use), src/vfs/ (kit assembly), scanning module (new or existing punk module - to be determined)
Detail: goals/G-019-dependency-scan-module-trimming.md
Goal: a package-dependency scan from an executable's entrypoint (candidate basis: the tclparser parse API - currently satisfied only by the c-only tclparser library, with punk::lib's pure-Tcl fallback an unimplemented stub) determines the module closure the executable actually requires, so a build can ship only those modules - while 'batteries included' builds remain a supported alternative, not a casualty.
Acceptance: for at least one punk-based executable target, the build can run a dependency scan from its entrypoint producing the closure of required packages/modules plus a mechanism to declare dynamically-loaded extras the scan cannot see; a trimmed kit assembled from that closure starts and passes its basic function check (e.g. repl launch or the app's smoke test) with no missing-package errors; the trimmed kit's module listing is a strict subset of the batteries-included equivalent (demonstrating real exclusion); batteries-included builds remain producible unchanged.
### G-020 [proposed] Screen capture and input injection module with per-platform backends (Windows first)
Scope: src/modules/punk/ (new module - name TBD), src/vfs/punk9win.vfs/lib_tcl9/ (existing treectrl/Img/twapi payloads); scriptlib/aloupe.tcl stays untouched as a standalone app
Detail: goals/G-020-screencap-input-module.md
Goal: a punk module drives screen/window capture and mouse/keyboard injection from scripts via per-platform backends - Windows (treectrl loupe capture + twapi input/window-location) is the initial complete target, with the backend contract designed so X11 (Linux/FreeBSD) and macOS backends can be added without changing callers, and Wayland-native sessions explicitly out of scope.
Acceptance: on Windows from a punk shell or script: a screen region and a window located by title/class pattern are each captured to a Tk photo and written as a valid PNG; mouse movement/click and key events injected into a located test window produce their observable effect (typed text arrives, click acts); window location returns the handle and geometry for a pattern; a capability-introspection call reports per-feature support and an unsupported platform/backend yields a clean capability-based refusal, not a crash; the backend interface (capture / input / window-locate) is documented well enough that a non-Windows backend can be added without modifying callers; the aloupe script remains functional and unmodified.
### G-021 [proposed] Agent-drivable visual/UI verification via piped snapshot and interaction calls
Scope: src/modules/punk/ (G-020 module's agent-facing surface), AGENTS.md guidance (post G-015 pattern), src/tests/ (visual-verification test hooks)
Detail: goals/G-021-agent-visual-verification.md
Goal: a tool-calling agent can, during a session, use piped script calls (G-015) to a punk executable to locate the applicable UI window, snapshot it to a PNG file and/or base64 output suitable for AI image analysis, and drive mouse/keyboard interactions - enabling tests whose verification is visual-only and/or input-driven.
Acceptance: on Windows, single piped script calls (no interactive session) can: list/match windows for a pattern with machine-parseable output; save a located window's snapshot to a caller-specified path and optionally emit it base64 on stdout; run a scripted interaction sequence (focus, click at offset, type text, snapshot) end-to-end; failures exit nonzero with the error on stderr per G-015 semantics; the invocation patterns are documented for agents alongside the G-017 guidance; at least one real visual-or-input-driven verification (e.g. a Tk app smoke test) is exercised through this path.
### G-022 [proposed] Scriptable safe fossil move/rename in `dev repo`; rename this project's fossil repo to punkshell
Scope: src/modules/punk/mix/commandset/repo-999999.0a1.0.tm, src/modules/punk/repo-999999.0a1.0.tm, src/tests/modules/punk/mix/testsuites/repo/
Detail: goals/G-022-fossil-rename-punkshell.md
Goal: the `dev repo` commandset can move and rename fossil repositories non-interactively and safely - all checkouts repointed, no phantom central config-db entries, no dangling old repo db, fossil project-name renamable with project-code unchanged - and this project's fossil repo (currently project-name 'shellspy') is renamed to 'punkshell' through that mechanism via a G-015 piped script call, not by hand.
Acceptance: the commandset provides a flag-driven (no stdin prompts) move/rename operation which on a scratch repo with an open checkout: repoints every registered checkout, leaves the central config-db listing only the new path, removes or archives the old repo db file (per option), clears stale ckout: back-references, and applies a requested project-name change while preserving project-code; the GAP characterization tests in src/tests/modules/punk/mix/testsuites/repo/fossilmove.test are updated to assert the clean behaviour and pass; after G-015 is achieved, this repo's fossil db (shellspy.fossil / project-name shellspy) is renamed to punkshell via the new operation invoked through a piped `script` call, with `fossil info` in this checkout showing the new repository path and project-name and `fossil all ls` free of the old path.
### G-023 [proposed] Version-named punk binaries per Tcl generation (versioned / dev / release-gated plain names)
Scope: src/make.tcl, src/runtime/ (mapping config - see G-024), bin/ (build outputs)
Detail: goals/G-023-version-named-binaries.md
Goal: project builds produce version-named punk executables for tcl 8.6 and tcl 9 as the project version advances - punk8-<major>-<minor>-<patch>.exe / punk9-<major>-<minor>-<patch>.exe per version, punk8-dev.exe / punk9-dev.exe tracking the latest build, and plain punk8.exe / punk9.exe created initially then replaced only when an actual release is tagged - tolerating the disk growth for now.
Acceptance: a project build at the current punkproject.toml version produces punk8-<M>-<m>-<p>.exe and punk9-<M>-<m>-<p>.exe (names derived from the version, not hand-maintained) plus punk8-dev.exe / punk9-dev.exe updated to that same build; rebuilding at an unchanged version refreshes that version's binaries and -dev without touching other versions' outputs; plain punk8.exe / punk9.exe exist and are replaced only by an explicit release step - a normal build never overwrites them; the scheme is declared succinctly via the G-024 toml mapping (no per-version config edits); archival/deletion of accumulated versioned binaries is out of scope with the trigger question recorded in the detail file.
### G-024 [proposed] mapvfs.config converted to toml (tomlish-parsed) with succinct scheme declarations
Scope: src/runtime/mapvfs.config (replaced/deprecated), src/make.tcl (parsing), src/bootsupport/modules/tomlish-*.tm (parser dependency)
Detail: goals/G-024-mapvfs-toml.md
Goal: the runtime-to-vfs-to-executable build mapping moves from the custom line format of src/runtime/mapvfs.config to a toml file parsed with the tomlish package - still supporting explicit per-executable mappings (runtime, vfs folder, output name, kit type) while also expressing generative schemes like the G-023 versioned naming in a single succinct declaration.
Acceptance: a mapvfs toml file parsed via tomlish (no ad-hoc toml parsing) drives the build: every mapping currently active in mapvfs.config is expressible and at least one existing target builds identically from the toml; the G-023 versioned/dev/release-gated output scheme is declared in one entry that expands to its outputs without enumerating versions; malformed or unresolvable entries fail the build with a clear message naming the entry; the legacy .config format is either fully migrated (old parser removed) or explicitly deprecated with documented precedence between the two files.
### G-025 [proposed] Punk executables self-report project version and build provenance
Scope: src/vfs/_config/punk_main.tcl (subcommand dispatch), src/make.tcl (stamping build info into the vfs), src/vfs/ (stamp payload location), src/modules/punk/ (in-shell command - the single implementation)
Detail: goals/G-025-exe-selfreport.md
Goal: a punk executable reports its identity from embedded data rather than its filename - a documented subcommand prints the punkproject.toml project version it was built from plus the input runtime binary name and vfs folder name used to assemble it - with the same-named command available in the punk module so scripts running in any punk shell (including tclsh-hosted ones like `tclsh src/make.tcl shell`) get the same report in-process without exec, stamp fields reported as absent rather than fabricated when there is no stamp.
Acceptance: the build stamps project version, runtime binary name, and vfs folder name into the kit; the built executable invoked with the version-report subcommand prints those fields machine-parseably on stdout and exits 0 with no other output (G-015-compatible; no repl fallthrough); a same-named command in the punk module returns the same fields in-process (subcommand implemented as a wrapper over it - one implementation) and works from the code interp; the report distinguishes stamped provenance from live facts: a stamped kit reports its stamp, a `src`-mode or source-tree session additionally reports the live punkproject.toml version as a distinct field when it differs, and unstamped contexts (`tclsh src/make.tcl shell`, plain tclsh with punk modules) report stamp fields explicitly absent with live runtime facts (actual `info nameofexecutable`) still provided; the report is correct when the executable file has been renamed or copied; executables built before stamping existed fail gracefully with a clear message rather than fabricating values.
### G-026 [proposed] Enforceable clean-checkout provenance policy for vendor and bootsupport pulls
Scope: src/make.tcl (vendorupdate and bootsupport steps), src/vendormodules/include_modules.config, src/bootsupport/modules*/include_modules.config
Detail: goals/G-026-vendor-provenance-policy.md
Goal: pulling vendored or bootsupport artifacts from local source projects enforces committed provenance - the warn-only dirty-checkout check added to vendorupdate in project 0.2.5 becomes a policy that can abort with an explicit override, covers the bootsupport update path as well, and the residual staleness question (built modules that predate or postdate the committed source even in a clean checkout) has a recorded design decision.
Acceptance: vendorupdate and the bootsupport update refuse to pull from a source project whose fossil/git checkout is dirty unless an explicit documented override is given (warn-only selectable as a configured mode); the check reports each VCS root once per run and does not fire for unversioned source locations; bootsupport_localupdate is covered by the same shared check (no second divergent implementation); the staleness gap - a clean checkout whose built modules/ artifacts do not correspond to the committed source - is either detected (mechanism chosen and implemented) or explicitly recorded in the detail file as accepted risk with the considered mechanisms; behaviour is exercised by a test or documented manual verification against a scratch dirty checkout.
### G-027 [proposed] Pull-based infrastructure updates for punkshell-derived projects
Scope: src/modules/punk/mix/commandset/project-999999.0a1.0.tm (project.new push path), src/modules/punkcheck-999999.0a1.0.tm (install provenance records), src/make.tcl (derived-project pull entrypoint)
Detail: goals/G-027-derived-project-pull-updates.md
Goal: a project generated from a punkshell layout can pull infrastructure updates (make.tcl/build.tcl, bootsupport modules and libs, layout template payloads) from its originating punkshell project by running one command inside the derived project - replacing the current push model (`dev project.new -force 1 -update 1` run from punkshell) - with install provenance robust to derived-project workdir moves (not local relative paths alone), VCS-state awareness on both ends, and a recorded decision on pulling from remote sources.
Acceptance: one documented command run inside a derived project updates its punkshell-derived infrastructure from the source punkshell project; the update still works after the derived project's working directory has been moved (proven by moving a scratch derived project and pulling); VCS integration on both ends: the pull applies the G-026 clean-checkout policy to the punkshell source, and reports (or refuses per option) when target files it would overwrite carry uncommitted local modifications in the derived project's git/fossil checkout; .punkcheck records remain the provenance basis (updates are recorded and unchanged targets skipped, as with existing punkcheck-tracked installs); the push flow keeps working until explicitly retired; the remote-pull question (updating from a remote punkshell repository rather than a local checkout) has a recorded design decision - implemented, or deferred with rationale in the detail file.
### G-028 [proposed] Name the process locking a file when builds cannot replace a target
Scope: src/modules/punkboot/utils-999999.0a1.0.tm (locker-report helper), src/make.tcl (kit deploy failure reporting)
Detail: goals/G-028-file-locker-identification.md
Goal: when a build cannot delete/overwrite a target file (typically a built executable held open by another program), the failure message names the locking process(es) - via a punkboot::utils helper using the Windows Restart Manager API through optionally-available twapi or cffi, degrading cleanly to the current message when the API or bindings are unavailable or on other platforms.
Acceptance: on Windows with twapi or cffi loadable, a punkboot::utils proc given a file path returns the locking processes (at least pid and process name; empty list when unlocked); punkboot::utils itself stays pure Tcl - the binary binding is required lazily at call time and its absence yields a clean 'unavailable' result, not an error (bootsupport must not gain a compiled-extension dependency); make.tcl kit deploy failures include the locker report when determinable (e.g. "could not delete target binary ... in use by: 7zFM.exe (pid 1234)"); non-Windows platforms and binding-less environments produce the existing message unchanged; verified against a deliberately held handle (documented manual verification acceptable).
### G-029 [proposed] Build packaged test::<modulename> #modpod modules from src/tests
Scope: src/make.tcl (test-module packaging step), src/tests/ (source of truth), src/modules/test/ (generated #modpod targets), src/modules/punk/mix/ (modpod tooling as needed)
Detail: goals/G-029-testmodules-from-srctests.md
Goal: src/tests is the single source of truth for module test suites - a punkcheck-tracked make.tcl step generates the packaged test::<modulename> #modpod modules from src/tests/modules/<namespacepath>/testsuites content, ending hand-maintenance of parallel copies under src/modules/test/; the packaged form is a distributable in its own right - a user who downloads a built module can optionally download the matching test::<modulename> and verify the module's behaviour on their own system (package require + RUN, or an executable's -app test) with no source tree or test harness required.
Acceptance: a make.tcl step generates a test::<modulename> #modpod under src/modules/test/ from the corresponding src/tests testsuites (punkcheck-tracked, skipped when sources unchanged); the generated package works through the packaged path - loadable via package require test::<modulename> and runnable via its SUITE/RUN interface (e.g a built executable's -app test) - reporting the same test names and pass counts as running the same suites directly via src/tests/runtests.tcl; the consumer scenario is proven standalone: the generated package (plus the module under test and their dependencies) runs in an environment without the project source tree present; suite data files (e.g roundtrip toml files with deliberate crlf/mixed line endings) survive packaging byte-for-byte; documentation states src/tests is the source of truth and the generated modpods are build artifacts not to be hand-edited; proven end-to-end for at least one real module (tomlish, whose src/tests port and still-live modpod created the dual-copy situation, is the natural first).
### G-030 [proposed] make.tcl dogfoods punk::args: tabled usage, declared subcommands, prompt-free flags
Scope: src/make.tcl (dispatch, help, prompts), src/bootsupport/AGENTS.md + src/modules/AGENTS.md (bootstrap-tracked staleness contract), src/modules/punk/args-999999.0a1.0.tm (only as consumed)
Detail: goals/G-030-maketcl-punkargs.md
Goal: make.tcl - the first surface a developer sees - parses its subcommands and options via punk::args and showcases the tabled usage output for help and argument errors, every interactive y/n prompt gains a declared flag equivalent so agents can drive make.tcl with arguments instead of piped input, punk::args joins the bootstrap-tracked staleness set, and the boot phase plus the environment-repair commands keep working with degraded plain help when the bootsupport punk::args (or the table-rendering stack) is stale or unavailable.
Acceptance: `tclsh src/make.tcl` and `-help` render punk::args tabled usage listing every subcommand with a summary, and `make.tcl help <subcommand>` (or `<subcommand> -help`) shows that subcommand's definition; invalid arguments produce a punk::args usage error rather than ad-hoc messages; every y/n prompt has a documented flag equivalent (proven at least for vfscommonupdate and the project-build confirmations: a run with the flag completes non-interactively with stdin closed) and a non-interactive stdin without the flag fails fast with usage rather than hanging or half-aborting; punk::args is added to the bootstrap-tracked buildversion set with the doc contract updated (src/bootsupport/AGENTS.md, src/modules/AGENTS.md); with bootsupport punk::args unavailable or unloadable, make.tcl still boots and `check`, `bootsupport` and `modules` remain usable with plain-text fallback help (the guarded-require degrade rule); layout make.tcl copies follow via the established sync/G-027 channels (noted, not hand-synced).
### G-031 [proposed] Componentized kit boot: thin project-owned main + shared layout-owned boot core
Scope: src/vfs/_config/ (punk_main.tcl, project_main.tcl restructure), src/vfs/_vfscommon.vfs (boot core delivery), src/project_layouts/ (thin-main skeleton, via established sync channels)
Detail: goals/G-031-componentized-kit-boot.md
Goal: the per-project vfs main script becomes a thin project-owned file - declare the application's subcommands and launch defaults at clearly commented customization points, then hand over to a shared layout-owned boot core (vfs mounts, package modes and paths, libunknown, src-mode modpod registration) and default dispatch pulled in from within the kit - so project developers add app-specific subcommands without wading through or forking ~1000 lines of boot boilerplate, and boot improvements reach derived projects as pull-updatable payload instead of dying in vintage forks (tomlish_main.tcl: ~20 custom lines carrying a stale 500-line 2025 copy of the rest).
Acceptance: punkshell's own kits boot through a thin main plus shared boot core with behaviour parity - package modes including src mode, existing tclsh/shellspy/punk/shell/script dispatch semantics, and supported vfs types (zipfs/metakit/cookfs) all unchanged; a project-specific subcommand is added by editing only the thin main at a commented customization point (proven end-to-end in a derived project - tomlish replacing its forked main is the natural first); the boot core ships as layout-owned payload (via _vfscommon/layout channels) and the thin main as a project-owned skeleton, per the G-027 ownership classification; the boot core is versioned/identifiable so a kit can report which boot-core vintage it carries (ties to G-025 stamping).
### G-032 [proposed] Kit launcher dogfoods punk::args: tabled help and parsed subcommands
Scope: src/vfs/_config/ (default dispatch), src/lib/app-punkshell and sibling app packages as touched
Detail: goals/G-032-launcher-punkargs.md
Goal: the default launch dispatch defines its subcommands via punk::args - `<punkexe> -help` and argument errors render the tabled usage enumerating built-in and project-registered subcommands with summaries, and subcommand options parse through punk::args so projects can declare complex arguments - with the G-030 degradation rules (boot never fails and help degrades to plain text when punk::args or the ANSI rendering stack is unavailable).
Acceptance: `<punkexe> -help` renders tabled usage listing all subcommands including project-registered ones, each with a summary; at least one built-in subcommand's options are declared and parsed via punk::args with tabled usage errors on invalid input (the G-015 script subcommand or the G-025 version-report subcommand are the natural candidates); a project-registered subcommand's help appears by registration alone - no edits to the shared dispatch; with punk::args or the rendering stack unloadable, boot proceeds and help degrades to a plain subcommand list; verified on both a zipfs-based and a non-zipfs kit where both remain supported.
### G-033 [proposed] `proj:` package-mode scope prefix: visitor binary resolves dev/src against the cwd's project
Scope: src/vfs/_config/punk_main.tcl (package-mode dispatch and boot-time root discovery), src/modules/punk/repo-999999.0a1.0.tm (find_project / is_project_root - reuse or lean boot mirror), bin/AGENTS.md (mode docs)
Detail: goals/G-033-proj-mode-cwd-project.md
Goal: a `proj:` prefix on the package-mode string (e.g. `punkshell proj:internal-src shell`) makes the dev/src path blocks resolve against the punk project containing the current working directory - walking up to the nearest VCS repo root, the marker punk::repo::is_project_root already uses - instead of the executable's own project, so an installed ("visitor") punkshell binary, including one downloaded standalone with no source tree around it, can interactively explore a project that builds no shell-capable binary or no binary at all; the prefix scopes WHICH project, staying outside the ordered dash-list whose block order remains the same-version tie-break dial, and the explicit prefix gates the behaviour so no `cd` silently rebinds a normal launch.
Acceptance: `<installed-punkexe> proj:internal-src shell` (the documented canonical visitor invocation - kit copies win same-version ties, protecting the visiting shell's infrastructure; `proj:src` is the documented faithful-vintage variant where the project's copies win) launched from within a punk project's tree discovers the project root by walking up from cwd to the nearest VCS repo root and feeds it to the existing src-mode path machinery (src/modules, src/bootsupport/modules, src/vendormodules on the module path, src/lib on auto_path), so the session loads the project's dev-versioned modules while ordinary version resolution still lets the project supply anything the kit lacks or exceeds; the launch reports the detected project root and effective path precedence (never a silent rebind); with no project found walking up from cwd, or a `proj:` string containing no root-using block (dev/src), it warns and proceeds without false rebind; proven for a standalone binary outside any source tree (internal = kit contents regardless of binary location) against a project that builds no executable (tomlish is the natural first); exe-relative `src`/`dev` discovery is unchanged for a binary that IS in a project's bin/; the packagemode help text drafted in the detail file becomes the live punk::args documentation when implemented (rendered via G-032).
### G-034 [proposed] Zip-based #modpod modules mount in the shell code-interp on Tcl 8.6
Scope: src/modules/punk/mix/ (modpod mount path), vfs::zip availability in the repl code interp, src/modules/punk/cap/ (templates capability handler)
Goal: zip-based `#modpod` modules (e.g. `punk::mix::templates`) mount and their punk::cap handlers register in the `shell` subcommand's code interp on Tcl 8.6, matching the `script`/main-interp context - so `dev module.templates` and other `punk.templates`-capability consumers work in an interactive 8.6 shell instead of failing with `invalid command name vfs::RegisterMount`.
Acceptance: `dev module.templates` in an interactive 8.6 punk shell (`shell` subcommand) lists the template providers with no `failed to load ZIP archive-based module` / `invalid command name vfs::RegisterMount` / `Unable to register any template providers` / `invalid command name ::punk::cap::handlers::templates::api_punk.templates` errors, matching the `script`-subcommand output on the same binary (verified 2026-07-07: script works, shell fails); root cause fixed (the code interp lacks the vfs::zip library that provides vfs::RegisterMount for pre-zipfs Tcl, present in the main interp - restore it to the code interp or use an alternative modpod mount path there); Tcl 9 (built-in zipfs) behaviour is unchanged.
Detail: goals/G-034-modpod-codeinterp-tcl86.md
### G-035 [proposed] Characterise mixed .tm / pkgIndex.tcl provision of the same package
Scope: src/tests/modules/punk/libunknown/testsuites/ (characterization suite), src/modules/punk/libunknown-0.1.tm and src/modules/punk/packagepreference-999999.0a1.0.tm (as characterised, fixed only if outright bugs surface), src/modules/AGENTS.md + src/lib/AGENTS.md (resulting guidance)
Detail: goals/G-035-mixed-tm-pkgindex-provision.md
Goal: the behaviour when the same package is provided both as a .tm module and as a pkgIndex.tcl-based library - same or differing versions, under the standard package unknown, punk::libunknown and punk::packagepreference - is characterised by committed tests, and the currently informal working rule ("avoid mixing provision forms for one package - unexpected behaviour even with libunknown's improvements") is either substantiated with the specific failure modes named in AGENTS.md guidance, or retired if the characterisation shows the machinery now handles mixing predictably.
Acceptance: a committed test suite (extending src/tests/modules/punk/libunknown/testsuites/) characterises at least: same name+version provided via .tm and via pkgIndex.tcl (which registration wins, and whether it is deterministic across scan-trigger orderings) under the standard scanner, under punk::libunknown, and with punk::packagepreference active; differing versions across the two forms (version selection integrity including package prefer latest, and whether the losing form's registration lingers); re-registration effects (package forget then re-require crossing forms); surprising-but-accepted behaviours are pinned with GAP/known-quirk comments (the fossilmove characterization pattern), outright bugs fixed or filed as goals; the resulting do/don't guidance lands in src/modules/AGENTS.md and src/lib/AGENTS.md naming the characterised failure modes (or explicitly lifting the avoid-mixing rule if unwarranted).
### G-036 [achieved 2026-07-08] Root-cause the Tcl 9 console+udp worker-thread event-loop wedge; minimal repro for possible upstream reporting
Scope: src/modules/shellthread-999999.0a1.0.tm, src/bootsupport/modules/shellthread-1.6.2.tm, src/modules/shellfilter-999999.0a1.0.tm (as characterised - no product-code changes required by this goal; the punkshell mitigations are separate fixes)
Detail: goals/G-036-tcl9-udp-console-worker-wedge.md
Goal: the Tcl 9-only wedge - a worker thread that has used a tcludp syslog socket stops servicing its event queue (timers, thread::send) when the punkshell process is console-attached, the proximate trigger of the piped-stdin exit/quit hang - is root-caused to a named component (Tcl 9 Windows console driver, tcludp, Thread extension, or a specific interaction) with the smallest demonstrating repro, so the user can decide on and manually file an upstream report.
Acceptance: (reworked 2026-07-08 after the root cause was found) the wedge mechanism is identified and written up in the detail file - DONE: bundled tcludp 1.0.12's Windows per-thread UDP_ExitProc closes the process-global synchronization events, proven by dump handle-table analysis plus a live CloseHandle breakpoint and confirmed by the upstream 1.0.12->1.0.13 diff, which already fixes it (so no upstream report is required; the standalone minimal repro originally required here is waived as moot by the user); the tcl9 kits bundle tcludp >= 1.0.13 with the in-context batch harness baseline resolved - DONE 2026-07-08 (run-2 syslog workers alive vs the 4/4-wedged 1.0.12 baseline); DONE 2026-07-08 (punk::lib 0.3.0 has_libbug_udp_threadexit, surfaced via 'help tcl' in punk 0.2.1): a has_bug-style detection in the punkshell check machinery (in the vein of punk::lib::check::has_tclbug_* / punk::console::check::has_bug_*, surfaced through the same reporting as 'help tcl'/'help console') reports the vulnerable combination - simple version-based detection (loaded/bundled tcludp < 1.0.13 on Tcl 9 Windows) is sufficient, no behavioural probe needed; loose-end decisions (punk8win 8.6 kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses) recorded in the detail file when made (open as of 2026-07-08 - non-gating).
### G-037 [achieved 2026-07-08] Propagate platform vendor libraries into kit vfs lib_tcl trees via make.tcl
Scope: src/make.tcl (new or extended step), src/vendorlib_tcl8 + src/vendorlib_tcl9 (sources), src/vfs/<kit>.vfs/lib_tcl8 + lib_tcl9 (targets), punkcheck tracking
Detail: goals/G-037-vendorlib-vfs-propagation.md
Goal: platform-specific vendored binary packages under src/vendorlib_tcl<N>/<platform> reach the kit vfs lib_tcl<N> trees through a make.tcl step instead of hand-copying - updating a vendored package becomes a vendorlib drop plus standard build invocations (motivating case 2026-07-08: tcludp 1.0.12 -> 1.0.13 for the G-036 wedge fix - `libs`, `vfscommonupdate` and `project` all completed while every kit vfs still bundled udp 1.0.12, requiring a manual copy into each vfs lib_tcl9 folder).
Acceptance: with a newer package version placed under src/vendorlib_tcl9/<platform>, one documented make.tcl invocation updates the participating src/vfs/*/lib_tcl9 trees - installing the new package and removing or explicitly retiring the superseded version (no silent mixed-version provision, per the G-035 concerns) - with punkcheck-tracked provenance; which vfs folders participate is explicitly declared per kit rather than blanket-copied (kit vfs package sets may intentionally differ), with the declaration mechanism recorded (candidate home: the G-024 mapvfs toml); a subsequent `make.tcl project` yields kits loading the new version (provable via the tcludp case: built punk902z reports `package require udp` == 1.0.13 with no udp1.0.12 folder remaining in its vfs); the lib_tcl8 tree gets the same treatment or an explicit exclusion rationale in the goal record.
### G-038 [proposed] Piped-to-interactive restart continues the same session (context preserved)
Scope: src/lib/app-punkshell/punkshell.tcl (eof-restart handover), src/modules/punk/repl-999999.0a1.0.tm (eof-restart done-mode that skips codethread teardown), src/modules/punk/repl/codethread-999999.0a1.0.tm (as touched)
Detail: goals/G-038-piped-session-continuity.md
Goal: when piped stdin ends and app-punkshell opens the interactive console shell, the session continues rather than restarts - the piped phase's codethread/code interp survives (variables, procs, namespaces, loaded packages, cwd, ::errorInfo/::errorCode) and only the input channel and repl reader are renewed - so `'set ::jjj blah' | <punkexe> shell` leaves ::jjj inspectable at the prompt, replacing today's silent fresh-session swap (least-surprise violation; motivating transcripts in the detail file) and obsoleting the standalone piped-error-record mechanism this goal described before its 2026-07-08 rework.
Acceptance: after `'set ::jjj blah;error xxxx' | <punkexe> shell` reaches the interactive prompt: `set ::jjj` returns blah; the xxxx message, errorInfo traceback and errorCode are inspectable (via preserved ::errorInfo if nothing in the handover overwrites it, else a documented record - the chosen mechanism noted in the detail file); a proc defined and a package required during the piped phase remain available; a cwd change from the piped phase persists; a one-line notice at the restart says the session continued from piped input (mentioning the error when the last piped command errored, quiet otherwise); after the restart a terminal query from the code interp succeeds (e.g. `help console` runs its cursor-position test instead of being refused by the stale settled can_respond=0 anchor); interactive exit/quit teardown afterwards is clean (the G-036 regression harness still passes); PUNK_PIPE_EOF policy semantics and the script subcommand are unchanged; verified on both Tcl generations.
### G-039 [proposed] Investigate the orphaned-shell one-core spin on a dead console
Scope: src/modules/punk/repl-999999.0a1.0.tm (console reader/event loop and EOF/error paths), src/modules/punk/console-999999.0a1.0.tm; investigation-first
Detail: goals/G-039-orphan-console-spin.md
Goal: the observed failure mode - an interactive punk902z left running after its hosting terminal/console went away spins roughly a full core indefinitely (observed 2026-07-08: a 37-minute orphan with a single hard-looping thread) - is reliably reproduced and root-caused, then fixed or mitigated so a shell whose console dies exits or reaches zero-CPU idle cleanly.
Acceptance: a documented procedure reproduces the spin on the current kit (e.g. launch an interactive shell in a terminal, then kill/close the hosting terminal or conhost), or the investigation records the attempts made and what evidence would reopen it; the spinning code path is identified (prime suspect: a console read/event loop treating a dead console's immediate EOF/error as retryable without backoff or termination - adjacent to the console-EOF restart path G-038 takes ownership of); after fix/mitigation, the same procedure shows the orphaned process exiting or settling at effectively zero CPU within a short grace period, with live-console interactive behaviour unchanged; the wedge-scoring hazard note (orphans polluting process-liveness checks in test harnesses) is updated to match the outcome.
### G-040 [achieved 2026-07-08] punk::args choice aliasing (-choicealiases) with parse normalization, display folding, and doc-lookup parity
Scope: src/modules/punk/args-999999.0a1.0.tm (parse + usage rendering), src/modules/punk/ns-999999.0a1.0.tm (cmdinfo/cmd_traverse choice resolution parity), src/modules/punk-999999.0a1.0.tm (punk::help topic argdoc as first consumer), src/tests/modules/punk/args/testsuites/, src/tests/modules/punk/ns/testsuites/
Detail: goals/G-040-punkargs-choicealiases.md
Goal: punk::args supports choice aliases (-choicealiases {alias canonical ...}) accepted at parse and normalized to the canonical choice in results, folded into the canonical entry in usage display - and the punk::ns doc-lookup walk resolves choice words by the same rules as the parser (aliases, -choiceprefix, -choiceprefixdenylist, -choiceprefixreservelist) - so alias sets like punk::help's topics|help and console|term|terminal collapse to one displayed entry per topic with `help X` and `i help X` agreeing.
Acceptance: a definition using -choicealiases parses an alias (and an alias prefix where -choiceprefix allows) to its canonical choice in the parse result, with -choicerestricted 0 passthrough and the deny/reserve lists honoured unchanged; usage display shows one entry per canonical choice with aliases folded (no duplicate rows; -choicelabels attach to the canonical); punk::ns::cmdinfo/cmd_traverse resolve subcommand words to docids with the same outcome as the parser for alias, prefix, denied, reserved and unknown words (the pre-goal characterization tests updated from pinned-GAP to fixed); punk::help's topic definition adopts the feature so `i help` lists one entry per registered topic while `help h`/`help e` still fall through to command lookup; definitions without -choicealiases behave unchanged (existing punk::args and punk::ns suites pass).
### G-041 [proposed] punk::args multi-form matching: automated form selection for parsing and documentation
Scope: src/modules/punk/args-999999.0a1.0.tm (parse form selection, arg_error/usage form marking), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/synopsis closest-form indication), src/tests/modules/punk/args/testsuites/
Detail: goals/G-041-punkargs-form-matching.md
Goal: for multi-form definitions punk::args determines which form(s) an argument list matches - parse without -form attempts all permitted forms instead of effectively form 0, -form accepts the documented list-of-forms restriction, and the documentation surface indicates the match ('i after cancel <id>' presents the cancel form; 's after cancel someid' marks the closest synopsis) - with explicit single-form restriction retained for callers that require it.
Acceptance: parsing a multiform definition (after-like fixture) without -form succeeds when the args match exactly one form (the pinned GAP tests in forms.test flip to auto-selected results); an argument list matching no form (or several) produces an error naming the candidate forms rather than a form-0 type error; -form with a list of form names/indices restricts parsing to that subset (currently an 'Expected int 0-N or one of ...' error); 'i <cmd> <args...>' and synopsis output indicate the best-matching form(s) for supplied args; explicit -form <single-name-or-index> behaviour is unchanged; the full punk::args and punk::ns suites pass.
### G-042 [proposed] Subshell-declared help topics via punk::config with defined shadowing policy
Scope: src/modules/punk-999999.0a1.0.tm (::punk::helptopic registry), src/modules/punk/config-0.1.tm (stored declaration source), src/modules/punk/repl-999999.0a1.0.tm (subshell entry/exit hooks)
Detail: goals/G-042-subshell-help-topics.md
Goal: a named subshell can declare its own help topics (name, aliases, summary, text content) in punk::config stored configuration - registered into the ::punk::helptopic registry on subshell entry and removed/restored on exit - so 'help' inside a customised subshell presents that subshell's topics with 'help topics' and 'i help' staying accurate (the registry already regenerates the punk::args definitions), under a defined precedence policy between topics and command-name fallthrough.
Acceptance: a subshell whose configuration declares at least one custom topic shows it in 'help topics' and renders its configured content via 'help <topic>' inside that subshell, and 'i help' lists it as a documented choice; on quit/switch-away the parent's topic set is restored (no leakage, proven by declaring a topic in a subshell and checking the parent after exit); configuration is read through punk::config (toml per G-014 - no ad-hoc parsing) and a shell with no topic configuration behaves unchanged; config-declared topics are text-content only - they cannot name code to execute (or a recorded design decision to the contrary with its sandboxing rationale in the detail file); the shadowing policy is implemented and documented: a declared topic colliding with a built-in topic is rejected (or explicitly overrides per a documented rule), a topic name shadowing a command name wins over command fallthrough as today with 'i <cmd>'/'s <cmd>' documented as the command-help escape hatch; depends on G-014 for the stored-config substrate - the registry-side registration/restore mechanism may land earlier behind a programmatic API, with the config binding completing when G-014 lands.
### G-043 [proposed] Subshell definition plugins: punk.subshell capability composing commandsets, help topics and config defaults
Scope: src/modules/punk/cap-999999.0a1.0.tm (+ new punk.subshell handler), src/modules/punk/overlay-999999.0a1.0.tm (commandset composition), src/modules/punk/pluginmgr-0.5.1.tm (discovery/trust layer), src/modules/punk/mix-999999.0a1.0.tm (init wiring precedent), src/modules/punk/repl-999999.0a1.0.tm (subshell entry/exit), src/modules/punk/config-0.1.tm (data-declaration overlap with G-014/G-042)
Detail: goals/G-043-subshell-definition-plugins.md
Goal: a named subshell's definition - commandset imports (punk::overlay), help topics (the G-042 registry API) and config defaults - can be supplied by provider packages registered through a punk.subshell capability (punk::cap handler validating declarations) and/or declared in stored config (toml per G-014, naming already-installed commandset packages - absorbing punk::overlay's "toml configuration files for defining CLI configurations" todo), with punk::pluginmgr as the discovery/safe-interp trust layer for providers not already trusted - so entering a subshell composes its command surface and help from declarations instead of hardcoded init code.
Acceptance: a provider package declaring the punk.subshell capability supplies at least a commandset binding (namespace + prefix) and a help-topic set for a named subshell, and entering that subshell composes them (prefixed commands callable, topics in 'help topics'/'i help') with quit/switch-away restoring the parent surface (no leakage); the punk.subshell punk::cap handler validates declarations (malformed declarations vetoed with a useful message); a subshell's commandset composition can equivalently be declared in stored config without a provider package (G-014 substrate), and the existing hardcoded 'dev' CLI composition keeps working unchanged; declarations for capabilities with no registered handler are discoverable (query or report - closing the silent punk.isbogus gap); punk::cap pkg_unregister leaves no stale handler state (the 'destroy api objects?' review resolved); punk::pluginmgr-based discovery/loading of a provider is either demonstrated end-to-end (including .tm module loading in the safe interp) or explicitly deferred with the remaining pluginmgr gaps recorded in the detail file; G-042's registry mechanism is consumed, not duplicated.
### G-044 [proposed] punk::args-driven interactive command completion and hinting in the repl (raw mode primary)
Scope: src/modules/punk/repl-999999.0a1.0.tm (editbuf/reader integration, provider seam), src/modules/punk/args-999999.0a1.0.tm + src/modules/punk/ns-999999.0a1.0.tm (introspection surfaces as consumed), src/modules/punk/console-999999.0a1.0.tm (rendering)
Detail: goals/G-044-repl-command-completion.md
Goal: the interactive repl (raw mode primary) offers command completion and hinting driven by punk::args documentation - the command word resolved through the same doc-lookup flow as 'i' (ensembles, subcommands, ensemble parameters) and the argument position through the definition (options, choices with parse-consistent matching, literals, form awareness per G-041) - via an activation scheme that preserves the editbuf's literal-tab support (not exclusively plain Tab), behind a per-subshell completion-provider seam so alternative-language subshells (e.g. an interactive xtal session) can replace, augment or cleanly disable the Tcl-centric completer.
Acceptance: in a raw-mode interactive session a documented trigger (recorded in the detail file; a literal tab remains enterable into the editbuf) presents completions for command words, ensemble subcommands (resolved via the punk::ns doc-lookup flow without executing candidate commands), option flags and choice values (matching consistent with parse semantics: prefix/deny/reserve, and aliases once G-040 lands); a hint display shows the synopsis of the form(s) matching the partial input (consumes G-041's candidacy API when available - until then all-forms or form-0 display with the limitation noted is acceptable); no per-keystroke terminal queries are added when no completion display is active (G-013 approach note); the completer is a provider interface with the punk::args-driven implementation as the default - a subshell can declare an alternative provider or none, proven at minimum by a subshell with completion disabled showing no Tcl-centric interference (the xtal minimum bar; full xtal completion out of scope but not precluded by the interface); line-mode behaviour is unchanged unless a documented subset is added; G-013's raw editor essentials are a prerequisite and this goal does not weaken G-013's acceptance.
### G-045 [proposed] punk::args definition authoring ergonomics: record continuation, @cmd unindented fields, constructed-definition normalization
Scope: src/modules/punk/args-999999.0a1.0.tm (record parsing in resolve, tstr interplay, arg_error @cmd rendering), src/tests/modules/punk/args/testsuites/ (rendering.test/defquoting.test as the safety net), src/modules/punk-999999.0a1.0.tm (::punk::helptopic::define_docs de-hacked as the consumer proof)
Detail: goals/G-045-punkargs-authoring-ergonomics.md
Goal: authoring punk::args definitions no longer requires backslash line-continuations or ad-hoc workarounds for multi-line records and constructed definitions - a parser-recognised record-continuation mechanism (candidate: an unquoted trailing -& token, with the detail file recording the collision analysis and the element-count disambiguation alternative), -unindentedfields honoured for @cmd fields, and constructed (string-built) definitions able to opt into the same whole-block indent normalization file-style definitions get - with the container quoting rules (braced=literal, quoted=Tcl backslash semantics, \$\{ escape) promoted from defquoting.test into the define documentation.
Acceptance: a definition using the chosen record-continuation mechanism parses identically to its backslash-continuation equivalent (existing definitions unchanged - continuation is additive), with the token's collision rules documented and an escape/rejection story for values that legitimately match it; @cmd -help/-summary honour -unindentedfields (the rendering.test GAP rendering_unindentedfields_cmd_help_GAP flips to aligned); a constructed definition can request whole-block normalization so embedded continuation indentation behaves as in file-style definitions (the rendering_constructed_def_indent_characterization expectations updated to the chosen semantics), and ::punk::helptopic::define_docs drops its manual pre-normalization to prove it; the quoting rules from defquoting.test appear in the punk::args::define -help documentation; the full punk::args suite (128 tests incl. the rendering invariants: nesting independence, relative-indent preservation) passes with GAP tests flipped, none weakened.
### G-046 [achieved 2026-07-10] punk::args deferred -help resolution (parse-time performance + reentrancy) and rendering/value-shape fixes
Scope: src/modules/punk/args-999999.0a1.0.tm (resolve/get_dict: display-field deferral, dynamic-cache subst path, prefix writeback, string renderer, cmdhelp-facing messages), src/modules/punk/ansi-999999.0a1.0.tm (mark_columns argdoc as the reentrancy/perf testbed), src/tests/modules/punk/args/testsuites/ (GAP tests flip; perf verification)
Detail: goals/G-046-punkargs-deferred-help-and-fixes.md
Goal: argument resolution no longer processes -help and other display-only fields - their tstr expansion is deferred to display time (separately cached, per the existing in-source review notes) - so first parse of heavily documented commands gets measurably faster and definitions whose -help calls punk::args-parsing commands (the punk::ansi::mark_columns class) neither loop nor stall; alongside, the mechanical defects pinned by the characterization suites are fixed: @dynamic double-substituted multiline values align at their insertion column, prefix-normalized choice values keep the same shape as exact input, the -return string renderer aligns cmd-help continuations under the first line, and the misleading goodargs parse-error prefix in 'i <cmd> <args>' output is fixed.
Acceptance: parsing/argument resolution provably skips -help expansion (a definition whose -help contains a ${[...]} that would error or record its invocation shows the substitution did NOT run during a parse-only path, only for help display); first parse of punk::ansi::mark_columns drops from ~4s to well under a second with 'i punk::ansi::mark_columns' still rendering the embedded example, and a -help that parses its OWN definition id resolves or errors cleanly rather than looping; first-parse timing improves for at least one other heavily documented command (recorded in the detail file); rendering_atdynamic_multiline_help_insertion_GAP flips to all-aligned; choicegroups_imap_prefix_listwrap_GAP flips to shape-identical (prefix input yields the same plain string as exact input); the -return string renderer's cmd-help continuations align under the first line with relative indents preserved (rendering_string_renderer_characterization updated); the 'Bad number of leading values...' prefix shown by goodargs parsing in 'i string is'-style output is reworded or suppressed for the usage-display path; full punk::args and punk::ns suites pass with no non-GAP expectations weakened.
### G-047 [proposed] Declared primary VCS in punkproject.toml with per-developer commit-target override
Scope: punkproject.toml (schema), punkproject.local.toml (new, uncommitted per-checkout override), root AGENTS.md (Commit Conventions (any VCS) section), .gitignore + .fossil-settings/ignore-glob (ignore rules for the local file, per the coexistence contract), src/project_layouts/ (layout template payload - default values and ignore seeding only; project.new validation is follow-on work)
Detail: goals/G-047-declared-primary-vcs.md
Goal: a `[workflow] vcs = "<system>"` field in punkproject.toml declares the team's primary upstream VCS - the authoritative interchange history and the default target for unqualified "commit"/"checkin" requests - while the same field in an uncommitted per-checkout punkproject.local.toml lets an individual developer redirect their own unqualified commit instructions to their preferred system (mixed git/fossil-preferring teams), resolution order local override > project field > filesystem detection; the declaration governs developer commit workflow only - punk internal machinery (punkcheck tracking of related projects, upstream infrastructure pull per G-027, central project discovery per G-016) remains standardized on fossil regardless of the declared field; punkshell itself carries `[workflow] vcs = "git"` and derived-project layout templates default to fossil.
Acceptance: punkshell's punkproject.toml contains `[workflow] vcs = "git"`, and root AGENTS.md "Commit Conventions (any VCS)" documents the resolution order (punkproject.local.toml field, then punkproject.toml field, then filesystem detection, with the existing prose as final fallback) as the source agents consult for unqualified commit/checkin requests; punkproject.local.toml is ignored by both VCS per the .fossil-settings coexistence contract (git check-ignore matches it, ignore-glob covers it, the contract's verification comparisons stay clean); a reader resolving the primary VCS anchors at the project root via the punk::repo::is_project_root marker and ignores any nested punkproject.toml/punkproject.local.toml `[workflow] vcs`; the fossil-machinery carve-out is recorded in root AGENTS.md alongside the field documentation (a git-primary ecosystem project still maintains its fossil repo for punkcheck/pull/discovery machinery), and the field stays advisory to G-016 discovery with detection as fallback; the mixed-team sync semantics are documented (team primary = authoritative interchange; a developer committing granularly to the secondary owns batching their work up to the primary); project version patch-bumped with a CHANGELOG entry for the schema addition; make.tcl/project.new validation of the field against detected VCS systems stays out of scope (follow-on work).
### G-048 [proposed] textblock::table: parse via punk::args with shared passthrough documentation for table constructor options
Scope: src/modules/textblock-999999.0a1.0.tm (textblock::table proc + PUNKARGS, textblock::class::table class - constructor, opts_table_defaults, methods)
Detail: goals/G-048-textblock-table-punkargs.md
Goal: textblock::table parses its arguments via punk::args::parse (replacing the unvalidated dict merge at L6397), with its PUNKARGS definition covering both table-wrapper-specific options (-return, -rows, -headers) and the constructor passthrough options - the latter sourced by referencing punk::args definition blocks authored inline on the textblock::class::table class methods (the pattern used for render_to_input_line and rendertest in punk::ansi::class::class_ansi: lappend PUNKARGS [list { @id ... }] immediately before the method, punk::args::parse $args withid "..." inside the method body), so the constructor itself parses via punk::args and its documented option set is the single source of truth that textblock::table's PUNKARGS references rather than a parallel hand-typed list - retiring the "more options available - argument definition is incomplete" caveat and closing the -return -choiceprefix documentation/parsing mismatch (item #8 from the punk::args -choices audit) as a side effect.
Acceptance: textblock::class::table's constructor carries an inline punk::args define block (lappend PUNKARGS, @id naming the class+method) and parses its args via punk::args::parse withid, replacing the current manual switch at L466; textblock::table's PUNKARGS references the constructor's documented options (via @id reference, a shared fragment, or a documented include mechanism - the chosen mechanism recorded in the detail file) so table's definition is complete without hand-duplicating the constructor's option list; an invalid -return value (e.g. -return tab) produces a punk::args usage error instead of silently passing through; valid inputs reach textblock::class::table new with behaviour parity to the current dict merge; the -choiceprefix 0 on -return is honoured by construction (punk::args::parse enforces it); the "NOTE: more options available - argument definition is incomplete" comment is removed; existing textblock test suites pass.
### G-049 [achieved 2026-07-10] punk::args parse-status data model with machine-parsable cmdhelp returns
Scope: src/modules/punk/args-999999.0a1.0.tm (arg_error, parse error dispatch, colour-scheme handling), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp), src/tests/modules/punk/args/testsuites/args/usagemarking.test, src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test
Detail: goals/G-049-punkargs-parse-status-model.md
Goal: the information behind usage-display argument marking (which supplied arguments validated, which argument failed and why, which scheme applies) exists as a documented parse-status structure produced from a parse attempt and consumed by both arg_error renderers, and punk::ns::cmdhelp can return it machine-parsably via -return dict - replacing the transient goodargs/badarg locals, the badarg gaps in non-choice validation failures, and the stateful shared colour-array scheme handling.
Acceptance: cmdhelp -return dict distinguishes an incomplete, a fully-valid and an invalid argument set via per-argument statuses (received/ok/bad + overall scheme/message/form) with the structure documented; the table and string renderers derive their marking from that same structure, with rendered output unchanged except where the pinned GAP tests flip: badarg marking covers type/allocation failures not just choice violations (cmdhelp_GAP_no_badarg_marking_for_failed_typed_value), an explicit -scheme is honoured on the parse-failure path (cmdhelp_GAP_explicit_scheme_ignored_on_failure), the failure message names the queried command instead of cmdhelp's internal parse source line (cmdhelp_GAP_errormsg_leaks_internal_source), and scheme rendering no longer depends on or mutates shared colour state - the documented -scheme choice value 'nocolour' takes effect and repeated renders of the same call are identical regardless of prior scheme renders (usagemarking_GAP_scheme_nocolour_renders_with_leftover_colours, usagemarking_GAP_dash_nocolour_leaks_into_shared_array, usagemarking_GAP_dash_nocolour_leak_affects_later_info_render); all non-GAP characterization tests in usagemarking.test and cmdhelp.test pass unchanged.
### G-050 [proposed] punk::ns::synopsis argument-validity marking and status-aware returns
Scope: src/modules/punk/ns-999999.0a1.0.tm (synopsis), src/modules/punk/args-999999.0a1.0.tm (synopsis renderer), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (synopsis pins)
Detail: goals/G-050-synopsis-validity-marking.md
Goal: synopsis ('s') marks supplied argument words for validity the way cmdhelp's usage display does (goodarg/badarg styles via the G-049 parse-status structure), offers the same status machine-parsably in its dict return, and replaces the curried-alias excess-args length arithmetic (the 's pse' REVIEW note) with a parse against the resolved definition.
Acceptance: 's <cmd> <valid args>' renders the supplied words marked as validated and 's <cmd> <invalid args>' marks the offending word, while output with no argument words supplied is unchanged (the synopsis_GAP_no_argument_validity_marking pin flips); the dict return carries per-argument status; the curried-alias substitution behaviour (synopsis_curried_alias_shows_braced_target pin) is resolved to a documented presentation derived from parsing rather than list-length arithmetic; existing synopsis output tests (args testsuite synopsis.test and test::punk::args) pass; depends on G-049 for the status structure; G-044's completion/hint display is a consumer of the same status API (cross-reference, not a dependency).
### G-051 [proposed] cmdinfo truthful cmdtype for doc-only pseudo-commands and space-form docid prefix parity
Scope: src/modules/punk/ns-999999.0a1.0.tm (cmdinfo, cmd_traverse), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test, src/tests/modules/punk/ns/testsuites/ns/cmdflow.test
Detail: goals/G-051-cmdinfo-pseudo-and-prefix.md
Goal: cmdinfo reports a distinct cmdtype (e.g 'doconly') when resolution lands on a punk::args id with no corresponding real command instead of today's 'notfound', and the space-delimited docid jump accepts the same word prefixes the parser accepts - via the shared punk::args::choiceword_match resolver, not a second matching rule - so 'i string is tr' documents what 'string is tr' actually executes.
Acceptance: the pinned GAP tests flip: cmdhelp_GAP_pseudo_command_cmdtype_notfound and cmdhelp_GAP_string_is_true_pseudo report the new cmdtype with the docid unchanged, cmdhelp_GAP_spaceform_docid_prefix_not_honoured and cmdhelp_GAP_string_is_prefix_not_honoured resolve the child docid from a prefix exactly when parse accepts that prefix (honouring -choiceprefix, -choiceprefixdenylist, -choiceprefixreservelist and -choicealiases per G-040 parity); consumers of cmdinfo's cmdtype (cmdhelp, synopsis, eg) handle the new value with no behaviour change for real commands; cmdflow.test and the non-GAP cmdhelp.test tests pass unchanged.
### G-052 [proposed] TclOO method-level autodef documentation
Scope: src/modules/punk/ns-999999.0a1.0.tm (generate_autodef oo branches, cmd_traverse), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test
Detail: goals/G-052-oo-method-autodef.md
Goal: an undocumented method on a tcl::oo object or class gets an (autodef) punk::args definition generated from its introspected parameter list (info object call + method definition arglists - the machinery generate_autodef already uses for its class summary), so 'i $obj <method> <args...>' shows method-level usage with good/bad argument marking instead of only the class summary with the method word highlighted - explicitly documented methods continue to win.
Acceptance: the cmdhelp_GAP_oo_undocumented_method_class_summary_only pin flips: cmdinfo on an undocumented method resolves a method-level (autodef) docid with the trailing words in args_remaining, and bogus trailing arguments render error-scheme output instead of an info-scheme class summary; documented-method behaviour (cmdhelp_oo_documented_method) is unchanged; instance methods, class-defined methods and mixin/superclass-inherited methods resolve (constructor/'new' signatures at minimum characterized, in-scope or explicitly deferred in the detail file); the "-choiceprefix 0 ... methods must be specified in full always? - review" question on the class-summary method choicelist is resolved and documented either way; cmdflow.test and cmdhelp.test pass.
### G-053 [proposed] punk::args range-valued -multiple: occurrence arity with strict duplicate handling
Scope: src/modules/punk/args-999999.0a1.0.tm (spec compiler, parse, arg_error/synopsis renderers), src/tests/modules/punk/args/testsuites/args/
Detail: goals/G-053-punkargs-multiple-ranges.md
Goal: -multiple accepts a {min max} occurrence range (mirroring -choicemultiple; max -1 unbounded) alongside the legacy booleans - so a definition can declare "at most once, repeat is an error" ({0 1}) or bounded repetition ({2 4}) instead of choosing between silent last-wins (0) and unbounded collection (1) - with boolean semantics preserved exactly, including the prepend-defaults/last-wins override idiom.
Acceptance: parse raises a usage-style arity error naming the argument for occurrences outside a declared range; boolean -multiple 0/1 behaviour is unchanged (full existing punk::args suite passes untouched); the -optional/range-min reconciliation rule is documented and enforced at define time; the usage table Multi column and synopsis reflect declared ranges; -multipleunique/-multipleuniqueset compose with max>1 ranges unchanged; characterization tests cover the new forms and the value-shape rule.
### G-054 [achieved 2026-07-11] tclcore moduledoc: runtime-harvested 'string is' class choices with cross-version behavioural parity pins
Scope: src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (+ tclcore-buildversion.txt), src/tests/modules/punk/args/testsuites/args/ (new parity test), TEMP_REFERENCE/tcl9 (read-only reference)
Goal: the ::tcl::string::is definition's class choices (and the generated per-class virtual docids) are harvested from the running interpreter at define time - static choicelabels applied to classes present, generic label for unrecognized classes, a version note for dict - so accept/reject parity with the running Tcl holds on 8.6 and 9.x without hand-maintained per-version lists.
Acceptance: parse/parse_status against ::tcl::string::is and its per-class virtual ids agrees with the real interpreter's error-vs-ok outcome for a pinned probe matrix (missing args, trailing flag-like str word, option/class unique-prefix acceptance and ambiguity rejection, unknown option/class, -failindex var consumption leaving no str, per-version class presence: dict, unicode) on Tcl 9.0.x and 8.6; the rendered choices show only classes the running interp accepts; the parity test derives expectations from the live interpreter (not version arithmetic) and passes under both; existing args/tclcore suites pass; tclcore buildversion bumped with changelog.
### G-055 [proposed] Agent-driven tclcore moduledoc regeneration workflow with behavioural parity verification
Scope: goals/G-055-tclcore-regen-workflow.md (workflow doc), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (+ buildversion), src/tests/modules/punk/args/testsuites/args/ (parity pins), TEMP_REFERENCE/tcl9 (read-only source input; source retrieval mechanism deferred to the buildsuites toml configs / G-005 era)
Detail: goals/G-055-tclcore-regen-workflow.md
Goal: refreshing punk::args::moduledoc::tclcore is a documented, repeatable agent-driven workflow taking a Tcl source tree + version as input - man-page/source text carried verbatim (rearranged into @cmd -help/@examples/choicelabels, not reimagined; exceptions: synopsis notation is translated into punkshell's own more generic synopsis syntax rather than copied from the source docs, and text may be re-folded/line-wrapped to keep help display-width friendly while punk::args lacks word wrapping), one adaptive package with version-conditional definitions where released behaviour differs (8.6 vs 9.x), each regenerated command verified by a real-vs-model behavioural probe before acceptance, and source-checkin provenance recorded - designed so the same workflow extends later to tkcore (which loads on 'package require tk' and relies heavily on shared documentation sections, e.g the (default)::punk::args::moduledoc::tkcore::tk_standardoptions id) and to moduledocs for other core.tcl-lang.org projects (e.g tcludp).
Acceptance: the workflow is documented in the detail file (inputs, verbatim-text fidelity policy, probe-verification gate, provenance recording, shared-section reuse guidance for the tkcore pattern); the whole ::string ensemble plus a small selection of other commands including the multi-form ::after have been regenerated or verified through the workflow against the reference Tcl 9 sources, with parity pins added and 8.6 released-behaviour differences honoured (G-054's harvest/version-conditional techniques); an initial scan across tclcore commands identifying constructs not adequately modelable in the current punk::args system is recorded in the detail file, with each gap flagged as a candidate goal rather than worked around silently; tkcore and other-project moduledocs remain out of scope beyond the workflow being demonstrably reusable for them.
### G-056 [proposed] punk::args display-time word wrapping for help content
Scope: src/modules/punk/args-999999.0a1.0.tm (arg_error table and string renderers, helpers), src/modules/textblock-999999.0a1.0.tm (only if cell/column-level wrap is the chosen mechanism), src/tests/modules/punk/args/testsuites/args/ (new wrapping characterization + existing rendering suites)
Detail: goals/G-056-punkargs-word-wrapping.md
Goal: punk::args help display (@cmd -help, argument -help, choicelabels) word-wraps over-width lines to the effective display width at render time - ANSI/grapheme aware, splitting long logical lines but never joining existing ones, so deliberately structured content (hand-folded blocks, choice tables, art choicelabels, indented continuations) is preserved by construction - letting definitions store unfolded verbatim text (retiring the G-055 re-folding exception for new work) while usage output stays terminal-width friendly.
Acceptance: a definition whose -help/choicelabel contains a single long unfolded line renders fully within the effective display width in both the table and -return string renderers (no truncation, no overflow, wrapped continuations indented to match the field's existing paramindent alignment); wrap-point calculation is ANSI-aware (SGR sequences measure zero width; styling in effect carries across the wrap) and grapheme/double-width aware to the same standard as existing punk::ansi/textblock width handling; existing hand-folded and structured help renders byte-identical (full existing args rendering/usagemarking/deferredhelp suites pass unchanged - never-join semantics verified by characterization tests); effective width derives from the terminal when available with the current -maxwidth 80 table default as fallback, and an explicit width option overrides; the chosen mechanism (punk::args-side pre-wrap of field text vs textblock table column wrap support) and its rationale are recorded in the detail file; G-055's folding exception is marked lapsed for new work once this ships.
### G-057 [proposed] Windows kit builds embed a configurable icon (twapi resource replacement, per-vfs override)
Scope: src/make.tcl (kit/zipkit wrap steps), src/runtime/punk1.ico (project default, existing), src/vfs/*.vfs (override placement convention), src/runtime/mapvfs.config (only if an explicit config element is the chosen override mechanism), TEMP_REFERENCE/tcl-sfe (read-only reference), helper proc location decided in the work (make.tcl inline vs punk::mix lib)
Detail: goals/G-057-kit-icon-embedding.md
Goal: Windows kit/zipkit builds produce executables carrying an embedded icon chosen at build time - defaulting to the project icon src/runtime/punk1.ico, overridable per kit by its .vfs folder - by replacing the icon resources in the built executable using the twapi-based mechanism demonstrated in tcl-sfe (TEMP_REFERENCE/tcl-sfe, by twapi author and Tcl core member Ashok P. Nadkarni): RT_ICON/RT_GROUP_ICON replacement via twapi resource-update APIs, applied so the appended vfs payload stays intact (icon the stub before appending, or sfe-style split/update/reattach).
Acceptance: a Windows `make.tcl project` build produces kit executables whose embedded icon resources are the project default punk1.ico, and a kit whose .vfs supplies an override icon gets that icon instead (verified by resource inspection, e.g twapi::extract_resources, not just Explorer eyeballing); the icon-replaced executables still boot to a working punk shell reading their vfs payload for the kit types we build (kit, zip, zipcat per mapvfs.config); runtimes under src/runtime are never modified - replacement applies to the built copies only; twapi unavailable or non-Windows platform skips the icon step with a notice and the build otherwise completes unchanged; rebuilds are idempotent (re-wrapping an already-iconed build copy converges, no resource accumulation); the override convention (filename/location in the kit's custom .vfs folder vs a mapvfs.config element) and the stub-vs-split ordering decision are recorded in the detail file with the tcl-sfe attribution.
### G-058 [achieved 2026-07-10] Boot honours statically-linked runtime packages (static baseline seeding + packagepreference static-awareness)
Scope: src/vfs/_config/punk_main.tcl (boot auto_path/tm path filtering), src/modules/punk/packagepreference-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm + src/modules/punk/repl/codethread-999999.0a1.0.tm (code interp / codethread bootstrap), src/modules/shellthread-999999.0a1.0.tm (punkshell-created worker threads) as applicable, src/tests/ (un-gated unit tests + constraint-gated shell/kit integration tests), punkbin artifact repo (separate git repo, local checkout c:/repo/jn/punkbin - pinned runtime additions)
Detail: goals/G-058-static-runtime-packages.md
Goal: punkshell running on a runtime with statically-linked/builtin packages (tclsfe-x64: Thread/twapi/sqlite3/tdbc; punkbin runtimes' builtin Thread/tcllibc/vfs/vlerq; the expected shape of future zig-built static runtimes, G-005) keeps those packages resolvable in every interp and thread punkshell fabricates: boot captures the static baseline (info loaded entries with empty filename, plus their provided versions) before replacing package search paths, code interps and punkshell-created threads are seeded with ifneeded scripts mapping each static package to 'load {} <pkg>', and punk::packagepreference resolves static-vs-bundled by a documented version-aware policy instead of blindly loading a bundled dll over an already-provided static package.
Acceptance: a punk91-style kit (tclsfe-x64 + punk9win.vfs, no thread dll in the vfs) boots to a working repl with no "can't find package Thread" - punk::console loads in the code interp, and package require Thread succeeds there and in a punkshell-created worker thread, resolving to the static version; in the same kit, twapi resolves per the documented policy (no repeat of the observed static-Twapi-masked-by-older-vfs-twapi-5.0.2 double load; a genuinely newer bundled copy remains reachable by that policy); dll-based kits (punk902z) boot and pass their existing shell test baseline unchanged, as does a plain tclsh dev launch; the seeding mechanism is generic - driven by the captured baseline, no runtime-specific package naming - and the boot-time static baseline is introspectable at the repl; the seeding/preference logic is covered by un-gated unit tests against simulated baselines (runnable under plain tclsh), while kit-boot integration tests are gated behind a capability-probed tcltest constraint (a built kit whose baseline shows static entries including Thread) that skips cleanly when no such kit is present; the runtimes used for verification (tclsfe-x64.exe at minimum) are added to the punkbin artifact repository under win32-x86_64 with sha1sums.txt updated, so the constraint is satisfiable on other machines via the existing runtime-retrieval path; the punk91 code-interp vfs/vfs::zip load failure is re-diagnosed after the fix and either resolved or recorded as a distinct issue/candidate goal.
### G-059 [achieved 2026-07-11] WSL detection and suitability probing for driving unix-side tests from Windows
Scope: src/tests/ (capability probe helpers + constraint-gated cases in existing suites, e.g the unix sh-payload execution test in modules/punk/mix/testsuites/scriptwrap/multishell.test and a runtime.bash behaviour test), src/tests/AGENTS.md (enablement notes)
Detail: goals/G-059-wsl-test-driving.md
Goal: test runs on a Windows dev machine can exercise selected unix-side behaviour through WSL when it is present AND suitable - a capability-probed tcltest constraint (not mere wsl.exe existence) verifies a launchable distro, required tools (bash, coreutils/sha1 tooling), and working one-way staging into the guest's NATIVE filesystem - with all guest-side execution happening on that native filesystem: per-test artifacts are staged into a WSL-native tempdir and results collected back, the Windows checkout is never operated on via the shared /mnt path (DrvFs is slow and cross-boundary stat differences make git re-hash its index and fossil see phantom changes), and any future full-suite mode uses a separate native clone rather than the shared working tree.
Acceptance: a documented probe helper yields a wsl_linux_available constraint whose checks are capability-based (distro launches and answers uname/tool probes; staging into a native tempdir works) and which cannot misfire on wsl.exe-present-but-unusable installs (no distro, WSL1 limitations, broken interop); the currently unix-gated multishell sh-payload execution test runs green via WSL on a suitable machine and still skips cleanly elsewhere; at least one runtime.bash behaviour test (active/use/run resolution against a fixture runtime folder) runs inside WSL - all such tests executing from a native-filesystem staging dir with the shared path used only for one-way copy-in/out; the Windows checkout's git and fossil state is untouched by a WSL-gated run (verifiable: git status/fossil changes identical before and after); suite results on a WSL-less machine are unchanged (skips, not failures); enablement/limitations and the staging pattern recorded in src/tests/AGENTS.md.
### G-060 [proposed] QEMU-based cross-platform test matrix (arm's-length integration, GPL-safe posture)
Scope: build/test orchestration config and scripts (location to be settled with the buildsuites toml work - see G-005/buildsuites direction), goals/G-060-qemu-test-matrix.md (workflow + license posture), src/tests/ (any guest-driving hooks)
Detail: goals/G-060-qemu-test-matrix.md
Goal: comprehensive cross-platform verification (linux variants, FreeBSD/NetBSD/OpenBSD, arm architectures) is achievable from a single dev machine by driving QEMU guests as strictly external tooling - punkshell invokes qemu as a separate process with declarative per-guest config, and nothing in punkshell or its artifact repos bundles, links against, derives from or otherwise couples to QEMU (GPLv2) - so the project's BSD licensing posture is unaffected and QEMU remains a swappable convenience: the guest-driving contract is push-based artifact staging and result collection (the same pattern G-059 establishes for WSL - guests never share a working tree with the host) and must be satisfiable by real hardware, WSL or another hypervisor.
Acceptance: a documented, repeatable workflow provisions at least one non-Windows QEMU guest (e.g FreeBSD x86_64) that fetches a punkbin runtime and runs the source-tree suite inside the guest, driven from the Windows dev machine with results collected back; the guest-driving interface is hypervisor-agnostic (documented contract; QEMU is one provider); the license posture is recorded in the detail file (external-process invocation only, no QEMU binaries or derived code committed to punkshell or punkbin, guest OS images not redistributed by the project - fetched/built per machine); suite runs on machines without QEMU are unaffected.
### G-061 [proposed] Pseudoconsole expect-alternative for interactive shell testing (ConPTY + unix pty)
Scope: test-harness support (location TBD during the work: src/tests/testsupport/ and/or a small punk module), src/tests/shell/ (capability-gated interactive suites), goals/G-061-pseudoconsole-expect.md
Detail: goals/G-061-pseudoconsole-expect.md
Goal: interactive punkshell behaviour is testable headlessly and byte-accurately by driving a real shell under a pseudoconsole - ConPTY on windows, pty on unix - through an expect-like harness (spawn, send literal keys and control/arrow sequences, await patterns in the rendered ANSI byte stream with timeouts, collect transcript), giving the goals/G-044-detail preserve-list behaviours (continuation hints, raw-mode colour staging, tab markers and space dots, multiline history navigation) their durable cross-platform verification tier - complementary to G-020 capture testing (which verifies what reaches the screen; this verifies the byte stream) and to the G-001 console-seam unit tier.
Acceptance: a capability-gated harness spawns a built punkshell under ConPTY on windows (and under a unix pty when run on unix or via the G-059 WSL staging pattern), sends keys including control/arrow sequences, and awaits expected patterns with timeouts and forced teardown on expiry (hang-proof per the shell-test conventions); at least three preserve-list behaviours are verified through it (e.g the in-proc `set x "{*}{"` continuation-hint sequence, raw-mode tab-marker rendering and deletion, up-arrow recall and edit of a multiline history entry); tests skip cleanly where no pseudoconsole is available; the harness API is platform-agnostic with per-OS backends; candidate synchronisation/communication mechanisms between harness and shell (including the inter-subshell beep-protocol idea) are evaluated and the chosen approach documented in the detail file.

45
goals/AGENTS.md

@ -2,40 +2,41 @@
## Purpose
Holds optional detail prose for goals in the root `GOALS.md` index that need more than a one-line summary to state properly. The index entry is canonical; a detail file only elaborates and never contradicts its index entry.
Holds the per-goal detail files for the root `GOALS.md` index. Every goal has a detail file: it carries the goal's canonical contract prose (`Goal:` and `Acceptance:`) plus any supporting detail (context, approach, decisions, verification records). The index entry in root `GOALS.md` stays summary-only (ID, status, title, Scope, detail pointer).
## Ownership
- Files here are owned by the goal authoring workflow described in root `GOALS.md`.
- Goals are user-owned: agents add or edit goals only at the user's request or with explicit approval, proposal-first for index-entry changes (see the root `GOALS.md` maintenance rules). Agents must not write goals on their own initiative - suggesting candidate goals for user approval is welcome and encouraged.
- Detail files may be updated without pre-approval when recording findings, decisions, or verification artifacts from user-directed work on the goal or its subject matter; report such updates in the completion summary.
- The root `GOALS.md` index entry is the source of truth. If index and detail disagree, the index wins; fix the detail.
- Goals are user-owned: agents add or edit goals only at the user's request or with explicit approval, proposal-first for any contract element (see the root `GOALS.md` maintenance rules). The contract spans both tiers: the index entry (title, status, Scope) and this folder's `Goal:`/`Acceptance:` lines. Agents must not write goals on their own initiative - suggesting candidate goals for user approval is welcome and encouraged.
- Non-contract content (Context, Approach, Alternatives, Notes, Progress, findings, decisions, verification artifacts) may be updated without pre-approval during user-directed work on the goal or its subject matter; report such updates in the completion summary.
- Canonicality split: a detail file is canonical for its `Goal:` and `Acceptance:`; the root index is canonical for ID, status, title and Scope. The `Status:`/`Scope:` lines here mirror the index - if they disagree, the index wins and the mirror here is fixed.
## Local Contracts
### When a detail file is warranted
### One detail file per goal
A goal earns a detail file when it has any of:
- Non-obvious rationale (rejected alternatives, constraints discovered, why it is not done the obvious way)
- Multi-phase plan with sub-acceptance criteria
- External references or prior art worth citing
- More than ~3 lines of prose to state properly
Simple goals with a clear one-liner and measurable acceptance stay index-only. Do not create empty detail files for completeness.
Every entry in root `GOALS.md` has exactly one detail file here. A minimal detail file is just the header block below - do not pad it with empty sections; add Context/Approach/etc. only when there is real content.
### Naming
`G-<id>-<slug>.md` — e.g. `G-007-bytecompiler.md`. The `<id>` is the stable reference (taken from the `G-<id>` in the root index); the slug is human-readable and may change without breaking links as long as the ID prefix is preserved. Sortable by `ls goals/`.
`G-<id>-<slug>.md` — e.g. `G-013-raw-mode-default.md`. The `<id>` is the stable reference (taken from the `G-<id>` in the root index); the slug is human-readable and may change without breaking links as long as the ID prefix is preserved. Sortable by `ls goals/`.
### Detail file structure
### Detail file structure (suggested)
Required header (the first lines of every detail file):
```
# G-<id> <short title>
Status: <as in index>
Scope: <as in index>
Acceptance: <as in index>
Goal: <canonical - outcome-focused statement of what done looks like>
Acceptance: <canonical measurable, verifiable pass/fail criterion>
```
Optional sections after the header, only when they have real content:
```
## Context
<why this goal exists, what problem it solves>
@ -47,14 +48,20 @@ Acceptance: <as in index>
- <alt B> — deferred, see G-NNN
## Notes
<implementation notes, references, links>
<implementation notes, references, links, findings, verification records>
## Progress
<active goals worked incrementally: what landed (dated), what remains for acceptance>
```
### Progress tracking (active goals)
When user-directed work on an `active` goal lands without satisfying its `Acceptance:`, record it in the detail file's `## Progress` section: what landed (dated) and what remains for acceptance. Progress is non-contract content — updatable without pre-approval, reported in the completion summary. The achieved flip requires the remaining-work list to be resolved (empty, or each item verified satisfied); a partial increment never flips a goal. Increment numbering in commit messages is free-form and carries no contract meaning.
### Archive
- `goals/archive/` holds detail files for goals that have been achieved and moved to `GOALS-archive.md`.
- On archive: move `goals/G-<id>-<slug>.md``goals/archive/G-<id>-<slug>.md`. Do not rename the ID prefix.
- No orphan detail files: every file under `goals/` (excluding `archive/` and this `AGENTS.md`) must correspond to an `active` or `proposed` entry in the root `GOALS.md` index.
- `goals/archive/` holds detail files for achieved goals. Archiving happens as part of the achieved flip (see the root `GOALS.md` maintenance rules): the detail file moves `goals/G-<id>-<slug>.md``goals/archive/G-<id>-<slug>.md` and the index entry moves to `GOALS-archive.md`. Do not rename the ID prefix.
- No orphan detail files: every file here (excluding `archive/` and this `AGENTS.md`) corresponds to a `proposed`, `active`, `abandoned` or `superseded` entry in root `GOALS.md`; every file under `archive/` corresponds to a record in `GOALS-archive.md`.
## Work Guidance

3
goals/G-001-pluggable-console-backends.md

@ -1,7 +1,8 @@
# G-001 Pluggable console backends for non-detectable terminals
Status: proposed
Status: active
Scope: src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/lib/app-punkshell/punkshell.tcl
Goal: an interactive REPL can be launched against a non-detectable terminal-like device (ssh channel, tk text widget) via an ::opunk::Console subclass, with no edits to the base class or punk::console.
Acceptance: a subshell started with an ssh-channel-backed and a tk-widget-backed ::opunk::Console subclass runs an interactive REPL that reads/writes through that console; size, at_eof, and can_respond are answered by the subclass overrides; the base ::opunk::Console and punk::console module are unchanged.
## Context

1
goals/G-002-non-nested-subshell.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Goal: a subshell can target a named console (default or non-default) and run without blocking the parent, replacing the synchronous nested interp-eval model.
Acceptance: a parent REPL launches a subshell against a named console and continues processing its own input while the subshell runs; the parent can signal/query the running subshell; thread::send -async dispatched from within the subshell's code interp arrives at that interp (so packages like promise work when thread features aren't disabled); the "first subshell asymmetry" TODO at repl-999999.0a1.0.tm:3130 is resolved; existing synchronous `subshell punk`/`safe`/`safebase`/`punksafe` behaviour is preserved as a default mode.
## Context

1
goals/G-003-subshell-resource-limits.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Goal: a subshell's code interp can be launched with configurable resource limits (command-count, time) and sandboxing features, building on the resolved first-subshell asymmetry from G-002.
Acceptance: a subshell can be launched with at least one resource limit (command-count via `interp limit -command`, or time via `interp limit -time`) and one sandboxing feature (e.g. `interp hide` of a command, or full safe-interp restrictions) applied to its code interp, enforceable regardless of subshell nesting depth; a subshell can be configured anywhere on the spectrum from unrestricted to fully safe via expose/hide of commands; the existing default subshell behaviour (no limits, no extra sandbox beyond the existing safe/safebase/punksafe types) is preserved when no limits are configured.
## Context

1
goals/G-004-no-committed-binaries.md

@ -2,6 +2,7 @@
Status: proposed
Scope: repo-wide (bin/, src/vfs/, src/vendorlib/, src/vendormodules/, src/bootsupport/)
Goal: the committed repository contains no executable binaries; zip-based .tm modules are allowed but not if they embed executables.
Acceptance: a scan of the committed tree finds no executable binaries (shared libs, .exe, native .so/.dll/.dylib, bare ELF/Mach-O); any zip-based .tm modules present contain no embedded executables; the binary artifacts previously committed are retrievable via G-005 (build from source) or G-006 (pre-built download) so their removal does not break builds.
## Context

1
goals/G-005-zig-build-infrastructure.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/runtime/, build.zig / build.zig.zon (new), src/make.tcl integration
Goal: a zig-based build system retrieves and builds binary dependencies (including Tcl9) from source, replacing the committed-binary approach for the vendored/native components.
Acceptance: running the zig build produces the binary artifacts the repo previously committed (at minimum: Tcl9 library for one target platform); existing Tcl9-zig experiments brought into the project; `tclsh src/make.tcl` integrates with the zig build so a normal project build retrieves/builds binaries via zig when not present; no binary artifacts need to be committed for the build to succeed on a clean checkout with the zig toolchain available.
## Context

1
goals/G-006-prebuilt-artifact-download.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/runtime/, src/make.tcl integration, user-config (consent flags)
Goal: pre-built binary artifacts can be downloaded from a separate related binary-artifacts repository or user-configured sources, gated by explicit user consent/configuration by default.
Acceptance: a download mechanism fetches binary artifacts (the same set the zig build produces) from a configured source on demand; by default the download is gated behind explicit user consent (a config flag or interactive prompt) and does not occur silently; a user-configured source URL overrides the default binary-artifacts repo; downloaded artifacts satisfy the same build requirements as zig-built artifacts so `tclsh src/make.tcl project` succeeds with downloaded artifacts in place of built ones.
## Context

1
goals/G-008-scoped-console-state.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Goal: a subshell on the shared console can opt into scoped console state - its terminal mutations (tabstops, modes, cursor style, palette, title) are captured as an activatable per-subshell state set and the prior state is re-established on quit or switch-away, with today's shared/persistent behaviour remaining the default.
Acceptance: with a scoped subshell: tabstops, at least one DEC/ANSI mode and at least one palette entry changed inside the subshell are restored for the parent on quit (verified by terminal query where queryable) and the facts store matches the terminal; the default (unscoped) launch behaviour is unchanged; activation is set-based, proven by applying state set A, activating set B, then re-activating A and observing A's state; irreversible outputs (cleared screen/scrollback, emitted text) are documented as out of scope.
## Context

1
goals/G-009-themed-subshell-profiles.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/poshinfo-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/island-999999.0a1.0.tm
Goal: a subshell can be launched from a named profile binding a poshinfo-enumerated scheme to behavioural aspects (e.g. G-003 interp limits/hidden commands, punk::island filesystem access) so that restricted subshells are visually distinct, with the theme's terminal effects and associated profile data riding the G-008 state set.
Acceptance: a named profile associating a poshinfo scheme with at least one restriction aspect (an interp limit, a hidden command, or island-restricted filesystem access) launches by name; the scheme's visual state applies on entry (at minimum prompt styling plus one underlying terminal aspect such as a palette change) and is removed/restored on quit via the G-008 mechanism; profile-associated non-terminal data is scoped to the subshell's lifetime; an unthemed launch is unchanged.
## Context

1
goals/G-010-subshell-tree-navigation.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Goal: subshells form a navigable tree - a subshell can be suspended rather than quit, listed, resumed, and the console switched to any live subshell in the tree (e.g. grandchild to grandparent, or across branches) with each subshell's console state re-applied via its G-008 state set, building on the non-nested subshell model of G-002.
Acceptance: from a grandchild subshell a single switch command reaches the grandparent without unwinding through the intermediate parent; switching between subshells on different branches preserves each subshell's session state and re-applies its console state set on activation; suspended subshells can be listed and resumed; `quit` still unwinds to the launching parent as today; a subshell whose switch commands are hidden/restricted cannot initiate switches.
## Context

1
goals/G-011-console-stderr-semantics.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm
Goal: a console optionally carries an err channel as an attribute of its canonical {in out} identity - {in out err} specs accepted everywhere -console is, err resolving to process stderr for the default console and to the console's out channel elsewhere - so diagnostics and emit-to-err are first-class per-console operations instead of raw puts stderr.
Acceptance: console_spec_resolve and every -console site accept an {in out err} spec (err optional; existing pair/instance-name/object spec forms unchanged); opunk::Console exposes the err channel (nullable, additive base-class change); an unset err resolves to stderr for the default console and to the console's own out channel otherwise; punk::console's own warnings/diagnostics emitted while operating on a resolvable console go to that console's err (raw puts stderr remains only where no console is in play); an emit-to-err path exists and is exercised by at least one real consumer (e.g punk::repl); the effective err is discoverable from any thread/interp via console_fact_get (fact key err, returning the effective err channel name); ownership/fact/mode-cache keys remain canonical {in out}; the existing console test suites pass unchanged.
## Context

1
goals/G-012-template-payload-safety.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/project_layouts/, src/make.tcl, src/modules/punk/mix/ (layout instantiation), fauxlink module (bootsupport 0.1.1 - promoted if chosen as mechanism)
Goal: project layouts carry no live nested VCS-config files - template .gitignore payloads are stored inert (renamed, or fauxlink-encoded) and materialized at project generation - and src/make.tcl has an explicit punkcheck-tracked step that refreshes layout payloads from their canonical sources.
Acceptance: a scan of src/project_layouts finds no file named .gitignore, and git check-ignore --no-index over every layout file matches only root-.gitignore rules (nested rules provably inert); a project generated from each affected layout receives a working .gitignore whose content matches the canonical payload/target; after editing a canonical source (e.g. root .gitignore), the make.tcl template-refresh step updates the derived layout payloads (punkcheck-tracked), covering vendor/punk layouts as well as custom/_project; the previously hidden template files (layout READMEs, vendored TODO-class files) remain git-tracked without per-file force-add exceptions.
## Context

3
goals/G-013-raw-mode-default.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/console-999999.0a1.0.tm
Acceptance: as in root GOALS.md index (canonical).
Goal: a repl launched without explicit mode configuration starts in raw input mode with a clean display - every per-keystroke debug emission behind its own repl-operable toggle - and raw editing covers the line-mode essentials (history navigation, cursor movement).
Acceptance: a default launch lands in raw mode with line mode still selectable; with debug toggles at their defaults (off unless stored configuration via ::punk::config says otherwise - see G-014), typing/editing/submitting a command emits no cursor-positioned debug output; the per-keystroke add_chunk frame and the right-hand live editbuf view are gated separately, each toggleable from within a running repl; arrow-key history navigation and left/right cursor movement work in raw mode (current stubs replaced); the marked-line debugrepl output form is retained and works on terminals without cursor addressing (e.g. vt52); the debugrepl first-word activation mechanism is reviewed and the keep/replace outcome (e.g. a proper Tcl command that interp hide can restrict in subshells) is recorded in the detail file; on tcl 8.6 a background-initiated terminal query at an idle raw-mode prompt succeeds (the residue scenario fail-fast-guarded in punk::console 0.7.1).
## Context

3
goals/G-014-punk-config-toml.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/config-0.1.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm, src/vendormodules/tomlish-*.tm (vendored; canonical source in the external tomlish project space)
Acceptance: as in root GOALS.md index (canonical).
Goal: ::punk::config loads stored configuration from toml files in the XDG-located config dir - parsed via the vendored tomlish module, never an ad-hoc parser - and consumers resolve settings with per-named-subshell overrides, so features like the G-013 debug-view startup defaults read declared user configuration instead of hardcoded fallbacks.
Acceptance: a setting declared in a toml file under the XDG-located config dir is visible through the punk::config API at repl startup, and with no config files present built-in defaults apply with no errors beyond the existing missing-dir notice; a named subshell resolves its own overriding value for a key also defined at the parent/default scope, and a subshell with no override inherits the outer value (proven with at least one real key); at least one shipped feature (the G-013 editbuf-view startup default is the natural first) reads its default through this path rather than a hardcoded value; all toml reading/writing in punk::config goes through the tomlish module, and the tomlish API procs punk::config consumes carry punk::args (PUNKARGS) documentation - added upstream in the tomlish project and re-vendored here before punk::config implementation proceeds.
## Context

3
goals/G-016-projects-work-git-discovery.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/mix/commandset/project-999999.0a1.0.tm, src/modules/punk/repo-999999.0a1.0.tm
Acceptance: as in root GOALS.md index (canonical).
Goal: `dev projects.work <glob>` lists git-based project checkouts as well as fossil-based ones, each result identifying its VCS - fossil discovery stays central-config-db based, and git discovery uses a defined enumeration source (git has no central registry; the chosen mechanism is recorded in the detail file); output gains a `-return table|dict|json` option (default table, unchanged): dict is the canonical machine form (per-checkout records incl the VCS field, documented in the argdoc), json a faithful rendering of it for out-of-process consumers.
Acceptance: with a git-only project on disk registered in the chosen enumeration source and matching the glob, `projects.work` lists its working directory and identifies it as git; existing fossil results are unchanged apart from any added VCS-identifying column; a project that is both git and fossil (e.g. this repo) appears with both indicated rather than duplicated; glob matching remains case-insensitive; `-return dict` yields the documented record structure containing everything the table shows (incl VCS identification), `-return json` round-trips the same data, and the default table output is unchanged for existing users.
## Context

6
goals/G-017-agent-project-discovery.md

@ -0,0 +1,6 @@
# G-017 Agents locate local projects via piped `projects.work` calls, not filesystem scanning
Status: proposed
Scope: AGENTS.md (root) or a child doc it indexes (guidance content only - no code)
Goal: once G-015 makes piped script calls reliable, repository guidance directs agents asked to locate another local project to query it via a piped `projects.work` call to a punk executable instead of grepping/globbing the wider filesystem.
Acceptance: root AGENTS.md (or a child doc indexed from it) records the exact recommended invocation - executable, subcommand, glob usage, and `-return json` as the parse target with its record fields - and states when filesystem scanning remains appropriate (projects not registered in any discovery source); the guidance is added only after G-015 is achieved (and notes the fossil-only limitation until G-016); following the documented pattern, an agent locates a named sibling project's checkout dir with a single piped call.

3
goals/G-018-zig-plain-tclsh-kits.md

@ -2,7 +2,8 @@
Status: proposed
Scope: build.zig / build.zig.zon (per G-005), src/runtime/, src/make.tcl integration
Acceptance: as in root GOALS.md index (canonical).
Goal: developers can use the G-005 zig build system to produce self-contained zip-based tclsh executables that carry no punk-specific infrastructure (no punk boot layer, punk modules, or punk apps) - plain tclsh kits usable independently of the punkshell product.
Acceptance: a documented zig invocation on a clean checkout (zig toolchain available) produces a zip-based tclsh executable for at least one target platform that runs conventional tclsh invocations (`<exe> script.tcl args`, piped stdin) on a machine with no Tcl installation; a listing of the kit's mounted/zip contents shows stock Tcl (plus any declared stock runtime deps) and no punk namespaces, punk boot files, or punkshell apps; the punk-flavoured executables remain producible alongside.
## Context

3
goals/G-019-dependency-scan-module-trimming.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/make.tcl, src/modules/punk/lib-999999.0a1.0.tm (tclparser use), src/vfs/ (kit assembly), scanning module (new or existing punk module - to be determined)
Acceptance: as in root GOALS.md index (canonical).
Goal: a package-dependency scan from an executable's entrypoint (candidate basis: the tclparser parse API - currently satisfied only by the c-only tclparser library, with punk::lib's pure-Tcl fallback an unimplemented stub) determines the module closure the executable actually requires, so a build can ship only those modules - while 'batteries included' builds remain a supported alternative, not a casualty.
Acceptance: for at least one punk-based executable target, the build can run a dependency scan from its entrypoint producing the closure of required packages/modules plus a mechanism to declare dynamically-loaded extras the scan cannot see; a trimmed kit assembled from that closure starts and passes its basic function check (e.g. repl launch or the app's smoke test) with no missing-package errors; the trimmed kit's module listing is a strict subset of the batteries-included equivalent (demonstrating real exclusion); batteries-included builds remain producible unchanged.
## Context

3
goals/G-020-screencap-input-module.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/ (new module - name TBD), src/vfs/punk9win.vfs/lib_tcl9/ (existing treectrl/Img/twapi payloads); scriptlib/aloupe.tcl stays untouched as a standalone app
Acceptance: as in root GOALS.md index (canonical).
Goal: a punk module drives screen/window capture and mouse/keyboard injection from scripts via per-platform backends - Windows (treectrl loupe capture + twapi input/window-location) is the initial complete target, with the backend contract designed so X11 (Linux/FreeBSD) and macOS backends can be added without changing callers, and Wayland-native sessions explicitly out of scope.
Acceptance: on Windows from a punk shell or script: a screen region and a window located by title/class pattern are each captured to a Tk photo and written as a valid PNG; mouse movement/click and key events injected into a located test window produce their observable effect (typed text arrives, click acts); window location returns the handle and geometry for a pattern; a capability-introspection call reports per-feature support and an unsupported platform/backend yields a clean capability-based refusal, not a crash; the backend interface (capture / input / window-locate) is documented well enough that a non-Windows backend can be added without modifying callers; the aloupe script remains functional and unmodified.
## Context

3
goals/G-021-agent-visual-verification.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/ (G-020 module's agent-facing surface), AGENTS.md guidance (post G-015 pattern), src/tests/ (visual-verification test hooks)
Acceptance: as in root GOALS.md index (canonical).
Goal: a tool-calling agent can, during a session, use piped script calls (G-015) to a punk executable to locate the applicable UI window, snapshot it to a PNG file and/or base64 output suitable for AI image analysis, and drive mouse/keyboard interactions - enabling tests whose verification is visual-only and/or input-driven.
Acceptance: on Windows, single piped script calls (no interactive session) can: list/match windows for a pattern with machine-parseable output; save a located window's snapshot to a caller-specified path and optionally emit it base64 on stdout; run a scripted interaction sequence (focus, click at offset, type text, snapshot) end-to-end; failures exit nonzero with the error on stderr per G-015 semantics; the invocation patterns are documented for agents alongside the G-017 guidance; at least one real visual-or-input-driven verification (e.g. a Tk app smoke test) is exercised through this path.
## Context

3
goals/G-022-fossil-rename-punkshell.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/mix/commandset/repo-999999.0a1.0.tm, src/modules/punk/repo-999999.0a1.0.tm, src/tests/modules/punk/mix/testsuites/repo/
Acceptance: as in root GOALS.md index (canonical).
Goal: the `dev repo` commandset can move and rename fossil repositories non-interactively and safely - all checkouts repointed, no phantom central config-db entries, no dangling old repo db, fossil project-name renamable with project-code unchanged - and this project's fossil repo (currently project-name 'shellspy') is renamed to 'punkshell' through that mechanism via a G-015 piped script call, not by hand.
Acceptance: the commandset provides a flag-driven (no stdin prompts) move/rename operation which on a scratch repo with an open checkout: repoints every registered checkout, leaves the central config-db listing only the new path, removes or archives the old repo db file (per option), clears stale ckout: back-references, and applies a requested project-name change while preserving project-code; the GAP characterization tests in src/tests/modules/punk/mix/testsuites/repo/fossilmove.test are updated to assert the clean behaviour and pass; after G-015 is achieved, this repo's fossil db (shellspy.fossil / project-name shellspy) is renamed to punkshell via the new operation invoked through a piped `script` call, with `fossil info` in this checkout showing the new repository path and project-name and `fossil all ls` free of the old path.
## Context

3
goals/G-023-version-named-binaries.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/make.tcl, src/runtime/ (mapping config - see G-024), bin/ (build outputs)
Acceptance: as in root GOALS.md index (canonical).
Goal: project builds produce version-named punk executables for tcl 8.6 and tcl 9 as the project version advances - punk8-<major>-<minor>-<patch>.exe / punk9-<major>-<minor>-<patch>.exe per version, punk8-dev.exe / punk9-dev.exe tracking the latest build, and plain punk8.exe / punk9.exe created initially then replaced only when an actual release is tagged - tolerating the disk growth for now.
Acceptance: a project build at the current punkproject.toml version produces punk8-<M>-<m>-<p>.exe and punk9-<M>-<m>-<p>.exe (names derived from the version, not hand-maintained) plus punk8-dev.exe / punk9-dev.exe updated to that same build; rebuilding at an unchanged version refreshes that version's binaries and -dev without touching other versions' outputs; plain punk8.exe / punk9.exe exist and are replaced only by an explicit release step - a normal build never overwrites them; the scheme is declared succinctly via the G-024 toml mapping (no per-version config edits); archival/deletion of accumulated versioned binaries is out of scope with the trigger question recorded in the detail file.
## Context

3
goals/G-024-mapvfs-toml.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/runtime/mapvfs.config (replaced/deprecated), src/make.tcl (parsing), src/bootsupport/modules/tomlish-*.tm (parser dependency)
Acceptance: as in root GOALS.md index (canonical).
Goal: the runtime-to-vfs-to-executable build mapping moves from the custom line format of src/runtime/mapvfs.config to a toml file parsed with the tomlish package - still supporting explicit per-executable mappings (runtime, vfs folder, output name, kit type) while also expressing generative schemes like the G-023 versioned naming in a single succinct declaration.
Acceptance: a mapvfs toml file parsed via tomlish (no ad-hoc toml parsing) drives the build: every mapping currently active in mapvfs.config is expressible and at least one existing target builds identically from the toml; the G-023 versioned/dev/release-gated output scheme is declared in one entry that expands to its outputs without enumerating versions; malformed or unresolvable entries fail the build with a clear message naming the entry; the legacy .config format is either fully migrated (old parser removed) or explicitly deprecated with documented precedence between the two files.
## Context

3
goals/G-025-exe-selfreport.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/vfs/_config/punk_main.tcl (subcommand dispatch), src/make.tcl (stamping build info into the vfs), src/vfs/ (stamp payload location), src/modules/punk/ (in-shell command - the single implementation)
Acceptance: as in root GOALS.md index (canonical).
Goal: a punk executable reports its identity from embedded data rather than its filename - a documented subcommand prints the punkproject.toml project version it was built from plus the input runtime binary name and vfs folder name used to assemble it - with the same-named command available in the punk module so scripts running in any punk shell (including tclsh-hosted ones like `tclsh src/make.tcl shell`) get the same report in-process without exec, stamp fields reported as absent rather than fabricated when there is no stamp.
Acceptance: the build stamps project version, runtime binary name, and vfs folder name into the kit; the built executable invoked with the version-report subcommand prints those fields machine-parseably on stdout and exits 0 with no other output (G-015-compatible; no repl fallthrough); a same-named command in the punk module returns the same fields in-process (subcommand implemented as a wrapper over it - one implementation) and works from the code interp; the report distinguishes stamped provenance from live facts: a stamped kit reports its stamp, a `src`-mode or source-tree session additionally reports the live punkproject.toml version as a distinct field when it differs, and unstamped contexts (`tclsh src/make.tcl shell`, plain tclsh with punk modules) report stamp fields explicitly absent with live runtime facts (actual `info nameofexecutable`) still provided; the report is correct when the executable file has been renamed or copied; executables built before stamping existed fail gracefully with a clear message rather than fabricating values.
## Context

3
goals/G-026-vendor-provenance-policy.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/make.tcl (vendorupdate and bootsupport steps), src/vendormodules/include_modules.config, src/bootsupport/modules*/include_modules.config
Acceptance: as in root GOALS.md index (canonical).
Goal: pulling vendored or bootsupport artifacts from local source projects enforces committed provenance - the warn-only dirty-checkout check added to vendorupdate in project 0.2.5 becomes a policy that can abort with an explicit override, covers the bootsupport update path as well, and the residual staleness question (built modules that predate or postdate the committed source even in a clean checkout) has a recorded design decision.
Acceptance: vendorupdate and the bootsupport update refuse to pull from a source project whose fossil/git checkout is dirty unless an explicit documented override is given (warn-only selectable as a configured mode); the check reports each VCS root once per run and does not fire for unversioned source locations; bootsupport_localupdate is covered by the same shared check (no second divergent implementation); the staleness gap - a clean checkout whose built modules/ artifacts do not correspond to the committed source - is either detected (mechanism chosen and implemented) or explicitly recorded in the detail file as accepted risk with the considered mechanisms; behaviour is exercised by a test or documented manual verification against a scratch dirty checkout.
## Context

3
goals/G-027-derived-project-pull-updates.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/mix/commandset/project-999999.0a1.0.tm (project.new push path), src/modules/punkcheck-999999.0a1.0.tm (install provenance records), src/make.tcl (derived-project pull entrypoint)
Acceptance: as in root GOALS.md index (canonical).
Goal: a project generated from a punkshell layout can pull infrastructure updates (make.tcl/build.tcl, bootsupport modules and libs, layout template payloads) from its originating punkshell project by running one command inside the derived project - replacing the current push model (`dev project.new -force 1 -update 1` run from punkshell) - with install provenance robust to derived-project workdir moves (not local relative paths alone), VCS-state awareness on both ends, and a recorded decision on pulling from remote sources.
Acceptance: one documented command run inside a derived project updates its punkshell-derived infrastructure from the source punkshell project; the update still works after the derived project's working directory has been moved (proven by moving a scratch derived project and pulling); VCS integration on both ends: the pull applies the G-026 clean-checkout policy to the punkshell source, and reports (or refuses per option) when target files it would overwrite carry uncommitted local modifications in the derived project's git/fossil checkout; .punkcheck records remain the provenance basis (updates are recorded and unchanged targets skipped, as with existing punkcheck-tracked installs); the push flow keeps working until explicitly retired; the remote-pull question (updating from a remote punkshell repository rather than a local checkout) has a recorded design decision - implemented, or deferred with rationale in the detail file.
## Context

3
goals/G-028-file-locker-identification.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punkboot/utils-999999.0a1.0.tm (locker-report helper), src/make.tcl (kit deploy failure reporting)
Acceptance: as in root GOALS.md index (canonical).
Goal: when a build cannot delete/overwrite a target file (typically a built executable held open by another program), the failure message names the locking process(es) - via a punkboot::utils helper using the Windows Restart Manager API through optionally-available twapi or cffi, degrading cleanly to the current message when the API or bindings are unavailable or on other platforms.
Acceptance: on Windows with twapi or cffi loadable, a punkboot::utils proc given a file path returns the locking processes (at least pid and process name; empty list when unlocked); punkboot::utils itself stays pure Tcl - the binary binding is required lazily at call time and its absence yields a clean 'unavailable' result, not an error (bootsupport must not gain a compiled-extension dependency); make.tcl kit deploy failures include the locker report when determinable (e.g. "could not delete target binary ... in use by: 7zFM.exe (pid 1234)"); non-Windows platforms and binding-less environments produce the existing message unchanged; verified against a deliberately held handle (documented manual verification acceptable).
## Context

3
goals/G-029-testmodules-from-srctests.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/make.tcl (test-module packaging step), src/tests/ (source of truth), src/modules/test/ (generated #modpod targets), src/modules/punk/mix/ (modpod tooling as needed)
Acceptance: as in root GOALS.md index (canonical).
Goal: src/tests is the single source of truth for module test suites - a punkcheck-tracked make.tcl step generates the packaged test::<modulename> #modpod modules from src/tests/modules/<namespacepath>/testsuites content, ending hand-maintenance of parallel copies under src/modules/test/; the packaged form is a distributable in its own right - a user who downloads a built module can optionally download the matching test::<modulename> and verify the module's behaviour on their own system (package require + RUN, or an executable's -app test) with no source tree or test harness required.
Acceptance: a make.tcl step generates a test::<modulename> #modpod under src/modules/test/ from the corresponding src/tests testsuites (punkcheck-tracked, skipped when sources unchanged); the generated package works through the packaged path - loadable via package require test::<modulename> and runnable via its SUITE/RUN interface (e.g a built executable's -app test) - reporting the same test names and pass counts as running the same suites directly via src/tests/runtests.tcl; the consumer scenario is proven standalone: the generated package (plus the module under test and their dependencies) runs in an environment without the project source tree present; suite data files (e.g roundtrip toml files with deliberate crlf/mixed line endings) survive packaging byte-for-byte; documentation states src/tests is the source of truth and the generated modpods are build artifacts not to be hand-edited; proven end-to-end for at least one real module (tomlish, whose src/tests port and still-live modpod created the dual-copy situation, is the natural first).
## Context

3
goals/G-030-maketcl-punkargs.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/make.tcl (dispatch, help, prompts), src/bootsupport/AGENTS.md + src/modules/AGENTS.md (bootstrap-tracked staleness contract), src/modules/punk/args-999999.0a1.0.tm (only as consumed)
Acceptance: as in root GOALS.md index (canonical).
Goal: make.tcl - the first surface a developer sees - parses its subcommands and options via punk::args and showcases the tabled usage output for help and argument errors, every interactive y/n prompt gains a declared flag equivalent so agents can drive make.tcl with arguments instead of piped input, punk::args joins the bootstrap-tracked staleness set, and the boot phase plus the environment-repair commands keep working with degraded plain help when the bootsupport punk::args (or the table-rendering stack) is stale or unavailable.
Acceptance: `tclsh src/make.tcl` and `-help` render punk::args tabled usage listing every subcommand with a summary, and `make.tcl help <subcommand>` (or `<subcommand> -help`) shows that subcommand's definition; invalid arguments produce a punk::args usage error rather than ad-hoc messages; every y/n prompt has a documented flag equivalent (proven at least for vfscommonupdate and the project-build confirmations: a run with the flag completes non-interactively with stdin closed) and a non-interactive stdin without the flag fails fast with usage rather than hanging or half-aborting; punk::args is added to the bootstrap-tracked buildversion set with the doc contract updated (src/bootsupport/AGENTS.md, src/modules/AGENTS.md); with bootsupport punk::args unavailable or unloadable, make.tcl still boots and `check`, `bootsupport` and `modules` remain usable with plain-text fallback help (the guarded-require degrade rule); layout make.tcl copies follow via the established sync/G-027 channels (noted, not hand-synced).
## Context

3
goals/G-031-componentized-kit-boot.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/vfs/_config/ (punk_main.tcl, project_main.tcl restructure), src/vfs/_vfscommon.vfs (boot core delivery), src/project_layouts/ (thin-main skeleton, via established sync channels)
Acceptance: as in root GOALS.md index (canonical).
Goal: the per-project vfs main script becomes a thin project-owned file - declare the application's subcommands and launch defaults at clearly commented customization points, then hand over to a shared layout-owned boot core (vfs mounts, package modes and paths, libunknown, src-mode modpod registration) and default dispatch pulled in from within the kit - so project developers add app-specific subcommands without wading through or forking ~1000 lines of boot boilerplate, and boot improvements reach derived projects as pull-updatable payload instead of dying in vintage forks (tomlish_main.tcl: ~20 custom lines carrying a stale 500-line 2025 copy of the rest).
Acceptance: punkshell's own kits boot through a thin main plus shared boot core with behaviour parity - package modes including src mode, existing tclsh/shellspy/punk/shell/script dispatch semantics, and supported vfs types (zipfs/metakit/cookfs) all unchanged; a project-specific subcommand is added by editing only the thin main at a commented customization point (proven end-to-end in a derived project - tomlish replacing its forked main is the natural first); the boot core ships as layout-owned payload (via _vfscommon/layout channels) and the thin main as a project-owned skeleton, per the G-027 ownership classification; the boot core is versioned/identifiable so a kit can report which boot-core vintage it carries (ties to G-025 stamping).
## Context

3
goals/G-032-launcher-punkargs.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/vfs/_config/ (default dispatch), src/lib/app-punkshell and sibling app packages as touched
Acceptance: as in root GOALS.md index (canonical).
Goal: the default launch dispatch defines its subcommands via punk::args - `<punkexe> -help` and argument errors render the tabled usage enumerating built-in and project-registered subcommands with summaries, and subcommand options parse through punk::args so projects can declare complex arguments - with the G-030 degradation rules (boot never fails and help degrades to plain text when punk::args or the ANSI rendering stack is unavailable).
Acceptance: `<punkexe> -help` renders tabled usage listing all subcommands including project-registered ones, each with a summary; at least one built-in subcommand's options are declared and parsed via punk::args with tabled usage errors on invalid input (the G-015 script subcommand or the G-025 version-report subcommand are the natural candidates); a project-registered subcommand's help appears by registration alone - no edits to the shared dispatch; with punk::args or the rendering stack unloadable, boot proceeds and help degrades to a plain subcommand list; verified on both a zipfs-based and a non-zipfs kit where both remain supported.
## Context

3
goals/G-033-proj-mode-cwd-project.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/vfs/_config/punk_main.tcl (package-mode dispatch and boot-time root discovery), src/modules/punk/repo-999999.0a1.0.tm (find_project / is_project_root - reuse or lean boot mirror), bin/AGENTS.md (mode docs)
Acceptance: as in root GOALS.md index (canonical).
Goal: a `proj:` prefix on the package-mode string (e.g. `punkshell proj:internal-src shell`) makes the dev/src path blocks resolve against the punk project containing the current working directory - walking up to the nearest VCS repo root, the marker punk::repo::is_project_root already uses - instead of the executable's own project, so an installed ("visitor") punkshell binary, including one downloaded standalone with no source tree around it, can interactively explore a project that builds no shell-capable binary or no binary at all; the prefix scopes WHICH project, staying outside the ordered dash-list whose block order remains the same-version tie-break dial, and the explicit prefix gates the behaviour so no `cd` silently rebinds a normal launch.
Acceptance: `<installed-punkexe> proj:internal-src shell` (the documented canonical visitor invocation - kit copies win same-version ties, protecting the visiting shell's infrastructure; `proj:src` is the documented faithful-vintage variant where the project's copies win) launched from within a punk project's tree discovers the project root by walking up from cwd to the nearest VCS repo root and feeds it to the existing src-mode path machinery (src/modules, src/bootsupport/modules, src/vendormodules on the module path, src/lib on auto_path), so the session loads the project's dev-versioned modules while ordinary version resolution still lets the project supply anything the kit lacks or exceeds; the launch reports the detected project root and effective path precedence (never a silent rebind); with no project found walking up from cwd, or a `proj:` string containing no root-using block (dev/src), it warns and proceeds without false rebind; proven for a standalone binary outside any source tree (internal = kit contents regardless of binary location) against a project that builds no executable (tomlish is the natural first); exe-relative `src`/`dev` discovery is unchanged for a binary that IS in a project's bin/; the packagemode help text drafted in the detail file becomes the live punk::args documentation when implemented (rendered via G-032).
## Context

6
goals/G-034-modpod-codeinterp-tcl86.md

@ -0,0 +1,6 @@
# G-034 Zip-based #modpod modules mount in the shell code-interp on Tcl 8.6
Status: proposed
Scope: src/modules/punk/mix/ (modpod mount path), vfs::zip availability in the repl code interp, src/modules/punk/cap/ (templates capability handler)
Goal: zip-based `#modpod` modules (e.g. `punk::mix::templates`) mount and their punk::cap handlers register in the `shell` subcommand's code interp on Tcl 8.6, matching the `script`/main-interp context - so `dev module.templates` and other `punk.templates`-capability consumers work in an interactive 8.6 shell instead of failing with `invalid command name vfs::RegisterMount`.
Acceptance: `dev module.templates` in an interactive 8.6 punk shell (`shell` subcommand) lists the template providers with no `failed to load ZIP archive-based module` / `invalid command name vfs::RegisterMount` / `Unable to register any template providers` / `invalid command name ::punk::cap::handlers::templates::api_punk.templates` errors, matching the `script`-subcommand output on the same binary (verified 2026-07-07: script works, shell fails); root cause fixed (the code interp lacks the vfs::zip library that provides vfs::RegisterMount for pre-zipfs Tcl, present in the main interp - restore it to the code interp or use an alternative modpod mount path there); Tcl 9 (built-in zipfs) behaviour is unchanged.

3
goals/G-035-mixed-tm-pkgindex-provision.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/tests/modules/punk/libunknown/testsuites/ (characterization suite), src/modules/punk/libunknown-0.1.tm and src/modules/punk/packagepreference-999999.0a1.0.tm (as characterised, fixed only if outright bugs surface), src/modules/AGENTS.md + src/lib/AGENTS.md (resulting guidance)
Acceptance: as in root GOALS.md index (canonical).
Goal: the behaviour when the same package is provided both as a .tm module and as a pkgIndex.tcl-based library - same or differing versions, under the standard package unknown, punk::libunknown and punk::packagepreference - is characterised by committed tests, and the currently informal working rule ("avoid mixing provision forms for one package - unexpected behaviour even with libunknown's improvements") is either substantiated with the specific failure modes named in AGENTS.md guidance, or retired if the characterisation shows the machinery now handles mixing predictably.
Acceptance: a committed test suite (extending src/tests/modules/punk/libunknown/testsuites/) characterises at least: same name+version provided via .tm and via pkgIndex.tcl (which registration wins, and whether it is deterministic across scan-trigger orderings) under the standard scanner, under punk::libunknown, and with punk::packagepreference active; differing versions across the two forms (version selection integrity including package prefer latest, and whether the losing form's registration lingers); re-registration effects (package forget then re-require crossing forms); surprising-but-accepted behaviours are pinned with GAP/known-quirk comments (the fossilmove characterization pattern), outright bugs fixed or filed as goals; the resulting do/don't guidance lands in src/modules/AGENTS.md and src/lib/AGENTS.md naming the characterised failure modes (or explicitly lifting the avoid-mixing rule if unwarranted).
## Context

1
goals/G-038-piped-session-continuity.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/lib/app-punkshell/punkshell.tcl (eof-restart handover), src/modules/punk/repl-999999.0a1.0.tm (eof-restart done-mode that skips codethread teardown), src/modules/punk/repl/codethread-999999.0a1.0.tm (as touched)
Goal: when piped stdin ends and app-punkshell opens the interactive console shell, the session continues rather than restarts - the piped phase's codethread/code interp survives (variables, procs, namespaces, loaded packages, cwd, ::errorInfo/::errorCode) and only the input channel and repl reader are renewed - so `'set ::jjj blah' | <punkexe> shell` leaves ::jjj inspectable at the prompt, replacing today's silent fresh-session swap (least-surprise violation; motivating transcripts in the detail file) and obsoleting the standalone piped-error-record mechanism this goal described before its 2026-07-08 rework.
Acceptance: after `'set ::jjj blah;error xxxx' | <punkexe> shell` reaches the interactive prompt: `set ::jjj` returns blah; the xxxx message, errorInfo traceback and errorCode are inspectable (via preserved ::errorInfo if nothing in the handover overwrites it, else a documented record - the chosen mechanism noted in the detail file); a proc defined and a package required during the piped phase remain available; a cwd change from the piped phase persists; a one-line notice at the restart says the session continued from piped input (mentioning the error when the last piped command errored, quiet otherwise); after the restart a terminal query from the code interp succeeds (e.g. `help console` runs its cursor-position test instead of being refused by the stale settled can_respond=0 anchor); interactive exit/quit teardown afterwards is clean (the G-036 regression harness still passes); PUNK_PIPE_EOF policy semantics and the script subcommand are unchanged; verified on both Tcl generations.
## Context

1
goals/G-039-orphan-console-spin.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm (console reader/event loop and EOF/error paths), src/modules/punk/console-999999.0a1.0.tm; investigation-first
Goal: the observed failure mode - an interactive punk902z left running after its hosting terminal/console went away spins roughly a full core indefinitely (observed 2026-07-08: a 37-minute orphan with a single hard-looping thread) - is reliably reproduced and root-caused, then fixed or mitigated so a shell whose console dies exits or reaches zero-CPU idle cleanly.
Acceptance: a documented procedure reproduces the spin on the current kit (e.g. launch an interactive shell in a terminal, then kill/close the hosting terminal or conhost), or the investigation records the attempts made and what evidence would reopen it; the spinning code path is identified (prime suspect: a console read/event loop treating a dead console's immediate EOF/error as retryable without backoff or termination - adjacent to the console-EOF restart path G-038 takes ownership of); after fix/mitigation, the same procedure shows the orphaned process exiting or settling at effectively zero CPU within a short grace period, with live-console interactive behaviour unchanged; the wedge-scoring hazard note (orphans polluting process-liveness checks in test harnesses) is updated to match the outcome.
## Context

1
goals/G-041-punkargs-form-matching.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (parse form selection, arg_error/usage form marking), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/synopsis closest-form indication), src/tests/modules/punk/args/testsuites/
Goal: for multi-form definitions punk::args determines which form(s) an argument list matches - parse without -form attempts all permitted forms instead of effectively form 0, -form accepts the documented list-of-forms restriction, and the documentation surface indicates the match ('i after cancel <id>' presents the cancel form; 's after cancel someid' marks the closest synopsis) - with explicit single-form restriction retained for callers that require it.
Acceptance: parsing a multiform definition (after-like fixture) without -form succeeds when the args match exactly one form (the pinned GAP tests in forms.test flip to auto-selected results); an argument list matching no form (or several) produces an error naming the candidate forms rather than a form-0 type error; -form with a list of form names/indices restricts parsing to that subset (currently an 'Expected int 0-N or one of ...' error); 'i <cmd> <args...>' and synopsis output indicate the best-matching form(s) for supplied args; explicit -form <single-name-or-index> behaviour is unchanged; the full punk::args and punk::ns suites pass.
## Context

3
goals/G-042-subshell-help-topics.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk-999999.0a1.0.tm (::punk::helptopic registry), src/modules/punk/config-0.1.tm (stored declaration source), src/modules/punk/repl-999999.0a1.0.tm (subshell entry/exit hooks)
Goal: a named subshell can declare its own help topics (name, aliases, summary, text content) in punk::config stored configuration - registered into the ::punk::helptopic registry on subshell entry and removed/restored on exit - so 'help' inside a customised subshell presents that subshell's topics with 'help topics' and 'i help' staying accurate (the registry already regenerates the punk::args definitions), under a defined precedence policy between topics and command-name fallthrough.
Acceptance: a subshell whose configuration declares at least one custom topic shows it in 'help topics' and renders its configured content via 'help <topic>' inside that subshell, and 'i help' lists it as a documented choice; on quit/switch-away the parent's topic set is restored (no leakage, proven by declaring a topic in a subshell and checking the parent after exit); configuration is read through punk::config (toml per G-014 - no ad-hoc parsing) and a shell with no topic configuration behaves unchanged; config-declared topics are text-content only - they cannot name code to execute (or a recorded design decision to the contrary with its sandboxing rationale in the detail file); the shadowing policy is implemented and documented: a declared topic colliding with a built-in topic is rejected (or explicitly overrides per a documented rule), a topic name shadowing a command name wins over command fallthrough as today with 'i <cmd>'/'s <cmd>' documented as the command-help escape hatch; depends on G-014 for the stored-config substrate - the registry-side registration/restore mechanism may land earlier behind a programmatic API, with the config binding completing when G-014 lands.
## Context
@ -115,6 +116,6 @@ Implications adopted for this goal:
- Seam already in place (punk 0.2.0): ::punk::helptopic::register / resolve / define_docs;
'help topics' derives from the registry; per-topic punk::args ids follow the
::punk::helptopic::<topic> convention consumed by 'i help <topic>' subhelp choiceinfo.
- Related session records: goals/G-040-punkargs-choicealiases.md (choice display folding),
- Related session records: goals/archive/G-040-punkargs-choicealiases.md (choice display folding),
the 2026-07-08 CHANGELOG entries for punk 0.2.0 (registry) and punk::ns 0.1.1
(doc-lookup trace cleanup).

1
goals/G-043-subshell-definition-plugins.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/cap-999999.0a1.0.tm (+ new punk.subshell handler), src/modules/punk/overlay-999999.0a1.0.tm (commandset composition), src/modules/punk/pluginmgr-0.5.1.tm (discovery/trust layer), src/modules/punk/mix-999999.0a1.0.tm (init wiring precedent), src/modules/punk/repl-999999.0a1.0.tm (subshell entry/exit), src/modules/punk/config-0.1.tm (data-declaration overlap with G-014/G-042)
Goal: a named subshell's definition - commandset imports (punk::overlay), help topics (the G-042 registry API) and config defaults - can be supplied by provider packages registered through a punk.subshell capability (punk::cap handler validating declarations) and/or declared in stored config (toml per G-014, naming already-installed commandset packages - absorbing punk::overlay's "toml configuration files for defining CLI configurations" todo), with punk::pluginmgr as the discovery/safe-interp trust layer for providers not already trusted - so entering a subshell composes its command surface and help from declarations instead of hardcoded init code.
Acceptance: a provider package declaring the punk.subshell capability supplies at least a commandset binding (namespace + prefix) and a help-topic set for a named subshell, and entering that subshell composes them (prefixed commands callable, topics in 'help topics'/'i help') with quit/switch-away restoring the parent surface (no leakage); the punk.subshell punk::cap handler validates declarations (malformed declarations vetoed with a useful message); a subshell's commandset composition can equivalently be declared in stored config without a provider package (G-014 substrate), and the existing hardcoded 'dev' CLI composition keeps working unchanged; declarations for capabilities with no registered handler are discoverable (query or report - closing the silent punk.isbogus gap); punk::cap pkg_unregister leaves no stale handler state (the 'destroy api objects?' review resolved); punk::pluginmgr-based discovery/loading of a provider is either demonstrated end-to-end (including .tm module loading in the safe interp) or explicitly deferred with the remaining pluginmgr gaps recorded in the detail file; G-042's registry mechanism is consumed, not duplicated.
## Context

1
goals/G-044-repl-command-completion.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm (editbuf/reader integration, provider seam), src/modules/punk/args-999999.0a1.0.tm + src/modules/punk/ns-999999.0a1.0.tm (introspection surfaces as consumed), src/modules/punk/console-999999.0a1.0.tm (rendering)
Goal: the interactive repl (raw mode primary) offers command completion and hinting driven by punk::args documentation - the command word resolved through the same doc-lookup flow as 'i' (ensembles, subcommands, ensemble parameters) and the argument position through the definition (options, choices with parse-consistent matching, literals, form awareness per G-041) - via an activation scheme that preserves the editbuf's literal-tab support (not exclusively plain Tab), behind a per-subshell completion-provider seam so alternative-language subshells (e.g. an interactive xtal session) can replace, augment or cleanly disable the Tcl-centric completer.
Acceptance: in a raw-mode interactive session a documented trigger (recorded in the detail file; a literal tab remains enterable into the editbuf) presents completions for command words, ensemble subcommands (resolved via the punk::ns doc-lookup flow without executing candidate commands), option flags and choice values (matching consistent with parse semantics: prefix/deny/reserve, and aliases once G-040 lands); a hint display shows the synopsis of the form(s) matching the partial input (consumes G-041's candidacy API when available - until then all-forms or form-0 display with the limitation noted is acceptable); no per-keystroke terminal queries are added when no completion display is active (G-013 approach note); the completer is a provider interface with the punk::args-driven implementation as the default - a subshell can declare an alternative provider or none, proven at minimum by a subshell with completion disabled showing no Tcl-centric interference (the xtal minimum bar; full xtal completion out of scope but not precluded by the interface); line-mode behaviour is unchanged unless a documented subset is added; G-013's raw editor essentials are a prerequisite and this goal does not weaken G-013's acceptance.
## Context

1
goals/G-045-punkargs-authoring-ergonomics.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (record parsing in resolve, tstr interplay, arg_error @cmd rendering), src/tests/modules/punk/args/testsuites/ (rendering.test/defquoting.test as the safety net), src/modules/punk-999999.0a1.0.tm (::punk::helptopic::define_docs de-hacked as the consumer proof)
Goal: authoring punk::args definitions no longer requires backslash line-continuations or ad-hoc workarounds for multi-line records and constructed definitions - a parser-recognised record-continuation mechanism (candidate: an unquoted trailing -& token, with the detail file recording the collision analysis and the element-count disambiguation alternative), -unindentedfields honoured for @cmd fields, and constructed (string-built) definitions able to opt into the same whole-block indent normalization file-style definitions get - with the container quoting rules (braced=literal, quoted=Tcl backslash semantics, \$\{ escape) promoted from defquoting.test into the define documentation.
Acceptance: a definition using the chosen record-continuation mechanism parses identically to its backslash-continuation equivalent (existing definitions unchanged - continuation is additive), with the token's collision rules documented and an escape/rejection story for values that legitimately match it; @cmd -help/-summary honour -unindentedfields (the rendering.test GAP rendering_unindentedfields_cmd_help_GAP flips to aligned); a constructed definition can request whole-block normalization so embedded continuation indentation behaves as in file-style definitions (the rendering_constructed_def_indent_characterization expectations updated to the chosen semantics), and ::punk::helptopic::define_docs drops its manual pre-normalization to prove it; the quoting rules from defquoting.test appear in the punk::args::define -help documentation; the full punk::args suite (128 tests incl. the rendering invariants: nesting independence, relative-indent preservation) passes with GAP tests flipped, none weakened.
## Context

1
goals/G-047-declared-primary-vcs.md

@ -2,6 +2,7 @@
Status: proposed
Scope: punkproject.toml (schema), punkproject.local.toml (new, uncommitted per-checkout override), root AGENTS.md (Commit Conventions (any VCS) section), .gitignore + .fossil-settings/ignore-glob (ignore rules for the local file, per the coexistence contract), src/project_layouts/ (layout template payload - default values and ignore seeding only; project.new validation is follow-on work)
Goal: a `[workflow] vcs = "<system>"` field in punkproject.toml declares the team's primary upstream VCS - the authoritative interchange history and the default target for unqualified "commit"/"checkin" requests - while the same field in an uncommitted per-checkout punkproject.local.toml lets an individual developer redirect their own unqualified commit instructions to their preferred system (mixed git/fossil-preferring teams), resolution order local override > project field > filesystem detection; the declaration governs developer commit workflow only - punk internal machinery (punkcheck tracking of related projects, upstream infrastructure pull per G-027, central project discovery per G-016) remains standardized on fossil regardless of the declared field; punkshell itself carries `[workflow] vcs = "git"` and derived-project layout templates default to fossil.
Acceptance: punkshell's punkproject.toml contains `[workflow] vcs = "git"`, and root AGENTS.md "Commit Conventions (any VCS)" documents the resolution order (punkproject.local.toml field, then punkproject.toml field, then filesystem detection, with the existing prose as final fallback) as the source agents consult for unqualified commit/checkin requests; punkproject.local.toml is ignored by both VCS per the .fossil-settings coexistence contract (git check-ignore matches it, ignore-glob covers it, the contract's verification comparisons stay clean); a reader resolving the primary VCS anchors at the project root via the punk::repo::is_project_root marker and ignores any nested punkproject.toml/punkproject.local.toml `[workflow] vcs`; the fossil-machinery carve-out is recorded in root AGENTS.md alongside the field documentation (a git-primary ecosystem project still maintains its fossil repo for punkcheck/pull/discovery machinery), and the field stays advisory to G-016 discovery with detection as fallback; the mixed-team sync semantics are documented (team primary = authoritative interchange; a developer committing granularly to the secondary owns batching their work up to the primary); project version patch-bumped with a CHANGELOG entry for the schema addition; make.tcl/project.new validation of the field against detected VCS systems stays out of scope (follow-on work).
## Context

1
goals/G-048-textblock-table-punkargs.md

@ -2,6 +2,7 @@
Status: proposed
Scope: src/modules/textblock-999999.0a1.0.tm (textblock::table proc + PUNKARGS, textblock::class::table class - constructor, opts_table_defaults, methods)
Goal: textblock::table parses its arguments via punk::args::parse (replacing the unvalidated dict merge at L6397), with its PUNKARGS definition covering both table-wrapper-specific options (-return, -rows, -headers) and the constructor passthrough options - the latter sourced by referencing punk::args definition blocks authored inline on the textblock::class::table class methods (the pattern used for render_to_input_line and rendertest in punk::ansi::class::class_ansi: lappend PUNKARGS [list { @id ... }] immediately before the method, punk::args::parse $args withid "..." inside the method body), so the constructor itself parses via punk::args and its documented option set is the single source of truth that textblock::table's PUNKARGS references rather than a parallel hand-typed list - retiring the "more options available - argument definition is incomplete" caveat and closing the -return -choiceprefix documentation/parsing mismatch (item #8 from the punk::args -choices audit) as a side effect.
Acceptance: textblock::class::table's constructor carries an inline punk::args define block (lappend PUNKARGS, @id naming the class+method) and parses its args via punk::args::parse withid, replacing the current manual switch at L466; textblock::table's PUNKARGS references the constructor's documented options (via @id reference, a shared fragment, or a documented include mechanism - the chosen mechanism recorded in the detail file) so table's definition is complete without hand-duplicating the constructor's option list; an invalid -return value (e.g. -return tab) produces a punk::args usage error instead of silently passing through; valid inputs reach textblock::class::table new with behaviour parity to the current dict merge; the -choiceprefix 0 on -return is honoured by construction (punk::args::parse enforces it); the "NOTE: more options available - argument definition is incomplete" comment is removed; existing textblock test suites pass.
## Context

3
goals/G-050-synopsis-validity-marking.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/ns-999999.0a1.0.tm (synopsis), src/modules/punk/args-999999.0a1.0.tm (synopsis renderer), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (synopsis pins)
Acceptance: see GOALS.md index entry (canonical).
Goal: synopsis ('s') marks supplied argument words for validity the way cmdhelp's usage display does (goodarg/badarg styles via the G-049 parse-status structure), offers the same status machine-parsably in its dict return, and replaces the curried-alias excess-args length arithmetic (the 's pse' REVIEW note) with a parse against the resolved definition.
Acceptance: 's <cmd> <valid args>' renders the supplied words marked as validated and 's <cmd> <invalid args>' marks the offending word, while output with no argument words supplied is unchanged (the synopsis_GAP_no_argument_validity_marking pin flips); the dict return carries per-argument status; the curried-alias substitution behaviour (synopsis_curried_alias_shows_braced_target pin) is resolved to a documented presentation derived from parsing rather than list-length arithmetic; existing synopsis output tests (args testsuite synopsis.test and test::punk::args) pass; depends on G-049 for the status structure; G-044's completion/hint display is a consumer of the same status API (cross-reference, not a dependency).
## Context

3
goals/G-051-cmdinfo-pseudo-and-prefix.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/ns-999999.0a1.0.tm (cmdinfo, cmd_traverse), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test, src/tests/modules/punk/ns/testsuites/ns/cmdflow.test
Acceptance: see GOALS.md index entry (canonical).
Goal: cmdinfo reports a distinct cmdtype (e.g 'doconly') when resolution lands on a punk::args id with no corresponding real command instead of today's 'notfound', and the space-delimited docid jump accepts the same word prefixes the parser accepts - via the shared punk::args::choiceword_match resolver, not a second matching rule - so 'i string is tr' documents what 'string is tr' actually executes.
Acceptance: the pinned GAP tests flip: cmdhelp_GAP_pseudo_command_cmdtype_notfound and cmdhelp_GAP_string_is_true_pseudo report the new cmdtype with the docid unchanged, cmdhelp_GAP_spaceform_docid_prefix_not_honoured and cmdhelp_GAP_string_is_prefix_not_honoured resolve the child docid from a prefix exactly when parse accepts that prefix (honouring -choiceprefix, -choiceprefixdenylist, -choiceprefixreservelist and -choicealiases per G-040 parity); consumers of cmdinfo's cmdtype (cmdhelp, synopsis, eg) handle the new value with no behaviour change for real commands; cmdflow.test and the non-GAP cmdhelp.test tests pass unchanged.
## Context

3
goals/G-052-oo-method-autodef.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/ns-999999.0a1.0.tm (generate_autodef oo branches, cmd_traverse), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test
Acceptance: see GOALS.md index entry (canonical).
Goal: an undocumented method on a tcl::oo object or class gets an (autodef) punk::args definition generated from its introspected parameter list (info object call + method definition arglists - the machinery generate_autodef already uses for its class summary), so 'i $obj <method> <args...>' shows method-level usage with good/bad argument marking instead of only the class summary with the method word highlighted - explicitly documented methods continue to win.
Acceptance: the cmdhelp_GAP_oo_undocumented_method_class_summary_only pin flips: cmdinfo on an undocumented method resolves a method-level (autodef) docid with the trailing words in args_remaining, and bogus trailing arguments render error-scheme output instead of an info-scheme class summary; documented-method behaviour (cmdhelp_oo_documented_method) is unchanged; instance methods, class-defined methods and mixin/superclass-inherited methods resolve (constructor/'new' signatures at minimum characterized, in-scope or explicitly deferred in the detail file); the "-choiceprefix 0 ... methods must be specified in full always? - review" question on the class-summary method choicelist is resolved and documented either way; cmdflow.test and cmdhelp.test pass.
## Context

3
goals/G-053-punkargs-multiple-ranges.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (spec compiler, parse, arg_error/synopsis renderers), src/tests/modules/punk/args/testsuites/args/
Acceptance: see GOALS.md index entry (canonical).
Goal: -multiple accepts a {min max} occurrence range (mirroring -choicemultiple; max -1 unbounded) alongside the legacy booleans - so a definition can declare "at most once, repeat is an error" ({0 1}) or bounded repetition ({2 4}) instead of choosing between silent last-wins (0) and unbounded collection (1) - with boolean semantics preserved exactly, including the prepend-defaults/last-wins override idiom.
Acceptance: parse raises a usage-style arity error naming the argument for occurrences outside a declared range; boolean -multiple 0/1 behaviour is unchanged (full existing punk::args suite passes untouched); the -optional/range-min reconciliation rule is documented and enforced at define time; the usage table Multi column and synopsis reflect declared ranges; -multipleunique/-multipleuniqueset compose with max>1 ranges unchanged; characterization tests cover the new forms and the value-shape rule.
## Context

3
goals/G-055-tclcore-regen-workflow.md

@ -2,7 +2,8 @@
Status: proposed
Scope: goals/G-055-tclcore-regen-workflow.md (workflow doc), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (+ buildversion), src/tests/modules/punk/args/testsuites/args/ (parity pins), TEMP_REFERENCE/tcl9 (read-only source input; source retrieval mechanism deferred to the buildsuites toml configs / G-005 era)
Acceptance: see GOALS.md index entry (canonical).
Goal: refreshing punk::args::moduledoc::tclcore is a documented, repeatable agent-driven workflow taking a Tcl source tree + version as input - man-page/source text carried verbatim (rearranged into @cmd -help/@examples/choicelabels, not reimagined; exceptions: synopsis notation is translated into punkshell's own more generic synopsis syntax rather than copied from the source docs, and text may be re-folded/line-wrapped to keep help display-width friendly while punk::args lacks word wrapping), one adaptive package with version-conditional definitions where released behaviour differs (8.6 vs 9.x), each regenerated command verified by a real-vs-model behavioural probe before acceptance, and source-checkin provenance recorded - designed so the same workflow extends later to tkcore (which loads on 'package require tk' and relies heavily on shared documentation sections, e.g the (default)::punk::args::moduledoc::tkcore::tk_standardoptions id) and to moduledocs for other core.tcl-lang.org projects (e.g tcludp).
Acceptance: the workflow is documented in the detail file (inputs, verbatim-text fidelity policy, probe-verification gate, provenance recording, shared-section reuse guidance for the tkcore pattern); the whole ::string ensemble plus a small selection of other commands including the multi-form ::after have been regenerated or verified through the workflow against the reference Tcl 9 sources, with parity pins added and 8.6 released-behaviour differences honoured (G-054's harvest/version-conditional techniques); an initial scan across tclcore commands identifying constructs not adequately modelable in the current punk::args system is recorded in the detail file, with each gap flagged as a candidate goal rather than worked around silently; tkcore and other-project moduledocs remain out of scope beyond the workflow being demonstrably reusable for them.
## Context

3
goals/G-056-punkargs-word-wrapping.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (arg_error table and string renderers, helpers), src/modules/textblock-999999.0a1.0.tm (only if cell/column-level wrap is the chosen mechanism), src/tests/modules/punk/args/testsuites/args/ (new wrapping characterization + existing rendering suites)
Acceptance: see GOALS.md index entry (canonical).
Goal: punk::args help display (@cmd -help, argument -help, choicelabels) word-wraps over-width lines to the effective display width at render time - ANSI/grapheme aware, splitting long logical lines but never joining existing ones, so deliberately structured content (hand-folded blocks, choice tables, art choicelabels, indented continuations) is preserved by construction - letting definitions store unfolded verbatim text (retiring the G-055 re-folding exception for new work) while usage output stays terminal-width friendly.
Acceptance: a definition whose -help/choicelabel contains a single long unfolded line renders fully within the effective display width in both the table and -return string renderers (no truncation, no overflow, wrapped continuations indented to match the field's existing paramindent alignment); wrap-point calculation is ANSI-aware (SGR sequences measure zero width; styling in effect carries across the wrap) and grapheme/double-width aware to the same standard as existing punk::ansi/textblock width handling; existing hand-folded and structured help renders byte-identical (full existing args rendering/usagemarking/deferredhelp suites pass unchanged - never-join semantics verified by characterization tests); effective width derives from the terminal when available with the current -maxwidth 80 table default as fallback, and an explicit width option overrides; the chosen mechanism (punk::args-side pre-wrap of field text vs textblock table column wrap support) and its rationale are recorded in the detail file; G-055's folding exception is marked lapsed for new work once this ships.
## Context

3
goals/G-057-kit-icon-embedding.md

@ -2,7 +2,8 @@
Status: proposed
Scope: src/make.tcl (kit/zipkit wrap steps), src/runtime/punk1.ico (project default, existing), src/vfs/*.vfs (override placement convention), src/runtime/mapvfs.config (only if an explicit config element is the chosen override mechanism), TEMP_REFERENCE/tcl-sfe (read-only reference), helper proc location decided in the work (make.tcl inline vs punk::mix lib)
Acceptance: see GOALS.md index entry (canonical).
Goal: Windows kit/zipkit builds produce executables carrying an embedded icon chosen at build time - defaulting to the project icon src/runtime/punk1.ico, overridable per kit by its .vfs folder - by replacing the icon resources in the built executable using the twapi-based mechanism demonstrated in tcl-sfe (TEMP_REFERENCE/tcl-sfe, by twapi author and Tcl core member Ashok P. Nadkarni): RT_ICON/RT_GROUP_ICON replacement via twapi resource-update APIs, applied so the appended vfs payload stays intact (icon the stub before appending, or sfe-style split/update/reattach).
Acceptance: a Windows `make.tcl project` build produces kit executables whose embedded icon resources are the project default punk1.ico, and a kit whose .vfs supplies an override icon gets that icon instead (verified by resource inspection, e.g twapi::extract_resources, not just Explorer eyeballing); the icon-replaced executables still boot to a working punk shell reading their vfs payload for the kit types we build (kit, zip, zipcat per mapvfs.config); runtimes under src/runtime are never modified - replacement applies to the built copies only; twapi unavailable or non-Windows platform skips the icon step with a notice and the build otherwise completes unchanged; rebuilds are idempotent (re-wrapping an already-iconed build copy converges, no resource accumulation); the override convention (filename/location in the kit's custom .vfs folder vs a mapvfs.config element) and the stub-vs-split ordering decision are recorded in the detail file with the tcl-sfe attribution.
## Context

3
goals/G-060-qemu-test-matrix.md

@ -2,7 +2,8 @@
Status: proposed
Scope: build/test orchestration config and scripts (location to be settled with the buildsuites toml work - see G-005/buildsuites direction), goals/G-060-qemu-test-matrix.md (workflow + license posture), src/tests/ (any guest-driving hooks)
Acceptance: see GOALS.md index entry (canonical).
Goal: comprehensive cross-platform verification (linux variants, FreeBSD/NetBSD/OpenBSD, arm architectures) is achievable from a single dev machine by driving QEMU guests as strictly external tooling - punkshell invokes qemu as a separate process with declarative per-guest config, and nothing in punkshell or its artifact repos bundles, links against, derives from or otherwise couples to QEMU (GPLv2) - so the project's BSD licensing posture is unaffected and QEMU remains a swappable convenience: the guest-driving contract is push-based artifact staging and result collection (the same pattern G-059 establishes for WSL - guests never share a working tree with the host) and must be satisfiable by real hardware, WSL or another hypervisor.
Acceptance: a documented, repeatable workflow provisions at least one non-Windows QEMU guest (e.g FreeBSD x86_64) that fetches a punkbin runtime and runs the source-tree suite inside the guest, driven from the Windows dev machine with results collected back; the guest-driving interface is hypervisor-agnostic (documented contract; QEMU is one provider); the license posture is recorded in the detail file (external-process invocation only, no QEMU binaries or derived code committed to punkshell or punkbin, guest OS images not redistributed by the project - fetched/built per machine); suite runs on machines without QEMU are unaffected.
## Context

3
goals/G-061-pseudoconsole-expect.md

@ -2,7 +2,8 @@
Status: proposed
Scope: test-harness support (location TBD during the work: src/tests/testsupport/ and/or a small punk module), src/tests/shell/ (capability-gated interactive suites), goals/G-061-pseudoconsole-expect.md
Acceptance: see GOALS.md index entry (canonical).
Goal: interactive punkshell behaviour is testable headlessly and byte-accurately by driving a real shell under a pseudoconsole - ConPTY on windows, pty on unix - through an expect-like harness (spawn, send literal keys and control/arrow sequences, await patterns in the rendered ANSI byte stream with timeouts, collect transcript), giving the goals/G-044-detail preserve-list behaviours (continuation hints, raw-mode colour staging, tab markers and space dots, multiline history navigation) their durable cross-platform verification tier - complementary to G-020 capture testing (which verifies what reaches the screen; this verifies the byte stream) and to the G-001 console-seam unit tier.
Acceptance: a capability-gated harness spawns a built punkshell under ConPTY on windows (and under a unix pty when run on unix or via the G-059 WSL staging pattern), sends keys including control/arrow sequences, and awaits expected patterns with timeouts and forced teardown on expiry (hang-proof per the shell-test conventions); at least three preserve-list behaviours are verified through it (e.g the in-proc `set x "{*}{"` continuation-hint sequence, raw-mode tab-marker rendering and deletion, up-arrow recall and edit of a multiline history entry); tests skip cleanly where no pseudoconsole is available; the harness API is platform-agnostic with per-OS backends; candidate synchronisation/communication mechanisms between harness and shell (including the inter-subshell beep-protocol idea) are evaluated and the chosen approach documented in the detail file.
## Context

1
goals/G-007-console-location-transparency.md → goals/archive/G-007-console-location-transparency.md

@ -2,6 +2,7 @@
Status: achieved 2026-07-05
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Goal: punk::console presents one API in every interp/thread of a punk session - code-interp callers see the same console facts and can perform the same queries/operations as the parent, routed to the console-owning context via a punk-side ownership registry, with no `repl eval` required.
Acceptance: from a running punk session's code interp, without `repl eval`: `console_fact_get` returns the same values the parent sees and a fact set in the parent is immediately visible; a terminal query (e.g. `get_cursor_pos` or `dec_get_mode`) against the default console succeeds and cooperates with the repl reader (no lost or garbled input); a console constructed and owned by code-interp code is operated on locally (no round-trip to the parent); the existing console test suites pass and single-interp (non-repl) usage is unchanged.
## Context

5
goals/G-015-script-subcommand-piped-stdin.md → goals/archive/G-015-script-subcommand-piped-stdin.md

@ -1,8 +1,9 @@
# G-015 Punk executable `script` subcommand: reliable non-interactive piped/script execution
Status: achieved 2026-07-07 (implemented as src/lib/app-punkscript; verified on punk902z/tcl9 and punksys/tcl8.6 kits: piped success/error/exit-code cases, file form with args and stdin passthrough, and the motivating `dev projects.work *tomlish*` one-liner - table emitted, exit 0, no boilerplate. Stdin form additionally echoes the script's final result when non-empty, giving return-value commands one-liner ergonomics; file form keeps pure script semantics. Terminal-stdin-with-no-args yields a usage error by design - spot-check from a real console typed session remains a manual item.)
Status: achieved 2026-07-07
Scope: src/vfs/_config/punk_main.tcl, src/lib/app-punkshell/punkshell.tcl (script path or a leaner dedicated app package)
Acceptance: as in root GOALS.md index (canonical).
Goal: `<punkexe> script [<scriptname>] [<args>...]` executes a script file or piped stdin content (scriptname optional when input is piped) in an interp preloaded with the basic punk modules and aliases a punk shell provides by default, and always terminates with the script's success/failure as its exit code - without the `shell` subcommand's shellfilter channel transforms/logging stacks and without ever dropping into an interactive shell - so agents can reliably make piped script calls to punk executables.
Acceptance: piping commands to `<punkexe> script` runs them and terminates at stdin EOF with no trailing `exit` required, exit code 0 on success; a failing piped command terminates the process with a nonzero exit code and the error on stderr, never landing in an interactive shell regardless of console availability or PUNK_PIPE_EOF; `<punkexe> script <file> [<args>...]` executes the file with conventional ::argv0/::argv and propagates its error status the same way; the script path installs none of the `shell` subcommand's shellfilter stacks/transforms and the launch plumbing itself emits nothing on stdout/stderr (the current stub's stderr diagnostics removed) so exec-style callers see only the script's own output; the motivating example works with no package require boilerplate: piping `dev projects.work *<name>*` to `<punkexe> script` emits the matching-project table and exits 0, because the script interp carries the default punk shell module/alias environment.
## Context

3
goals/G-036-tcl9-udp-console-worker-wedge.md → goals/archive/G-036-tcl9-udp-console-worker-wedge.md

@ -2,7 +2,8 @@
Status: achieved 2026-07-08
Scope: src/modules/shellthread-999999.0a1.0.tm, src/bootsupport/modules/shellthread-1.6.2.tm, src/modules/shellfilter-999999.0a1.0.tm (as characterised - no product-code changes required by this goal; the punkshell mitigations are separate fixes)
Acceptance: (reworked 2026-07-08 after the root cause was found) the wedge mechanism is identified and written up in the detail file - DONE: bundled tcludp 1.0.12's Windows per-thread UDP_ExitProc closes the process-global synchronization events, proven by dump handle-table analysis plus a live CloseHandle breakpoint and confirmed by the upstream 1.0.12->1.0.13 diff, which already fixes it (so no upstream report is required; the standalone minimal repro originally required here is waived as moot by the user); the tcl9 kits bundle tcludp >= 1.0.13 with the in-context batch harness baseline resolved - DONE 2026-07-08 (run-2 syslog workers alive vs the 4/4-wedged 1.0.12 baseline); REMAINING: a has_bug-style detection in the punkshell check machinery (in the vein of punk::lib::check::has_tclbug_* / punk::console::check::has_bug_*, surfaced through the same reporting as 'help tcl'/'help console') reports the vulnerable combination - simple version-based detection (loaded/bundled tcludp < 1.0.13 on Tcl 9 Windows) is sufficient, no behavioural probe needed; loose-end decisions (punk8win 8.6 kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses) recorded in the detail file when made.
Goal: the Tcl 9-only wedge - a worker thread that has used a tcludp syslog socket stops servicing its event queue (timers, thread::send) when the punkshell process is console-attached, the proximate trigger of the piped-stdin exit/quit hang - is root-caused to a named component (Tcl 9 Windows console driver, tcludp, Thread extension, or a specific interaction) with the smallest demonstrating repro, so the user can decide on and manually file an upstream report.
Acceptance: (reworked 2026-07-08 after the root cause was found) the wedge mechanism is identified and written up in the detail file - DONE: bundled tcludp 1.0.12's Windows per-thread UDP_ExitProc closes the process-global synchronization events, proven by dump handle-table analysis plus a live CloseHandle breakpoint and confirmed by the upstream 1.0.12->1.0.13 diff, which already fixes it (so no upstream report is required; the standalone minimal repro originally required here is waived as moot by the user); the tcl9 kits bundle tcludp >= 1.0.13 with the in-context batch harness baseline resolved - DONE 2026-07-08 (run-2 syslog workers alive vs the 4/4-wedged 1.0.12 baseline); DONE 2026-07-08 (punk::lib 0.3.0 has_libbug_udp_threadexit, surfaced via 'help tcl' in punk 0.2.1): a has_bug-style detection in the punkshell check machinery (in the vein of punk::lib::check::has_tclbug_* / punk::console::check::has_bug_*, surfaced through the same reporting as 'help tcl'/'help console') reports the vulnerable combination - simple version-based detection (loaded/bundled tcludp < 1.0.13 on Tcl 9 Windows) is sufficient, no behavioural probe needed; loose-end decisions (punk8win 8.6 kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses) recorded in the detail file when made (open as of 2026-07-08 - non-gating).
## Context

1
goals/G-037-vendorlib-vfs-propagation.md → goals/archive/G-037-vendorlib-vfs-propagation.md

@ -2,6 +2,7 @@
Status: achieved 2026-07-08
Scope: src/make.tcl (new or extended step), src/vendorlib_tcl8 + src/vendorlib_tcl9 (sources), src/vfs/<kit>.vfs/lib_tcl8 + lib_tcl9 (targets), punkcheck tracking
Goal: platform-specific vendored binary packages under src/vendorlib_tcl<N>/<platform> reach the kit vfs lib_tcl<N> trees through a make.tcl step instead of hand-copying - updating a vendored package becomes a vendorlib drop plus standard build invocations (motivating case 2026-07-08: tcludp 1.0.12 -> 1.0.13 for the G-036 wedge fix - `libs`, `vfscommonupdate` and `project` all completed while every kit vfs still bundled udp 1.0.12, requiring a manual copy into each vfs lib_tcl9 folder).
Acceptance: with a newer package version placed under src/vendorlib_tcl9/<platform>, one documented make.tcl invocation updates the participating src/vfs/*/lib_tcl9 trees - installing the new package and removing or explicitly retiring the superseded version (no silent mixed-version provision, per the G-035 concerns) - with punkcheck-tracked provenance; which vfs folders participate is explicitly declared per kit rather than blanket-copied (kit vfs package sets may intentionally differ), with the declaration mechanism recorded (candidate home: the G-024 mapvfs toml); a subsequent `make.tcl project` yields kits loading the new version (provable via the tcludp case: built punk902z reports `package require udp` == 1.0.13 with no udp1.0.12 folder remaining in its vfs); the lib_tcl8 tree gets the same treatment or an explicit exclusion rationale in the goal record.
## Context

1
goals/G-040-punkargs-choicealiases.md → goals/archive/G-040-punkargs-choicealiases.md

@ -2,6 +2,7 @@
Status: achieved 2026-07-08
Scope: src/modules/punk/args-999999.0a1.0.tm (parse + usage rendering), src/modules/punk/ns-999999.0a1.0.tm (cmdinfo/cmd_traverse choice resolution parity), src/modules/punk-999999.0a1.0.tm (punk::help topic argdoc as first consumer), src/tests/modules/punk/args/testsuites/, src/tests/modules/punk/ns/testsuites/
Goal: punk::args supports choice aliases (-choicealiases {alias canonical ...}) accepted at parse and normalized to the canonical choice in results, folded into the canonical entry in usage display - and the punk::ns doc-lookup walk resolves choice words by the same rules as the parser (aliases, -choiceprefix, -choiceprefixdenylist, -choiceprefixreservelist) - so alias sets like punk::help's topics|help and console|term|terminal collapse to one displayed entry per topic with `help X` and `i help X` agreeing.
Acceptance: a definition using -choicealiases parses an alias (and an alias prefix where -choiceprefix allows) to its canonical choice in the parse result, with -choicerestricted 0 passthrough and the deny/reserve lists honoured unchanged; usage display shows one entry per canonical choice with aliases folded (no duplicate rows; -choicelabels attach to the canonical); punk::ns::cmdinfo/cmd_traverse resolve subcommand words to docids with the same outcome as the parser for alias, prefix, denied, reserved and unknown words (the pre-goal characterization tests updated from pinned-GAP to fixed); punk::help's topic definition adopts the feature so `i help` lists one entry per registered topic while `help h`/`help e` still fall through to command lookup; definitions without -choicealiases behave unchanged (existing punk::args and punk::ns suites pass).
## Context

1
goals/G-046-punkargs-deferred-help-and-fixes.md → goals/archive/G-046-punkargs-deferred-help-and-fixes.md

@ -2,6 +2,7 @@
Status: achieved 2026-07-10
Scope: src/modules/punk/args-999999.0a1.0.tm (resolve/get_dict: display-field deferral, dynamic-cache subst path, prefix writeback, string renderer, cmdhelp-facing messages), src/modules/punk/ansi-999999.0a1.0.tm (mark_columns argdoc as the reentrancy/perf testbed), src/tests/modules/punk/args/testsuites/ (GAP tests flip; perf verification)
Goal: argument resolution no longer processes -help and other display-only fields - their tstr expansion is deferred to display time (separately cached, per the existing in-source review notes) - so first parse of heavily documented commands gets measurably faster and definitions whose -help calls punk::args-parsing commands (the punk::ansi::mark_columns class) neither loop nor stall; alongside, the mechanical defects pinned by the characterization suites are fixed: @dynamic double-substituted multiline values align at their insertion column, prefix-normalized choice values keep the same shape as exact input, the -return string renderer aligns cmd-help continuations under the first line, and the misleading goodargs parse-error prefix in 'i <cmd> <args>' output is fixed.
Acceptance: parsing/argument resolution provably skips -help expansion (a definition whose -help contains a ${[...]} that would error or record its invocation shows the substitution did NOT run during a parse-only path, only for help display); first parse of punk::ansi::mark_columns drops from ~4s to well under a second with 'i punk::ansi::mark_columns' still rendering the embedded example, and a -help that parses its OWN definition id resolves or errors cleanly rather than looping; first-parse timing improves for at least one other heavily documented command (recorded in the detail file); rendering_atdynamic_multiline_help_insertion_GAP flips to all-aligned; choicegroups_imap_prefix_listwrap_GAP flips to shape-identical (prefix input yields the same plain string as exact input); the -return string renderer's cmd-help continuations align under the first line with relative indents preserved (rendering_string_renderer_characterization updated); the 'Bad number of leading values...' prefix shown by goodargs parsing in 'i string is'-style output is reworded or suppressed for the usage-display path; full punk::args and punk::ns suites pass with no non-GAP expectations weakened.
## Context

3
goals/G-049-punkargs-parse-status-model.md → goals/archive/G-049-punkargs-parse-status-model.md

@ -2,7 +2,8 @@
Status: achieved 2026-07-10
Scope: src/modules/punk/args-999999.0a1.0.tm (arg_error, parse error dispatch, colour-scheme handling), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp), src/tests/modules/punk/args/testsuites/args/usagemarking.test, src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test
Acceptance: see GOALS.md index entry (canonical).
Goal: the information behind usage-display argument marking (which supplied arguments validated, which argument failed and why, which scheme applies) exists as a documented parse-status structure produced from a parse attempt and consumed by both arg_error renderers, and punk::ns::cmdhelp can return it machine-parsably via -return dict - replacing the transient goodargs/badarg locals, the badarg gaps in non-choice validation failures, and the stateful shared colour-array scheme handling.
Acceptance: cmdhelp -return dict distinguishes an incomplete, a fully-valid and an invalid argument set via per-argument statuses (received/ok/bad + overall scheme/message/form) with the structure documented; the table and string renderers derive their marking from that same structure, with rendered output unchanged except where the pinned GAP tests flip: badarg marking covers type/allocation failures not just choice violations (cmdhelp_GAP_no_badarg_marking_for_failed_typed_value), an explicit -scheme is honoured on the parse-failure path (cmdhelp_GAP_explicit_scheme_ignored_on_failure), the failure message names the queried command instead of cmdhelp's internal parse source line (cmdhelp_GAP_errormsg_leaks_internal_source), and scheme rendering no longer depends on or mutates shared colour state - the documented -scheme choice value 'nocolour' takes effect and repeated renders of the same call are identical regardless of prior scheme renders (usagemarking_GAP_scheme_nocolour_renders_with_leftover_colours, usagemarking_GAP_dash_nocolour_leaks_into_shared_array, usagemarking_GAP_dash_nocolour_leak_affects_later_info_render); all non-GAP characterization tests in usagemarking.test and cmdhelp.test pass unchanged.
## Context

6
goals/archive/G-054-tclcore-stringis-harvest.md

@ -0,0 +1,6 @@
# G-054 tclcore moduledoc: runtime-harvested 'string is' class choices with cross-version behavioural parity pins
Status: achieved 2026-07-11
Scope: src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (+ tclcore-buildversion.txt), src/tests/modules/punk/args/testsuites/args/ (new parity test), TEMP_REFERENCE/tcl9 (read-only reference)
Goal: the ::tcl::string::is definition's class choices (and the generated per-class virtual docids) are harvested from the running interpreter at define time - static choicelabels applied to classes present, generic label for unrecognized classes, a version note for dict - so accept/reject parity with the running Tcl holds on 8.6 and 9.x without hand-maintained per-version lists.
Acceptance: parse/parse_status against ::tcl::string::is and its per-class virtual ids agrees with the real interpreter's error-vs-ok outcome for a pinned probe matrix (missing args, trailing flag-like str word, option/class unique-prefix acceptance and ambiguity rejection, unknown option/class, -failindex var consumption leaving no str, per-version class presence: dict, unicode) on Tcl 9.0.x and 8.6; the rendered choices show only classes the running interp accepts; the parity test derives expectations from the live interpreter (not version arithmetic) and passes under both; existing args/tclcore suites pass; tclcore buildversion bumped with changelog.

3
goals/G-058-static-runtime-packages.md → goals/archive/G-058-static-runtime-packages.md

@ -2,7 +2,8 @@
Status: achieved 2026-07-10
Scope: src/vfs/_config/punk_main.tcl (boot auto_path/tm path filtering), src/modules/punk/packagepreference-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm + src/modules/punk/repl/codethread-999999.0a1.0.tm (code interp / codethread bootstrap), src/modules/shellthread-999999.0a1.0.tm (punkshell-created worker threads) as applicable, src/tests/ (un-gated unit tests + constraint-gated shell/kit integration tests), punkbin artifact repo (separate git repo, local checkout c:/repo/jn/punkbin - pinned runtime additions)
Acceptance: see GOALS.md index entry (canonical).
Goal: punkshell running on a runtime with statically-linked/builtin packages (tclsfe-x64: Thread/twapi/sqlite3/tdbc; punkbin runtimes' builtin Thread/tcllibc/vfs/vlerq; the expected shape of future zig-built static runtimes, G-005) keeps those packages resolvable in every interp and thread punkshell fabricates: boot captures the static baseline (info loaded entries with empty filename, plus their provided versions) before replacing package search paths, code interps and punkshell-created threads are seeded with ifneeded scripts mapping each static package to 'load {} <pkg>', and punk::packagepreference resolves static-vs-bundled by a documented version-aware policy instead of blindly loading a bundled dll over an already-provided static package.
Acceptance: a punk91-style kit (tclsfe-x64 + punk9win.vfs, no thread dll in the vfs) boots to a working repl with no "can't find package Thread" - punk::console loads in the code interp, and package require Thread succeeds there and in a punkshell-created worker thread, resolving to the static version; in the same kit, twapi resolves per the documented policy (no repeat of the observed static-Twapi-masked-by-older-vfs-twapi-5.0.2 double load; a genuinely newer bundled copy remains reachable by that policy); dll-based kits (punk902z) boot and pass their existing shell test baseline unchanged, as does a plain tclsh dev launch; the seeding mechanism is generic - driven by the captured baseline, no runtime-specific package naming - and the boot-time static baseline is introspectable at the repl; the seeding/preference logic is covered by un-gated unit tests against simulated baselines (runnable under plain tclsh), while kit-boot integration tests are gated behind a capability-probed tcltest constraint (a built kit whose baseline shows static entries including Thread) that skips cleanly when no such kit is present; the runtimes used for verification (tclsfe-x64.exe at minimum) are added to the punkbin artifact repository under win32-x86_64 with sha1sums.txt updated, so the constraint is satisfiable on other machines via the existing runtime-retrieval path; the punk91 code-interp vfs/vfs::zip load failure is re-diagnosed after the fix and either resolved or recorded as a distinct issue/candidate goal.
## Context

3
goals/G-059-wsl-test-driving.md → goals/archive/G-059-wsl-test-driving.md

@ -2,7 +2,8 @@
Status: achieved 2026-07-11
Scope: src/tests/ (capability probe helpers + constraint-gated cases in existing suites, e.g the unix sh-payload execution test in modules/punk/mix/testsuites/scriptwrap/multishell.test and a runtime.bash behaviour test), src/tests/AGENTS.md (enablement notes)
Acceptance: see GOALS.md index entry (canonical).
Goal: test runs on a Windows dev machine can exercise selected unix-side behaviour through WSL when it is present AND suitable - a capability-probed tcltest constraint (not mere wsl.exe existence) verifies a launchable distro, required tools (bash, coreutils/sha1 tooling), and working one-way staging into the guest's NATIVE filesystem - with all guest-side execution happening on that native filesystem: per-test artifacts are staged into a WSL-native tempdir and results collected back, the Windows checkout is never operated on via the shared /mnt path (DrvFs is slow and cross-boundary stat differences make git re-hash its index and fossil see phantom changes), and any future full-suite mode uses a separate native clone rather than the shared working tree.
Acceptance: a documented probe helper yields a wsl_linux_available constraint whose checks are capability-based (distro launches and answers uname/tool probes; staging into a native tempdir works) and which cannot misfire on wsl.exe-present-but-unusable installs (no distro, WSL1 limitations, broken interop); the currently unix-gated multishell sh-payload execution test runs green via WSL on a suitable machine and still skips cleanly elsewhere; at least one runtime.bash behaviour test (active/use/run resolution against a fixture runtime folder) runs inside WSL - all such tests executing from a native-filesystem staging dir with the shared path used only for one-way copy-in/out; the Windows checkout's git and fossil state is untouched by a WSL-gated run (verifiable: git status/fossil changes identical before and after); suite results on a WSL-less machine are unchanged (skips, not failures); enablement/limitations and the staging pattern recorded in src/tests/AGENTS.md.
## Context

2
src/lib/app-punkscript/punkscript.tcl

@ -1,7 +1,7 @@
package provide app-punkscript 1.0
#Lean one-shot script runner for the punk executable 'script' subcommand (goal G-015).
#
#Contract (G-015 - see goals/G-015-script-subcommand-piped-stdin.md):
#Contract (G-015 - see goals/archive/G-015-script-subcommand-piped-stdin.md):
# - runs a script FILE (first argument, remaining arguments become the script's ::argv)
# or, with no arguments, the whole of piped/redirected stdin as the script.
# - the script interp carries the default punk shell module/alias environment

2
src/tests/shell/testsuites/binscripts/runtimebash_wsl.test

@ -4,7 +4,7 @@ package require tcltest
#driven through WSL from a windows dev machine. The payload had never been executed on
#real unix before this suite - only syntax-checked (bash -n / zsh -n).
#
#Staging design (goals/G-059-wsl-test-driving.md): runtime.bash plus a fixture runtime
#Staging design (goals/archive/G-059-wsl-test-driving.md): runtime.bash plus a fixture runtime
#folder (fake executable "runtimes" that just echo markers) are staged ONE-WAY into a
#tempdir on the distro's NATIVE filesystem and all execution happens there. The windows
#checkout is never operated on from inside WSL, so git/fossil state cannot be disturbed

2
src/tests/testsupport/wslprobe.tcl

@ -10,7 +10,7 @@
# dict: ok 0|1 output <combined stdout+stderr>
# staging_cleanup - rm -rf a staging dir (guarded: only paths staging_create made)
#
#DESIGN (see goals/G-059-wsl-test-driving.md): all guest-side execution happens on the
#DESIGN (see goals/archive/G-059-wsl-test-driving.md): all guest-side execution happens on the
#distro's native filesystem. The shared /mnt path is used only for one-way copy-in of
#artifacts - the windows checkout is never operated on from inside WSL (DrvFs is slow
#and cross-boundary stat differences make git re-hash its index and fossil see phantom

Loading…
Cancel
Save