diff --git a/AGENTS.md b/AGENTS.md index 05bc48df..a6e7ae42 100644 --- a/AGENTS.md +++ b/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) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e05df0ef --- /dev/null +++ b/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`. diff --git a/src/AGENTS.md b/src/AGENTS.md index 2a8612ba..d55f55d1 100644 --- a/src/AGENTS.md +++ b/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 diff --git a/src/make.tcl b/src/make.tcl index c7f006ba..7e8a29fb 100644 --- a/src/make.tcl +++ b/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