You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

17 KiB

G-033 proj: package-mode scope prefix: visitor binary resolves dev/src against the cwd's project

Status: achieved 2026-07-28 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; no cd silently 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 (the is_project_root marker) 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 dev machinery (module.templates -> punk::mix::commandset::module -> punk::cap punk.templates plugin 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:... script also benefits), G-027 (if a universal project marker is ever seeded, proj could prefer it over the structural VCS heuristic).
  • G-016 relationship (recorded 2026-07-24 after overlap review; neither file previously referenced the other): G-016's projects.work registry enumeration and this goal's cwd-anchored walk-up are the two project-discovery surfaces (machine registry vs VCS-root walk) - a project findable by one should be findable by the other; both ride punk::repo.
  • G-047 relationship: G-047's primary-VCS resolution anchors at the same is_project_root marker proj: uses; its nested-punkproject.toml rule and this goal's root walk must agree.
  • G-119 relationship (G-119 achieved 2026-07-25 - see goals/archive/G-119-runtime-active-freshness.md): its freshness-verdict contract now sits in bin/AGENTS.md's runtime-manager contract, the same doc this goal's package-mode docs will extend - the mode docs must not contradict it on naming (artifact-named actives, -rN parsing).
  • G-089 relationship (recorded 2026-07-24 after overlap review): G-089's proj: scriptlib resolution rides this goal's visitor root discovery - its Notes already flag that proj:... script benefits; the two share the same root walk and the same never-silent rebind rule.
  • Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md).
  • Related: G-043 references this goal one-directionally (visitor binaries: shellproject-anchored resources vs cwd-project resources) - relationship recorded at activation (2026-07-28 goals_xref survey; the other moderate-score overlaps G-123/G-127/G-131 share only punk_main.tcl/bin co-location, no proj: semantics).

Progress

Implemented and verified 2026-07-28 (activation and achievement same day; punkshell 0.29.0, punk::args::moduledoc::punkexe 0.1.1):

  • Boot chicken-and-egg decision: option (a) - lean inline walk-up in punk_main.tcl using only Tcl builtins, as ::punkboot::proj_root_find (remains defined post-boot for probing, like the G-129 mount procs). It mirrors punk::repo::is_project_root including the is_candidate_root requirements (unwise-path exclusion; ./src plus one of src/modules, src/vfs, src/scriptapps, or punkproject.toml) so boot-time and runtime discovery agree (the G-016 findable-by-both principle). One recorded divergence: .git is accepted as file OR directory (worktree/submodule checkouts), where punk::repo::is_git_root requires a directory.
  • proj: with an invalid mode remainder is not consumed (whole arg passes through as subcommand/script, matching the existing invalid-mode fallthrough); bare proj: is consumed as internal and then warned as having no root-using block.
  • dev under proj: resolves /modules{,_tcl} and /lib{,_tcl} only - the exe-relative and cwd-relative lib candidates of the exe-anchored dev mode are deliberately not mixed in.
  • Verification (punk91 kit rebaked with the new boot; standalone copy of the kit placed in a session scratchpad outside any source tree):
    • punkvisit.exe proj:internal-src script <probe> from c:/repo/jn/tclmodules/tomlish (fossil root, builds no executable): root detected and reported, precedence line reported, tomlish src paths appended after kit-internal paths, and package require tomlish -> 999999.0a1.0 sourced from tomlish/src/modules.
    • Same from C:/repo/jn/shellspy: "src mode: registered 6 #modpod modules" (modpod scanner fed by the visited root) and the punkexe moduledoc loads from the visited project's src/modules at 999999.0a1.0.
    • proj:src variant: tomlish src paths BEFORE kit-internal paths (faithful-vintage ordering).
    • No-project case (cwd = scratchpad): warns and proceeds, script still runs, dev/src unbound.
    • proj:os: warns 'proj:' has no effect (no dev/src block).
    • Exe-relative regression: bin/punk91 src script <probe> unchanged (modpods registered, dev module versions load); piped punk subcommand under proj:internal-src evaluates in the shell environment with the visited project's modules (its exit code 1 on piped exit is pre-existing, present without proj:).
  • packagemode help text is live: the drafted leader text (extended with the proj: wording) sits in punk::args::moduledoc::punkexe 0.1.1 as the packagemode leader of (script)::punkexe (@leaders max raised to 2); rendering/parse wiring remains G-032's lane. The freshly deployed kit still carries moduledoc 0.1.0 - the 0.1.1 stamp reaches kits at the next vfscommonupdate promotion (deliberately not run from a dirty tree in this change-set).
  • Docs: bin/AGENTS.md launch-package-modes section (proj: contract + canonical invocations), src/vfs/AGENTS.md punk_main contract line, ARCHITECTURE.md entry-point bullet; CHANGELOG 0.29.0 entry (minor bump - new launch invocation).