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
2026-08-27 17:07:09 -07:00
# --source-only is now a no-op: The Source is the one registry, so every
# release already ships Source-only — tag, CI's publish to The Source, the
# release page, and the docs push, with no separate storefront leg to skip.
# The flag is still accepted (for backward-compatible invocations) and just
# prints a notice; it no longer changes behavior.
2026-08-24 10:06:23 -07:00
SOURCE_ONLY = false
2025-10-01 13:51:47 -07:00
for arg in " $@ " ; do
case $arg in
--skip-tests)
SKIP_TESTS = true
; ;
--dry-run)
DRY_RUN = true
; ;
2026-08-24 10:06:23 -07:00
--source-only)
SOURCE_ONLY = true
; ;
2025-10-01 13:51:47 -07:00
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 } "
2026-08-27 17:07:09 -07:00
echo "Usage: ./scripts/release.sh [patch|minor|major|<explicit-version>] [--dry-run] [--source-only (no-op; The Source is the one registry)]"
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
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
2026-08-24 10:06:23 -07:00
if [ " $SOURCE_ONLY " = true ] ; then
2026-08-27 17:07:09 -07:00
echo -e " ${ YELLOW } ⚠️ The Source is the one registry; --source-only is implied ${ NC } "
2026-08-24 10:06:23 -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
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
ci(release): mechanize the releases-wall entry — never hand-written again
Every release used to get its releases/open-brainy.json entry typed by hand
after the fact. scripts/wall-entry.mjs derives it from the CHANGELOG entry
release.sh just composed (headline = first bullet, items = every bullet,
hash stripped) and prepends it, refusing by name on a duplicate version and
validating the whole file's shape + newest-first ordering before and after
it writes.
release.sh now runs it as its own step, between the CHANGELOG update and
the release commit, and stages releases/open-brainy.json into that commit.
The product engine's rail runs this identical script against its own
releases/brainy.json, unchanged — each repo's wall file lives beside the
CHANGELOG it derives from; there is no cross-repo step.
A --check mode validates a wall file's exact key set, field types, and
newest-first ordering with no duplicates, read-only. tests/unit/release/wall-entry.test.ts
covers derivation, prepend, duplicate refusal, and --check's shape/ordering
checks over temp copies — never the real files. --check also runs green
against both releases/open-brainy.json and releases/brainy.json as they
stand today.
2026-09-02 14:15:39 -07:00
RELEASE_DATE = $( date +%Y-%m-%d)
CHANGELOG_ENTRY = " ### [ ${ NEW_VERSION } ](https://source.soulcraft.com/soulcraftlabs/open-brainy/compare/v ${ CURRENT_VERSION } ...v ${ NEW_VERSION } ) ( ${ RELEASE_DATE } )
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 "
ci(release): mechanize the releases-wall entry — never hand-written again
Every release used to get its releases/open-brainy.json entry typed by hand
after the fact. scripts/wall-entry.mjs derives it from the CHANGELOG entry
release.sh just composed (headline = first bullet, items = every bullet,
hash stripped) and prepends it, refusing by name on a duplicate version and
validating the whole file's shape + newest-first ordering before and after
it writes.
release.sh now runs it as its own step, between the CHANGELOG update and
the release commit, and stages releases/open-brainy.json into that commit.
The product engine's rail runs this identical script against its own
releases/brainy.json, unchanged — each repo's wall file lives beside the
CHANGELOG it derives from; there is no cross-repo step.
A --check mode validates a wall file's exact key set, field types, and
newest-first ordering with no duplicates, read-only. tests/unit/release/wall-entry.test.ts
covers derivation, prepend, duplicate refusal, and --check's shape/ordering
checks over temp copies — never the real files. --check also runs green
against both releases/open-brainy.json and releases/brainy.json as they
stand today.
2026-09-02 14:15:39 -07:00
# Step 6b: Update the releases wall entry — mechanical, derived from the
# CHANGELOG entry just composed. The fleet's HQ page reads releases/open-brainy.json
# directly; this used to be hand-written after every release (David: never
# again — make it a step of the rail).
echo -e " ${ BLUE } 5️ ⃣▸ Updating the releases wall... ${ NC } "
node scripts/wall-entry.mjs --product open-brainy --version " ${ NEW_VERSION } " --date " ${ RELEASE_DATE } " --from-changelog CHANGELOG.md
echo -e " ${ GREEN } ✅ Releases wall updated ${ NC } \n "
2025-10-01 13:26:04 -07:00
# Step 7: Create release commit
echo -e " ${ BLUE } 6️ ⃣ Creating release commit... ${ NC } "
ci(release): mechanize the releases-wall entry — never hand-written again
Every release used to get its releases/open-brainy.json entry typed by hand
after the fact. scripts/wall-entry.mjs derives it from the CHANGELOG entry
release.sh just composed (headline = first bullet, items = every bullet,
hash stripped) and prepends it, refusing by name on a duplicate version and
validating the whole file's shape + newest-first ordering before and after
it writes.
release.sh now runs it as its own step, between the CHANGELOG update and
the release commit, and stages releases/open-brainy.json into that commit.
The product engine's rail runs this identical script against its own
releases/brainy.json, unchanged — each repo's wall file lives beside the
CHANGELOG it derives from; there is no cross-repo step.
A --check mode validates a wall file's exact key set, field types, and
newest-first ordering with no duplicates, read-only. tests/unit/release/wall-entry.test.ts
covers derivation, prepend, duplicate refusal, and --check's shape/ordering
checks over temp copies — never the real files. --check also runs green
against both releases/open-brainy.json and releases/brainy.json as they
stand today.
2026-09-02 14:15:39 -07:00
git add package.json package-lock.json CHANGELOG.md releases/open-brainy.json
2025-10-01 13:26:04 -07:00
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-08-12 16:56:08 -07:00
# TAG FIRST, branch second — deliberately two pushes: the runner is
# sequential, and a combined push can queue the release commit's ci.yml run
# AHEAD of the tag's publish-source run (observed on 10.0.0: the publish sat
# ~37 minutes behind a redundant CI run of the very commit the local gates
# had just proven). Pushing the tag alone queues the publish immediately;
# the branch push (and its ci.yml run) follows behind it, harmlessly.
echo -e " ${ BLUE } 8️ ⃣ Pushing to origin (tag first — the publish must never queue behind CI)... ${ NC } "
git push origin " v ${ NEW_VERSION } "
git push 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
2026-08-27 17:07:09 -07:00
# credential anymore; it only waits for CI's result before continuing on to
# the release page and the docs push.
SOURCE_NPM_REG = "https://source.soulcraft.com/api/packages/soulcraftlabs/npm/"
2026-08-04 10:56:21 -07:00
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
2026-08-27 17:07:09 -07:00
LANDED_VERSION = $( npm view " @soulcraftlabs/brainy@ ${ NEW_VERSION } " version " --@soulcraftlabs: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-08-27 17:07:09 -07:00
echo -e " ${ RED } v ${ NEW_VERSION } was tagged and pushed, but @soulcraftlabs/brainy@ ${ NEW_VERSION } never became visible on the ${ NC } "
echo -e " ${ RED } Source registry after ${ SOURCE_POLL_MAX_ATTEMPTS } attempts, ${ SOURCE_POLL_INTERVAL_S } s apart. Aborting. ${ NC } "
2026-07-23 10:01:28 -07:00
exit 1
fi
2025-10-01 13:26:04 -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
2026-08-28 13:00:42 -07:00
if curl -sf -X POST "https://source.soulcraft.com/api/v1/repos/soulcraftlabs/open-brainy/releases" \
2026-07-23 11:09:43 -07:00
-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-08-31 09:30:46 -07:00
# Step 12 RETIRED (2026-08-31, CORTEX-SITE-BRAINY-RENAME round 12, David-ruled):
# soulcraft.com/docs carries the paid product's documentation only. This
# engine's documentation home is THIS repository — README and docs/ — and the
# site serves 301s for the slugs this rail used to push. The push script stays
# in the tree for history; the rail no longer calls it.
echo -e " ${ BLUE } Docs step: this engine documents itself in its own repo (site push retired 2026-08-31) ${ NC } "
2026-07-18 14:02:23 -07:00
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 ""
2026-08-27 17:26:44 -07:00
echo -e " 🏠 The Source: ${ BLUE } https://source.soulcraft.com/soulcraftlabs/open-brainy/releases/tag/v ${ NEW_VERSION } ${ NC } "