5.4 KiB
G-019 Dependency-scan-driven module trimming for punk-based executables
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) Goal: a package-dependency scan from an executable's entrypoint (candidate basis: the tclparser parse API - c library preferred where present, punk::lib's parity-tested pure-Tcl fallback engaging automatically otherwise, so the scan can run on any tclsh - G-070) 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
Punk executables are currently 'batteries included' - the kit assembly ships the full module snapshot regardless of what a given executable's app actually uses. For executables built around a specific app or entrypoint, a package-dependency scan could determine the module closure genuinely required and let the build ship only that, giving smaller artifacts. Batteries-included builds remain a supported alternative (the middle and far points of the spectrum described in G-018's context section).
The candidate scanning basis is static parsing of Tcl source via the tclparser
parse API (tclpro-descended; https://wiki.tcl-lang.org/page/tclparser). Its
current availability shapes the work:
- The preferred implementation is the c-only tclparser library
(
package require parser); punk::lib uses it opportunistically. - punk::lib's same-API pure-Tcl fallback is now REAL (G-070, achieved
2026-08-02 - see goals/archive/G-070-pure-tcl-tclparser.md):
punk::lib::tclparser_tcldelegates to the punk::tclparser engine module, parity-tested against the c library, andpunk::lib::tclparser_preferauto-dispatches (c preferred, pure-Tcl fallback wired otherwise). Note the covered set is what punk::lib consumes (parse command/getstring/ countnewline) - 'parse expr' and the other subcommands still require the c library. - punk::args::moduledoc::parser documents the parse API.
Using the C library inside build tooling previously made the build depend on a native binary - intersecting the G-004/G-005/G-006 binary-provenance story (zig-built or consent-downloaded, never committed). With G-070 achieved that coupling is gone for the parse-command analysis surface: the scan can run on any tclsh, with the c library an optional performance upgrade.
Approach
- Static scan from the executable's entrypoint/app: walk
package require(andtcl::tm/ source-time load constructs) through the reachable sources, resolving the transitive closure against the project's module trees. - Dynamic requires are the known limit: computed package names, plugin-style
loading, and
package requireinside rarely-hit code paths cannot be proven statically. A declaration mechanism for extras (per-executable manifest of packages to include beyond the scan result) is part of the goal's acceptance, not an afterthought. - Scan output should be a reviewable artifact (the closure list), feeding kit assembly rather than silently filtering it - so a wrong exclusion is diagnosable from the build record.
- Verification of a trimmed kit is behavioural (starts, passes its app's smoke check) plus structural (module listing strictly a subset of the batteries-included equivalent).
Alternatives considered
- Runtime tracing (run the app, record
package requirecalls) instead of static parsing - rejected as primary: coverage-dependent (only exercised paths are seen), though it could later complement the static scan for validating the declared-extras list. - Naive regex scan for
package requirelines - rejected: misses computed names it can't even flag, false-positives in strings/comments; the tclparser parse API exists precisely to do this properly.
Notes
- Related: G-070 (achieved 2026-08-02 - see goals/archive/G-070-pure-tcl-tclparser.md) - supplies this goal's candidate scanning basis without the native-binary coupling: tclparser_tcl is now a working pure-Tcl 'parse' implementation (punk::tclparser engine, tclparser_prefer dispatch), so the scan can run inside build tooling on any tclsh.
- Interacts with G-005/G-006 (how the c-only tclparser binary is provisioned if chosen) and G-018 (the spectrum of executable flavours).
- G-115 relationship (recorded 2026-07-24; G-115 achieved 2026-07-31 - see goals/archive/G-115-declarative-vfs-composition.md): the scan closure plus declared dynamic extras is a payload-determination input the now-live per-.vfs declaration surface (src/vfs/.vfs.toml) can consume - a trimmed kit is then a declared kit with a generated declaration.
- punk::lib sites already noting tclparser use/fallback: tclword_to_scriptlist and related parsing helpers (~lib 3322-3580).