Browse Source

project versioning: change-driven bump policy + make.tcl projectversion check

Add a project-level version bumping mechanism for punkshell, tracked in
punkproject.toml, independent of individual module versions.

- root AGENTS.md: new 'Project Versioning' section with change-driven
  SEMVER bump policy (patch/minor/major triggers), CHANGELOG.md requirement,
  advisory enforcement contract, and independence from module versions.
- src/AGENTS.md: cross-reference to root Project Versioning section so
  agents working in src/ discover the rule during their DOX chain walk.
- CHANGELOG.md: new repo-root file with initial 0.2.0 entry; latest
  '## [X.Y.Z]' header must match punkproject.toml version.
- src/make.tcl: new 'projectversion' subcommand — read-only, advisory check
  that warns on CHANGELOG/punkproject.toml version mismatch and on src/
  commits since the last punkproject.toml change. Exempt from bootsupport
  staleness abort (same as 'check'). Self-contained Tcl, no punk deps.

Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
master
Julian Noble 3 weeks ago
parent
commit
717432676f
  1. 30
      AGENTS.md
  2. 10
      CHANGELOG.md
  3. 1
      src/AGENTS.md
  4. 116
      src/make.tcl

30
AGENTS.md

@ -113,6 +113,36 @@ When the user requests a durable behavior change, record it here or in the relev
- `secondary-models` is intentionally omitted (not reliably introspectable). May be added later if a harness surfaces subagent model info.
- The `Assisted-by` trailer describes tooling, not authorship; it does not replace or conflict with the `Co-Authored-By` ban above.
## Project Versioning
The punkshell project version is tracked in `punkproject.toml` (`[project] version`) at the repo root and is independent of individual module versions (those live in `src/modules/*-buildversion.txt` per `src/modules/AGENTS.md` "Versioning And Releases"). The project version describes the user-visible punkshell product, not any single module.
### Bump policy (change-driven)
An agent must bump the `punkproject.toml` version as part of its DOX closeout pass whenever its change ships user-visible shell behaviour. This is change-driven, not release-driven — the version stays honest between releases.
- **Patch** — bug fixes, internal refactors, doc-only updates that ship in a build without changing user-facing shell behaviour.
- **Minor** — new shell commands, new launchers, new default modules visible at the REPL, backward-compatible behaviour additions.
- **Major** — removed commands, changed default behaviour, changed launch invocation, breaking changes to the shell's user-facing contract.
Changes confined to tests, build tooling internals, or non-shipped surfaces do not require a bump. When in doubt, bump patch.
### Changelog
Every version bump must add a corresponding entry to `CHANGELOG.md` at the repo root. The latest `## [X.Y.Z]` header in that file must match the `version` field in `punkproject.toml`. Entries are newest-first, one bullet per notable change.
### Enforcement
`tclsh src/make.tcl projectversion` is a read-only check (never aborts, only warns) that:
1. Verifies the `CHANGELOG.md` latest version header matches `punkproject.toml`.
2. Checks whether `src/` has git commits since the last commit touching `punkproject.toml`; if so, warns that a project-version bump may be overdue.
The check is advisory — it catches forgotten bumps, not hard errors. Agents should run it during closeout for any `src/` change that ships behaviour, alongside existing `make.tcl` verification.
### Relationship to module versions
The project version is fully independent of module versions. A module bump (even a major one) does not force a project bump unless the module's change is itself user-visible at the shell level. Conversely, a project bump does not require bumping any module.
## Child DOX Index
- `src/` — Source tree root; editable source code, build scripts, tests, VFS payloads, vendor deps, docs (see src/AGENTS.md)

10
CHANGELOG.md

