# G-043 Subshell definition plugins: punk.subshell capability composing commandsets, help topics and config defaults 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 Drafted from the 2026-07-08 code review of the intended plugin facility (conducted while reviewing G-042). The codebase already contains a deliberate three-layer plugin stack, two-thirds built: 1. Discovery/loading + trust boundary (placeholder): punk::mix::init records the intent - "todo: use tcllib pluginmgr to load all modules that provide 'punk.templates'" - blocked on tcllib pluginmgr 0.5 bugs (.tm modules vs packages; bug reports submitted upstream) and on controlling module/library path ordering in its safe interps, with the stated plan "develop punk::pluginmgr to fix these issues". punk::pluginmgr-0.5.1.tm is that development's start: a vendored fork of tcllib pluginmgr (snit type; safe-interp loading; -pattern/-api/-check/-cmds/-setup contract) with one marked divergence (LoadPlugin sources a bare plugin.tcl dropped in a plugin path) and the .tm gap still commented in place. 2. Capability registration/validation (working): punk::cap - in-process, cooperative (its doctools position tcllib pluginmgr as "an alternative which uses safe interpreters"). Providers declare (capname capdict) pairs via a capprovider.registration object; a handler registered for the capability name can veto each declaration (pkg_register) and exposes an api_ object. Handlers are registered by the application (punk::mix::init), deliberately not at package load. punk.templates is the only handled capability. 3. Capability-specific handler APIs (one real instance): punk::cap::handlers::templates, whose pathtype vocabulary (module / adhoc / currentproject* / shellproject* / absolute) is the existing pattern for kit-vs-srctree resource resolution. Commandset composition already exists and is multi-consumer by design: punk::overlay::import_commandset rename-imports a commandset namespace's exported commands under a prefix into a CLI ensemble, explicitly supporting the same commandset serving multiple CLIs simultaneously. Its punk::args documentation records the intent this goal absorbs: "This is part of the punk::mix CLI commandset infrastructure - design in flux. Todo - .toml configuration files for defining CLI configurations." Today's `dev` CLI is composed by hardcoded import_commandset calls in punk::mix::cli::_init (catch-guarded optional sets) - the shape a declarative composition replaces. The user's direction (2026-07-08): support plugins for subshell definitions including help, and plugin commandsets that may apply to particular subshells. ## Approach (sketch - to be refined when activated) Two declaration paths with distinct trust models, one composition mechanism: - Config path (data): stored configuration (G-014 toml) declares a named subshell's composition - which commandset packages to import (with prefix/separator), which help topics (G-042 text topics), which config defaults. Config can only NAME already installed/locatable packages; requiring/importing them is an explicit trust decision equivalent to today's hardcoded init. No code is carried in config (G-042 stance). - Plugin path (code): a provider package declares the punk.subshell capability with a capdict describing what it offers per subshell name (commandset namespace(s) + prefix, help topics via the ::punk::helptopic programmatic API, config defaults). The punk.subshell handler validates shape at registration (veto malformed). Providers not already trusted arrive via punk::pluginmgr discovery/safe-interp loading; in-process registration remains available for providers the app requires directly (the punk::cap norm today). Phases: 1. punk.subshell capability handler (validation + api object) and the composition step at subshell entry/exit: import_commandset into the subshell's command resolution scope, register topics, apply config defaults; restore on quit/switch-away. The restore semantics should be specified to survive the G-002/G-010 non-nested subshell rework (same consideration as G-042's registry save/restore and G-008's state sets). 2. Config-declared composition (G-014-gated) reusing exactly the same composition step. 3. punk::cap housekeeping absorbed here: pkg_unregister completing handler cleanup (api-object lifecycle), and unhandled-capability discoverability (a query or report for declarations that no registered handler accepted - the deliberate punk.isbogus demonstration in punk::mix::templates documents the current silence). 4. punk::pluginmgr integration: .tm loading in the safe interp, controlled module/library path ordering, and the api-contract check wired to the punk.subshell registration. May be deferred as a whole (acceptance allows it) if the fork needs more work than the composition machinery justifies at the time - record remaining gaps here if so. ## Alternatives considered - Hardcoding per-subshell composition in repl init code (status quo pattern from punk::mix::cli::_init) - rejected as the end state: it is exactly what the punk::overlay toml todo and the punk::mix::init pluginmgr todo set out to replace. - Config-only (no provider packages): insufficient - commandsets are code; config can name them but a third-party subshell definition needs a carrier for code plus a trust story, which is the capability + pluginmgr stack's purpose. - Plugin-only (no config path): rejected - the common case (compose existing local commandsets into a project- or task-specific subshell) should not require authoring a provider package; data-only declaration must stay first-class. - Extending tcllib pluginmgr in place upstream rather than forking - upstream reports are filed, but the path-ordering customisation punk needs was already judged (punk::mix::init note) to need punk-side control; the fork exists. Re-converge upstream if tcllib takes the fixes. ## Notes - Dependencies/relations: G-042 (help-topic registry API - consumed by this goal), G-014 (toml stored config - gates the config path), G-009 (themed profiles - a profile is a natural bundle of this goal's composition plus theme/restrictions), G-002/G-010 (non-nested subshells - restore-on-switch semantics), G-003 (restricted subshells - a restricted subshell composing plugin commandsets needs the expose/hide interaction considered), G-033 (visitor binaries - shellproject-anchored resources vs cwd-project resources, same anchor question as the templates pathtypes). - Rough edges catalogued 2026-07-08 (this goal's acceptance covers the first two): punk::cap pkg_unregister "destroy api objects?" review; silent unhandled-capability declarations (punk.isbogus); hardcoded _multivendor whitelist in the templates handler (not this goal's to fix - noted for the templates/layout update-story work under G-012/G-027). - The templates handler's registration-time vs call-time path resolution split (and its documented staleness edge for long-running shells) is the precedent for deciding when a punk.subshell declaration's resources are resolved. - Related review record: goals/G-042-subshell-help-topics.md "Relationship to the intended plugin facility" section (same 2026-07-08 review). - Additional composition aspect (user direction 2026-07-08, recorded via G-044): a subshell definition may also declare repl-behaviour aspects - at minimum a command-completion provider (G-044's provider seam: replace, augment, or disable the default punk::args-driven completer), potentially syntax-related behaviour later. The motivating scenario is a language subshell such as an interactive xtal session (https://tarray.magicsplat.com/xtal_lang.html - alternative syntax translated to Tcl): such a subshell would compose an evaluator/commandset, its own help topics, and a non-Tcl completion provider (or none). An actual xtal session is not owned by this goal - when pursued it becomes its own goal built on this composition.