Summary
scripts/bump-version.sh silently no-ops on package.json whenever its current version doesn't match version.json. Because the helper that updates auxiliary files uses a grep -q pre-check, the script gives a "✓ Updated N files" success report while skipping package.json entirely. Every bump since the initial drift has compounded the gap.
Current state on main (a0ede7e)
| File |
Version |
version.json |
0.35.31 |
package.json |
0.29.16 |
PM2's process list shows 0.29.16 because PM2 reads version from package.json at process start. The drift is real, not a display artifact.
$ grep '"version"' package.json | head -1
"version": "0.29.16",
$ cat version.json | grep version
"version": "0.35.31",
package.json's version field has not been updated since commit df6a2fc (v0.29.17, Apr 2026). The last 15+ release tags (0.30.x → 0.35.31) ship with package.json frozen.
Root cause
scripts/bump-version.sh lines 100–113:
update_file() {
local file="$1"
local pattern="$2"
local replacement="$3"
local description="$4"
if [ -f "$file" ]; then
if grep -q "$pattern" "$file" 2>/dev/null; then # ← silently skips on miss
_sed_inplace "$file" "s|$pattern|$replacement|g"
echo -e " ${GREEN}✓${NC} $description"
FILES_UPDATED=$((FILES_UPDATED + 1))
fi
fi
}
Called for package.json at lines 125–128 with pattern = "\"version\": \"$CURRENT_VERSION\"". CURRENT_VERSION is read from version.json. If package.json has already drifted, the grep -q fails, the sed never runs, and no warning is printed. The success report (Updated N files) still claims success.
A botched rebase around v0.29.17/0.29.18 set the initial drift; every bump since has silently preserved it.
Why it matters
npm/yarn/PM2/Docker introspection all read package.json — they report stale versions.
- Anyone running
npm publish or building OCI images tags artifacts with the wrong version.
- Operators following the documented
Pull/Build/Restart workflow have no signal that package.json is broken — the script reports success.
CLAUDE.md explicitly documents package.json as one of the files the script "updates ALL version references across":
This script updates ALL version references across the codebase:
version.json (source of truth)
package.json
- …
So behavior diverges from documented contract.
Suggested fix
For canonical version sources that must track version.json regardless of drift (package.json at minimum), force-replace using a regex matching any current version, and log the previous value so drift is visible:
force_set_version() {
local file="$1"
local pattern="$2" # extended regex matching any current version
local replacement="$3" # literal replacement
local description="$4"
if [ -f "$file" ] && grep -Eq "$pattern" "$file" 2>/dev/null; then
local old; old=$(grep -Eo "$pattern" "$file" | head -1)
_sed_inplace "$file" -E "s|$pattern|$replacement|g"
if [ "$old" != "$replacement" ]; then
echo -e " ${GREEN}✓${NC} $description ${YELLOW}(was: $old)${NC}"
else
echo -e " ${GREEN}✓${NC} $description"
fi
FILES_UPDATED=$((FILES_UPDATED + 1))
fi
}
# Replace the package.json call with:
force_set_version "$PROJECT_ROOT/package.json" \
'"version": "[0-9]+\.[0-9]+\.[0-9]+"' \
"\"version\": \"$NEW_VERSION\"" \
"package.json"
Optional hardening: at the top of the script, read package.json's version and warn loudly when it disagrees with version.json before bumping. That way drift never compounds silently again.
Also bump package.json directly in the same fix PR to 0.35.31 (or whatever current is at merge) so the next bump starts from a synced baseline.
Repro
git clone https://github.com/23blocks-OS/ai-maestro.git
cd ai-maestro
./scripts/bump-version.sh patch # reports success
grep '"version"' package.json # still 0.29.16, untouched
Summary
scripts/bump-version.shsilently no-ops onpackage.jsonwhenever its current version doesn't matchversion.json. Because the helper that updates auxiliary files uses agrep -qpre-check, the script gives a "✓ Updated N files" success report while skippingpackage.jsonentirely. Every bump since the initial drift has compounded the gap.Current state on
main(a0ede7e)version.json0.35.31package.json0.29.16PM2's process list shows
0.29.16because PM2 readsversionfrompackage.jsonat process start. The drift is real, not a display artifact.package.json'sversionfield has not been updated since commitdf6a2fc(v0.29.17, Apr 2026). The last 15+ release tags (0.30.x → 0.35.31) ship withpackage.jsonfrozen.Root cause
scripts/bump-version.shlines 100–113:Called for
package.jsonat lines 125–128 withpattern = "\"version\": \"$CURRENT_VERSION\"".CURRENT_VERSIONis read fromversion.json. Ifpackage.jsonhas already drifted, thegrep -qfails, thesednever runs, and no warning is printed. The success report (Updated N files) still claims success.A botched rebase around v0.29.17/0.29.18 set the initial drift; every bump since has silently preserved it.
Why it matters
npm/yarn/PM2/Docker introspection all readpackage.json— they report stale versions.npm publishor building OCI images tags artifacts with the wrong version.Pull/Build/Restartworkflow have no signal thatpackage.jsonis broken — the script reports success.CLAUDE.md explicitly documents
package.jsonas one of the files the script "updates ALL version references across":So behavior diverges from documented contract.
Suggested fix
For canonical version sources that must track
version.jsonregardless of drift (package.jsonat minimum), force-replace using a regex matching any current version, and log the previous value so drift is visible:Optional hardening: at the top of the script, read
package.json's version and warn loudly when it disagrees withversion.jsonbefore bumping. That way drift never compounds silently again.Also bump
package.jsondirectly in the same fix PR to 0.35.31 (or whatever current is at merge) so the next bump starts from a synced baseline.Repro