@ -0,0 +1,10 @@
# Changelog
All notable changes to the punkshell project version are documented here.
The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.toml`.
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.2.0] - 2026-07-05
- Initial tracking of the punkshell project version in `punkproject.toml`.

1
src/AGENTS.md

@ -79,6 +79,7 @@ Recovery after a wrong path guess:
- When touching VFS payloads, describe regeneration steps in durable docs if the workflow changes.
- When a tool summary reports cleanup issues such as `git diff --check` whitespace errors, verify against exact command output and exit code before running secondary scans or reporting the issue.
- `make.tcl` performs version-aware bootsupport staleness detection: major bumps abort, minor bumps prompt (y/N), patch bumps warn-and-proceed. See `src/bootsupport/AGENTS.md` "Bootsupport Staleness Handling" for the full contract and the version-bump discipline agents must follow.
- Project-level version bumps are governed by the root `AGENTS.md` "Project Versioning" section. Any `src/` change that ships user-visible shell behaviour requires a `punkproject.toml` version bump and a `CHANGELOG.md` entry as part of the DOX closeout pass. Run `tclsh src/make.tcl projectversion` to verify.
## Verification

116
src/make.tcl

@ -22,7 +22,7 @@ namespace eval ::punkboot {
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k]
variable help_flags [list -help --help /? -h]
variable known_commands [list project modules libs packages vfs bin info check shell vendorupdate bootsupport vfscommonupdate ]
variable known_commands [list project modules libs packages vfs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
}
@ -328,6 +328,55 @@ proc ::punkboot::lib::bootsupport_prompt_yesno {prompt} {
return [expr {$answer eq "y" || $answer eq "yes"}]
}
#------------------------------------------------------------------------------
# Project-version helpers self-contained Tcl, no punk package deps.
# Used by the 'projectversion' subcommand to verify that punkproject.toml
# and CHANGELOG.md stay consistent and that the project version is bumped
# when src/ changes ship user-visible behaviour. Advisory only (warns).
#------------------------------------------------------------------------------
# Read the [project] version from a TOML file via a simple line scan.
# Returns the version string (e.g. "0.2.0") or "" if not found.
proc ::punkboot::lib::read_punkproject_version {tomlfile} {
if {![file exists $tomlfile]} {return ""}
set fh [open $tomlfile r]
try {
set content [read $fh]
} finally {
close $fh
}
set in_project 0
foreach line [split $content \n] {
set trimmed [string trim $line]
if {[string index $trimmed 0] eq {[} && [string index $trimmed end] eq {]}} {
set in_project [expr {$trimmed eq "\[project\]"}]
continue
}
if {$in_project && [regexp {^version\s*=\s*"([^"]+)"} $trimmed _ v]} {
return $v
}
}
return ""
}
# Read the latest ## [X.Y.Z] version header from a changelog file.
# Returns the version string or "" if no header found.
proc ::punkboot::lib::read_changelog_latest_version {changelogfile} {
if {![file exists $changelogfile]} {return ""}
set fh [open $changelogfile r]
try {
set content [read $fh]
} finally {
close $fh
}
foreach line [split $content \n] {
if {[regexp {^##\s*\[([0-9][^\]]*)\]} $line _ v]} {
return $v
}
}
return ""
}
if {"::try" ni [info commands ::try]} {
puts stderr "Tcl interpreter possibly too old - need at least tcl 8.6 - 'try' command not found - aborting"
exit 1
@ -1358,6 +1407,8 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - show module/library paths and any potentially problematic packages for running this script" \n
append h " $scriptname shell" \n
append h " - run the punk shell using bootsupport libraries." \n
append h " $scriptname projectversion" \n
append h " - advisory check: verify CHANGELOG.md matches punkproject.toml and warn if src/ has changes since the last project-version bump." \n \n
append h "" \n
if {[llength [dict get $pkg_availability missing]] || [llength [dict get $pkg_availability broken]]} {
set has_recommended 0
@ -1532,7 +1583,7 @@ if {[lsearch $::argv -k] >= 0} {
# - minor bump -> prompt user; backward-compat aliases *should* keep old bootsupport functional
# - patch bump -> warn and proceed (non-breaking)
# 'check' is exempt in all cases the warning is shown at the end of its output instead.
if {[info exists ::punkboot::stale_bootsupport] && $::punkboot::command ne "check"} {
if {[info exists ::punkboot::stale_bootsupport] && $::punkboot::command ne "check" && $::punkboot::command ne "projectversion"} {
set _stale $::punkboot::stale_bootsupport
set _have_major 0
set _have_minor 0
@ -1825,10 +1876,69 @@ if {$::punkboot::command eq "info"} {
exit 0
}
if {$::punkboot::command eq "projectversion"} {
set sep [string repeat - 75]
puts stdout $sep
puts stdout "project version check"
puts stdout $sep
set pp_file [file join $projectroot punkproject.toml]
set cl_file [file join $projectroot CHANGELOG.md]
set src_folder [file join $projectroot src]
set pp_version [::punkboot::lib::read_punkproject_version $pp_file]
set cl_version [::punkboot::lib::read_changelog_latest_version $cl_file]
puts stdout "punkproject.toml version : [expr {[string length $pp_version] ? $pp_version : "(not found)"}]"
puts stdout "CHANGELOG.md version : [expr {[string length $cl_version] ? $cl_version : "(not found)"}]"
# 1. Consistency: CHANGELOG latest header must match punkproject.toml version.
if {![string length $pp_version]} {
puts stderr "WARNING: could not read version from $pp_file"
} elseif {![string length $cl_version]} {
puts stderr "WARNING: no '## [X.Y.Z]' version header found in $cl_file"
} elseif {$pp_version ne $cl_version} {
puts stderr "WARNING: version mismatch punkproject.toml=$pp_version but CHANGELOG.md=$cl_version"
puts stderr " The latest '## [X.Y.Z]' header in CHANGELOG.md must match the version in punkproject.toml."
} else {
puts stdout "consistency: OK (versions match)"
}
if {$::punkboot::command eq "shell"} {
# 2. Staleness: warn if src/ has git commits since the last commit
# touching punkproject.toml (version bump may be overdue).
# Best-effort silently skipped if git is unavailable or the repo
# is not under git revision control.
set pp_commit ""
if {![catch {exec git -C $projectroot log -1 --format=%H -- punkproject.toml} pp_raw]} {
set pp_commit [string trim $pp_raw]
}
if {[string length $pp_commit]} {
if {![catch {exec git -C $projectroot log --oneline ${pp_commit}..HEAD -- src/} src_raw]} {
set src_changes [string trim $src_raw]
if {[string length $src_changes]} {
set n [llength [split $src_changes \n]]
puts stderr "WARNING: $n commit(s) in src/ since last punkproject.toml change a project-version bump may be overdue."
puts stderr " Review the changes and bump punkproject.toml per the root AGENTS.md 'Project Versioning' section."
puts stderr " (showing first 5 commits below)"
set i 0
foreach line [split $src_changes \n] {
if {$i >= 5} break
puts stderr " $line"
incr i
}
} else {
puts stdout "staleness: OK (no src/ changes since last punkproject.toml commit)"
}
} else {
puts stdout "staleness: skipped (git query failed)"
}
} else {
puts stdout "staleness: skipped (punkproject.toml has no git history or git unavailable)"
}
puts stdout $sep
exit 0
}
package require struct::list
package require punk
package require punk::repl

Loading…
Cancel
Save