2025-10-01 13:26:04 -07:00
#!/bin/bash
set -e # Exit on error
# Brainy Release Script
# Simple, reliable release workflow: build → test → commit → push → publish → release
# Colors for output
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
NC = '\033[0m' # No Color
# Parse arguments
RELEASE_TYPE = " ${ 1 :- patch } " # patch, minor, or major
2025-10-01 13:51:47 -07:00
SKIP_TESTS = false
DRY_RUN = false
for arg in " $@ " ; do
case $arg in
--skip-tests)
SKIP_TESTS = true
; ;
--dry-run)
DRY_RUN = true
; ;
esac
done
2025-10-01 13:26:04 -07:00
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
# An explicit version (e.g. "8.0.0-rc.1") may be passed in place of patch/minor/major.
EXPLICIT_VERSION = ""
if [ [ " $RELEASE_TYPE " = ~ ^[ 0-9] +\. [ 0-9] +\. [ 0-9] +( -[ 0-9A-Za-z.-] +) ?$ ] ] ; then
EXPLICIT_VERSION = " $RELEASE_TYPE "
fi
# Release on whatever branch we're on — RC lines live on feature branches, not main.
CURRENT_BRANCH = " $( git rev-parse --abbrev-ref HEAD) "
2025-10-01 13:26:04 -07:00
echo -e " ${ BLUE } 🚀 Brainy Release Script ${ NC } "
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
echo -e " ${ BLUE } Release type: ${ RELEASE_TYPE } ${ NC } "
echo -e " ${ BLUE } Branch: ${ CURRENT_BRANCH } ${ NC } \n "
2025-10-01 13:26:04 -07:00
2025-10-01 13:51:47 -07:00
if [ " $DRY_RUN " = true ] ; then
2025-10-01 13:26:04 -07:00
echo -e " ${ YELLOW } ⚠️ DRY RUN MODE - No changes will be made ${ NC } \n "
fi
2025-10-01 13:51:47 -07:00
if [ " $SKIP_TESTS " = true ] ; then
echo -e " ${ YELLOW } ⚠️ SKIPPING TESTS - Use with caution! ${ NC } \n "
fi
2025-10-01 13:26:04 -07:00
# Step 1: Verify clean git state
echo -e " ${ BLUE } 1️ ⃣ Checking git status... ${ NC } "
if [ -n " $( git status --porcelain) " ] ; then
echo -e " ${ RED } ❌ Working directory not clean. Commit or stash changes first. ${ NC } "
exit 1
fi
echo -e " ${ GREEN } ✅ Working directory clean ${ NC } \n "
# Step 2: Build
echo -e " ${ BLUE } 2️ ⃣ Building project... ${ NC } "
2025-10-01 13:51:47 -07:00
if [ " $DRY_RUN " = false ] ; then
2025-10-01 13:26:04 -07:00
npm run build
fi
echo -e " ${ GREEN } ✅ Build successful ${ NC } \n "
# Step 3: Test
2025-10-01 13:51:47 -07:00
if [ " $SKIP_TESTS " = false ] ; then
echo -e " ${ BLUE } 3️ ⃣ Running tests... ${ NC } "
if [ " $DRY_RUN " = false ] ; then
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
# Full CI gate: unit + integration (test:ci = test:ci-unit && test:ci-integration).
npm run test:ci
2025-10-01 13:51:47 -07:00
fi
echo -e " ${ GREEN } ✅ Tests passed ${ NC } \n "
else
echo -e " ${ YELLOW } 3️ ⃣ Skipping tests... ${ NC } \n "
2025-10-01 13:26:04 -07:00
fi
# Step 4: Get current and new version
CURRENT_VERSION = $( node -p "require('./package.json').version" )
echo -e " ${ BLUE } Current version: ${ CURRENT_VERSION } ${ NC } "
# Calculate new version
IFS = '.' read -r -a VERSION_PARTS <<< " $CURRENT_VERSION "
MAJOR = " ${ VERSION_PARTS [0] } "
MINOR = " ${ VERSION_PARTS [1] } "
PATCH = " ${ VERSION_PARTS [2] } "
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
if [ -n " $EXPLICIT_VERSION " ] ; then
NEW_VERSION = " $EXPLICIT_VERSION "
else
case $RELEASE_TYPE in
major)
NEW_VERSION = " $(( MAJOR + 1 )) .0.0 "
; ;
minor)
NEW_VERSION = " ${ MAJOR } . $(( MINOR + 1 )) .0 "
; ;
patch)
NEW_VERSION = " ${ MAJOR } . ${ MINOR } . $(( PATCH + 1 )) "
; ;
*)
echo -e " ${ RED } ❌ Invalid release type: ${ RELEASE_TYPE } ${ NC } "
echo "Usage: ./scripts/release.sh [patch|minor|major|<explicit-version>] [--dry-run]"
exit 1
; ;
esac
fi
# A version with a hyphen (e.g. 8.0.0-rc.1) is a prerelease: publish under the 'rc'
# dist-tag (NOT 'latest') and mark the GitHub release as a prerelease.
PRERELEASE = false
NPM_TAG = "latest"
if [ [ " $NEW_VERSION " = = *"-" * ] ] ; then
PRERELEASE = true
NPM_TAG = "rc"
fi
echo -e " ${ BLUE } New version: ${ NEW_VERSION } ${ NC } "
if [ " $PRERELEASE " = true ] ; then
echo -e " ${ YELLOW } ⚠️ Prerelease → npm dist-tag ' ${ NPM_TAG } ', GitHub prerelease ${ NC } "
fi
echo ""
2025-10-01 13:26:04 -07:00
2025-10-01 13:51:47 -07:00
if [ " $DRY_RUN " = true ] ; then
2025-10-01 13:26:04 -07:00
echo -e " ${ YELLOW } DRY RUN: Would release version ${ NEW_VERSION } ${ NC } "
exit 0
fi
# Step 5: Bump version in package files
echo -e " ${ BLUE } 4️ ⃣ Bumping version to ${ NEW_VERSION } ... ${ NC } "
npm version $NEW_VERSION --no-git-tag-version
echo -e " ${ GREEN } ✅ Version bumped ${ NC } \n "
# Step 6: Update CHANGELOG
echo -e " ${ BLUE } 5️ ⃣ Updating CHANGELOG... ${ NC } "
# Get commits since last tag
LAST_TAG = $( git describe --tags --abbrev= 0 2>/dev/null || echo "" )
if [ -z " $LAST_TAG " ] ; then
COMMITS = $( git log --oneline --pretty= format:"- %s (%h)" )
else
COMMITS = $( git log ${ LAST_TAG } ..HEAD --oneline --pretty= format:"- %s (%h)" )
fi
# Create new changelog entry
2026-07-23 11:09:43 -07:00
CHANGELOG_ENTRY = " ### [ ${ NEW_VERSION } ](https://source.soulcraft.com/soulcraft/brainy/compare/v ${ CURRENT_VERSION } ...v ${ NEW_VERSION } ) ( $( date +%Y-%m-%d) )
2025-10-01 13:26:04 -07:00
${ COMMITS }
"
# Prepend to CHANGELOG.md after header
if [ -f "CHANGELOG.md" ] ; then
# Read header (first 4 lines)
HEADER = $( head -n 4 CHANGELOG.md)
# Read rest of file
REST = $( tail -n +5 CHANGELOG.md)
# Write new CHANGELOG
echo " $HEADER " > CHANGELOG.md
echo "" >> CHANGELOG.md
echo " $CHANGELOG_ENTRY " >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo " $REST " >> CHANGELOG.md
fi
echo -e " ${ GREEN } ✅ CHANGELOG updated ${ NC } \n "
# Step 7: Create release commit
echo -e " ${ BLUE } 6️ ⃣ Creating release commit... ${ NC } "
git add package.json package-lock.json CHANGELOG.md
git commit -m " chore(release): ${ NEW_VERSION } "
echo -e " ${ GREEN } ✅ Release commit created ${ NC } \n "
# Step 8: Create git tag
2026-05-26 11:44:14 -07:00
# Annotated (-a) so `git push --follow-tags` below actually pushes it. A lightweight tag is
# skipped by --follow-tags, which leaves the tag local-only and makes `gh release create` fail.
2025-10-01 13:26:04 -07:00
echo -e " ${ BLUE } 7️ ⃣ Creating git tag v ${ NEW_VERSION } ... ${ NC } "
2026-05-26 11:44:14 -07:00
git tag -a " v ${ NEW_VERSION } " -m " Release v ${ NEW_VERSION } "
2025-10-01 13:26:04 -07:00
echo -e " ${ GREEN } ✅ Tag created ${ NC } \n "
2026-08-04 10:56:21 -07:00
# Step 9: Push to origin — The Source is the one home (ruled 2026-07-23; the
2026-07-23 11:09:43 -07:00
# old public GitHub repo is archived history, no longer part of any release).
2026-07-23 10:01:28 -07:00
echo -e " ${ BLUE } 8️ ⃣ Pushing to origin... ${ NC } "
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
git push --follow-tags origin " $CURRENT_BRANCH "
2026-07-23 10:01:28 -07:00
echo -e " ${ GREEN } ✅ Pushed to origin ${ NC } \n "
2026-08-04 10:56:21 -07:00
# Step 10: The home publish (The Source, source.soulcraft.com) is CI's job
# now, not the laptop's — a tag push (just above) triggers
# .forgejo/workflows/publish-source.yml, which builds and publishes on The
# Source's own runner (datacenter-side: seconds, not the laptop's WAN timing
# out on an 87MB tarball PUT). The laptop holds no home-registry publish
# credential anymore; it only waits for CI's result before trusting the
# home/npmjs pair enough to publish the storefront leg.
SOURCE_NPM_REG = "https://source.soulcraft.com/api/packages/soulcraft/npm/"
SOURCE_POLL_INTERVAL_S = 15
SOURCE_POLL_MAX_ATTEMPTS = 200 # 200 × 15s = 50 minutes — the runner is sequential and a busy day's ci.yml
2026-08-04 10:14:46 -07:00
# backlog has twice exceeded the old 20-minute window (8.10.3, 9.0.0);
# ci.yml no longer runs on tag pushes, but same-day branch pushes still queue ahead
2026-08-04 10:56:21 -07:00
echo -e " ${ BLUE } 9️ ⃣ Waiting for CI to publish v ${ NEW_VERSION } to The Source registry (home)... ${ NC } "
SOURCE_LANDED = false
for ( ( attempt = 1; attempt <= SOURCE_POLL_MAX_ATTEMPTS; attempt++) ) ; do
LANDED_VERSION = $( npm view " @soulcraft/brainy@ ${ NEW_VERSION } " version " --@soulcraft:registry= ${ SOURCE_NPM_REG } " 2>/dev/null || echo "" )
2026-07-27 11:11:53 -07:00
if [ " $LANDED_VERSION " = " $NEW_VERSION " ] ; then
2026-08-04 10:56:21 -07:00
SOURCE_LANDED = true
2026-07-27 11:11:53 -07:00
break
2026-07-23 11:09:43 -07:00
fi
2026-08-04 10:56:21 -07:00
echo -e " ${ YELLOW } … not yet on The Source (attempt ${ attempt } / ${ SOURCE_POLL_MAX_ATTEMPTS } ); retrying in ${ SOURCE_POLL_INTERVAL_S } s ${ NC } "
sleep " $SOURCE_POLL_INTERVAL_S "
2026-07-27 11:11:53 -07:00
done
2026-08-04 10:56:21 -07:00
if [ " $SOURCE_LANDED " = true ] ; then
echo -e " ${ GREEN } ✅ CI published v ${ NEW_VERSION } to The Source ${ NC } \n "
2026-07-23 11:09:43 -07:00
else
2026-08-04 10:56:21 -07:00
echo -e " ${ RED } ❌ CI's home publish did not land — check the workflow run on The Source; the pair must not diverge. ${ NC } "
2026-07-27 11:11:53 -07:00
echo -e " ${ RED } v ${ NEW_VERSION } was tagged and pushed, but @soulcraft/brainy@ ${ NEW_VERSION } never became visible on the ${ NC } "
2026-08-04 10:56:21 -07:00
echo -e " ${ RED } Source registry after ${ SOURCE_POLL_MAX_ATTEMPTS } attempts, ${ SOURCE_POLL_INTERVAL_S } s apart. Aborting before npmjs. ${ NC } "
2026-07-23 10:01:28 -07:00
exit 1
fi
2025-10-01 13:26:04 -07:00
2026-07-23 11:09:43 -07:00
echo -e " ${ BLUE } 9️ ⃣½ Publishing to npmjs (storefront, dist-tag: ${ NPM_TAG } )... ${ NC } "
2026-08-04 08:17:02 -07:00
# BYTE-IDENTITY LAW: the storefront republishes CI's EXACT artifact — download
2026-08-04 10:56:21 -07:00
# the tarball The Source serves and publish that file, never a fresh local pack
2026-08-04 08:17:02 -07:00
# (a local rebuild can differ byte-wise, and the fleet verifies the pair by
# shasum across registries).
STOREFRONT_TMP = " $( mktemp -d) "
2026-08-04 10:56:21 -07:00
( cd " $STOREFRONT_TMP " && npm pack " @soulcraft/brainy@ ${ NEW_VERSION } " " --@soulcraft:registry= ${ SOURCE_NPM_REG } " >/dev/null)
SOURCE_TARBALL = " $( ls " $STOREFRONT_TMP " /soulcraft-brainy-*.tgz) "
echo -e " ${ BLUE } home artifact: $( sha256sum " $SOURCE_TARBALL " | cut -d' ' -f1) ${ NC } "
npm publish " $SOURCE_TARBALL " --tag " $NPM_TAG " "--@soulcraft:registry=https://registry.npmjs.org/"
2026-08-04 08:17:02 -07:00
rm -rf " $STOREFRONT_TMP "
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
# Brainy is the only PUBLIC @soulcraft package — verify visibility after every publish.
2026-07-23 11:09:43 -07:00
npm access get status @soulcraft/brainy "--@soulcraft:registry=https://registry.npmjs.org/" || true
2026-08-04 08:17:02 -07:00
# Verify the pair is byte-identical by registry-reported shasum — divergence here
# means the storefront leg must be treated as failed, loudly.
2026-08-04 10:56:21 -07:00
SOURCE_SHA = $( npm view " @soulcraft/brainy@ ${ NEW_VERSION } " dist.shasum " --@soulcraft:registry= ${ SOURCE_NPM_REG } " 2>/dev/null || echo "source-unavailable" )
2026-08-04 08:17:02 -07:00
NPMJS_SHA = $( npm view " @soulcraft/brainy@ ${ NEW_VERSION } " dist.shasum "--@soulcraft:registry=https://registry.npmjs.org/" 2>/dev/null || echo "npmjs-unavailable" )
2026-08-04 10:56:21 -07:00
if [ " $SOURCE_SHA " = " $NPMJS_SHA " ] ; then
2026-08-04 08:17:02 -07:00
echo -e " ${ GREEN } ✅ Published to npmjs — byte-identical pair (shasum ${ NPMJS_SHA } ) ${ NC } \n "
else
2026-08-04 10:56:21 -07:00
echo -e " ${ RED } ❌ REGISTRY DIVERGENCE: The Source shasum ${ SOURCE_SHA } != npmjs shasum ${ NPMJS_SHA } — investigate before announcing ${ NC } \n "
2026-08-04 08:17:02 -07:00
exit 1
fi
2026-07-23 11:09:43 -07:00
2026-08-04 10:56:21 -07:00
# Step 11: Release object on The Source (presentational — the tag, CHANGELOG,
# and RELEASES.md are the record; this just gives The Source's UI a release page).
echo -e " ${ BLUE } 🔟 Creating release page on The Source... ${ NC } "
2026-07-23 11:09:43 -07:00
if [ -n " ${ FORGEJO_RELEASE_TOKEN :- } " ] ; then
if curl -sf -X POST "https://source.soulcraft.com/api/v1/repos/soulcraft/brainy/releases" \
-H " Authorization: token ${ FORGEJO_RELEASE_TOKEN } " -H "Content-Type: application/json" \
-d " {\"tag_name\":\"v ${ NEW_VERSION } \",\"name\":\"v ${ NEW_VERSION } \",\"prerelease\": ${ PRERELEASE } } " >/dev/null; then
2026-08-04 10:56:21 -07:00
echo -e " ${ GREEN } ✅ Release page created on The Source ${ NC } \n "
2026-07-23 11:09:43 -07:00
else
2026-08-04 10:56:21 -07:00
echo -e " ${ RED } ⚠️ Release-page API call failed — tag + CHANGELOG remain the record; create the page via The Source's UI if wanted ${ NC } \n "
2026-07-23 11:09:43 -07:00
fi
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
else
2026-07-23 11:09:43 -07:00
echo -e " ${ RED } ⚠️ FORGEJO_RELEASE_TOKEN unset — no release page created; tag + CHANGELOG remain the record ${ NC } \n "
feat(8.0): id-normalization (#18) + aggregation min/max delete-safety + RC-safe release
- id-normalization (#18): `brain.newId()` (UUID v7) + v7 default ids; non-UUID string ids are
transparently normalized to a stable UUID v5 on creation AND every lookup — get/update/remove,
relate(from,to), related, find({connected}), getMany, removeMany, the transact op-handler, and
db.with() overlays — with the caller's original key preserved under `_originalId`. The engine only
ever sees a UUID; real UUIDs pass through untouched. universal/uuid.ts gains v5/v7/isUUID +
BRAINY_ID_NAMESPACE; new utils/idNormalization.ts (coerceNewEntityId / resolveEntityId).
- aggregation min/max delete-safety: `queryAggregate` no longer leaves a stale min/max after
deleting the current extreme — min/max now track a value multiset and recompute in-memory (no
entity scan; that scan was the prior delete-then-hang a consumer reported). Other metrics were
already exact across deletes. Regression test proves resolve-after-delete + correct new min/max.
- release.sh RC-safe: explicit version arg, prerelease detection (npm `--tag rc` + GitHub
`--prerelease`), full `test:ci` gate (unit + integration), current-branch push, npm-access
verification after publish.
- native-engine references point at `@soulcraft/cor` (8.0's partner) in user-facing strings/docs.
Unit 1461/0 · integration 599/0 · tsc clean.
2026-06-20 14:40:57 -07:00
fi
2025-10-01 13:26:04 -07:00
2026-07-18 14:02:23 -07:00
# Step 12: Push public docs to the soulcraft.com docs ingest door
# (VENUE-DOCS-RELEASE-PUSH). Skips with a loud warning when
# DOCS_INGEST_SECRET is unset; fails loudly (without undoing the publish —
# that already happened) when a push errors, so the docs site never
# silently trails npm.
echo -e " ${ BLUE } 1️ ⃣2️ ⃣ Pushing public docs to soulcraft.com/docs... ${ NC } "
if node scripts/push-docs.js; then
echo -e " ${ GREEN } ✅ Docs push step done ${ NC } \n "
else
echo -e " ${ RED } ❌ Docs push FAILED — soulcraft.com/docs trails npm until re-run or interim sync ${ NC } \n "
fi
2025-10-01 13:26:04 -07:00
echo -e " ${ GREEN } ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ${ NC } "
echo -e " ${ GREEN } 🎉 Release ${ NEW_VERSION } complete! ${ NC } "
echo -e " ${ GREEN } ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ${ NC } "
echo ""
echo -e " 📦 npm: ${ BLUE } https://www.npmjs.com/package/@soulcraft/brainy/v/ ${ NEW_VERSION } ${ NC } "
2026-08-04 10:56:21 -07:00
echo -e " 🏠 The Source: ${ BLUE } https://source.soulcraft.com/soulcraft/brainy/releases/tag/v ${ NEW_VERSION } ${ NC } "