12 KiB
G-033 proj: package-mode scope prefix: visitor binary resolves dev/src against the cwd's project
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)
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
The earlier intent (recalled 2026-07-07): an installed punkshell binary sitting
in PATH - possibly a downloaded binary, not built from any local source tree -
should be able to detect when the cwd is inside a punkshell project and set up
the appropriate source/dev library resolution for it, so the user can explore
that project interactively via the shell subcommand. The project being worked
on might not build a shell-capable binary, or might build no executable at all
(e.g. a modules-only project).
History check: this was never active in the full sense. src mode is new
(checkin 3d2d098d) and discovers the project root exe-relative (backtrack
from the executable's bin/); dev is likewise exe-relative; os is the only
cwd-relative piece and only shallowly (adds [pwd] and cwd-relative
modules/modules_tcl<N> ad-hoc .tm sources - no walk-up, no src/
structure). The comments in punk_main.tcl name "the info nameofexecutable or
info script or pwd relative paths" as design axes, so the intent seeded os
mode, but cwd project-structure detection was never built into the boot modes.
The cwd-relative detector already exists at the runtime layer:
punk::repo::find_project walks up from cwd via is_project_root, and dev
commands (module.templates, module.new, ...) already use it. The marker
question is answered: is_project_root defines a project as a VCS repo root
(git or fossil) that is not an excluded location - deliberately NOT relying on
punkproject.toml (its own comment notes that file also appears in layout
templates; tomlish has no punkproject.toml yet but IS a fossil root). This goal
aligns the boot-time package modes with that existing runtime detection.
Syntax: scope prefix, not a fifth token (decided 2026-07-07)
The existing modes (internal/dev/src/os) are ordered path-source blocks:
punk_main assembles new_tm_path token-by-token in dash-string order, and
because the head of tcl:™️:list wins same-version ties (rules verified
2026-07-06), earlier blocks win same-version collisions - deliberately so
(punk_main comment: "the lib/module paths based on the project being run should
take preference if 'dev' is earlier in the list, even if the version number is
the same"). The dash-list is the user's tie-break dial.
proj answers a different question - WHICH project root dev/src resolve
against - so it has no meaningful position in that ordering; a positional token
(os-proj-src-internal) reads as noise because it is a category error. Decided
syntax:
[proj:]<block>[-<block>...] e.g. proj:internal-src
- The
proj:prefix scopes root discovery to cwd-walk-up; everything after the colon is the unchanged ordered block string with unchanged semantics. - A
proj:string containing no root-using block (dev/src) warns that proj has no effect (os/internal do not reference a project root). - Parser impact is small: recognise an optional
proj:prefix on arg 0, strip it, then validate dash-tokens as today. Explicit prefix = opt-in; nocdsilently rebinds a normal launch. - Rejected: automatic cwd-detection without a token (silent rebind surprise);
an explicit -flag route (less convenient for everyday use - user decision);
a token-attached qualifier like
src@proj(binds the root choice to a precedence slot; the root choice is launch-wide).
Same-version shadowing: why kit-first is the canonical default (decided 2026-07-07)
Order only decides exact-version ties. The project's own code is unaffected by the choice: dev-versioned modules (999999.0a1.0, package prefer latest) and higher-versioned modules load from the project in either order, and anything absent from the kit has no tie so the project supplies it. The decision governs only same-version-different-content drift - rare within one lineage (punkshell version-bump discipline, G-026 provenance), but structurally expected in visitor mode, where the binary and the visited project's bootsupport/ vendormodules snapshots are different vintages by definition.
Failure modes are asymmetric:
- Project-first (
proj:src): faithful to the project's snapshot vintage (reproduces its bugs), but the visiting shell's own machinery loads the project's older copies - a stale project can degrade the visiting shell (the 2026-07-06 stale-textblock/shellfilter silent-stdout class, imported). - Kit-first (
proj:internal-src): the binary's tested infrastructure survives ties; robust session; drifted same-version project copies are silently masked (less faithful, invisible when it matters).
Canonical documented invocation: proj:internal-src (kit-first). The
project's genuinely-own code wins in either order; ties involve infrastructure
snapshots nobody explores; and for a standalone downloaded binary the kit is
its only trustworthy lineage. proj:src is documented as the faithful-vintage
variant for reproducing a project's behaviour exactly. No special-casing: both
fall out of the existing ordering semantics (explicit internal placement),
preserving one uniform mental model.
Standalone-binary semantics (downloaded, installed on PATH, no source tree):
internal= the kit's own vfs contents - location-independent; kit-first needs nothing from any source tree.- plain
src/dev(exe-relative) find no project and degrade to warned no-ops (existing behaviour) -proj:is precisely what gives such a binary a project context. - Trimmed kits (G-019) compose gracefully: kit-first only arbitrates ties, so modules missing from a trimmed kit still flow from the visited project.
- Note for docs: installing a binary INTO some project's bin/ makes exe-relative modes adopt that project (by design - adoption by location).
Required boot notice
A proj: launch must report the detected project root and the effective path
precedence (and warn-and-proceed when no project is found walking up from cwd)
- the rebind must never be silent. Candidate (non-acceptance) hardening: a shadow-report diagnostic listing name+version collisions between kit-internal and project paths, pairing naturally with G-025's buildinfo.
packagemode help text (source draft for G-032)
This is the designated source text for the packagemode leader in the launcher's punk::args definition (G-032 renders it; making it live is part of THIS goal's implementation - live help must not describe unimplemented behaviour, so it ships with the prefix, not before):
packagemode -optional 1 -default internal -help
"Package-source mode: an ordered dash-separated list of path blocks,
optionally scoped with the 'proj:' prefix.
Blocks (each adds a group of module/library paths):
internal - paths inside the executable's kit (always included;
appended last unless listed explicitly)
dev - the project's built output (<projectroot>/modules ...)
src - the project's unbuilt source (src/modules,
src/bootsupport/modules, src/vendormodules, src/lib)
os - ad-hoc .tm paths from the current directory/environment
Without 'proj:', dev/src resolve against the executable's own project
(executable in <projectroot>/bin). With 'proj:', they resolve against
the nearest project root at or above the current directory (a git or
fossil repository root) - for an installed punkshell binary visiting
a project that builds no shell of its own.
ORDER MATTERS: earlier blocks win when the same version of a module
exists in more than one location.
Examples:
src work on the executable's own project source
proj:internal-src explore the cwd project; the executable's own
copies win version ties (robust visitor default)
proj:src explore the cwd project; ITS copies win version
ties (faithful to the project's snapshot vintage)
The launch reports the detected project root and effective precedence."
Approach
- On
proj:: walk up from[pwd]to the nearest VCS repo root (theis_project_rootmarker) and feed that root into the existing src-mode path machinery (already parameterised on a root variable - adds src/modules, src/modules_tcl, src/bootsupport/modules, src/vendormodules to the tm path, src/lib to auto_path, registers #modpod modules, sets package prefer latest). Only the discovery is new; the setup is reused. - Boot chicken-and-egg: find_project/is_project_root live in punk::repo, not loadable at the early package-mode phase. Options, decision to record: (a) lean inline VCS-root walk-up in punk_main using only Tcl builtins (fossil: FOSSIL/.fslckout/.fos; git: .git) - matches how the src-mode #modpod scan already inlines logic at boot; (b) defer proj setup to a post-boot phase where punk::repo is available.
Notes
- The related
devmachinery (module.templates -> punk::mix::commandset::module -> punk::cappunk.templatesplugin handlers) uses find_project and has unpolished plugin issues, plus an 8.6 shell-context zip-modpod-mount failure (G-034). This goal deliberately does NOT gate on synchronising with that machinery - it only aligns with the find_project detection concept. - Related: G-018 (the installable generic/plain binary this serves), G-019
(trimmed kits borrow from the visited project gracefully under kit-first),
G-031 (boot core is where proj discovery belongs; the template
project_main.tcl lacks even src mode - fork drift), G-032 (renders the
packagemode help text above), G-015 (
proj:... scriptalso benefits), G-027 (if a universal project marker is ever seeded, proj could prefer it over the structural VCS heuristic). - Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md).