feat(recovery): the fold-checkpoint bound — crash folds (checkpoint, head], never the whole log twice
The fold checkpoint (_system/fold-checkpoint.json) is stamped strictly after a canonical-sync barrier over every live entity touched since the last stamp (syncEntityCanonical: ids → canonical paths → fsync; an absent file fsyncs its parent directory so deletes are as durable as writes). An unclean open under log authority now folds only (checkpoint, head]; the chain bootstraps at an empty brain's adoption (three-phase hooks around adoptLogAuthority) or at a brain's first whole-log fold — existing brains converge at their first crash with zero regression. Rollback restores sync immediately; abort paths feed the barrier; a failed barrier retains the old bound (bigger fold later, never a lost write). Five structural pins including boundedness itself. Also: the production-shaped write-flow gate leg (mixed traffic racing flushes, crash mid-traffic, every ack survives — from a consumer-reported gate miss), and two release-ceremony cures (tag-first push so the publish never queues behind the release commit's CI run; raw-curl npmjs shasum probe with propagation grace instead of a one-shot false divergence).
This commit is contained in:
parent
cbe34d115e
commit
ff43de1ada
8 changed files with 695 additions and 12 deletions
|
|
@ -177,8 +177,15 @@ echo -e "${GREEN}✅ Tag created${NC}\n"
|
|||
|
||||
# Step 9: Push to origin — The Source is the one home (ruled 2026-07-23; the
|
||||
# old public GitHub repo is archived history, no longer part of any release).
|
||||
echo -e "${BLUE}8️⃣ Pushing to origin...${NC}"
|
||||
git push --follow-tags origin "$CURRENT_BRANCH"
|
||||
# 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"
|
||||
echo -e "${GREEN}✅ Pushed to origin${NC}\n"
|
||||
|
||||
# Step 10: The home publish (The Source, source.soulcraft.com) is CI's job
|
||||
|
|
@ -227,14 +234,32 @@ npm publish "$SOURCE_TARBALL" --tag "$NPM_TAG" "--@soulcraft:registry=https://re
|
|||
rm -rf "$STOREFRONT_TMP"
|
||||
# Brainy is the only PUBLIC @soulcraft package — verify visibility after every publish.
|
||||
npm access get status @soulcraft/brainy "--@soulcraft:registry=https://registry.npmjs.org/" || true
|
||||
# Verify the pair is byte-identical by registry-reported shasum — divergence here
|
||||
# means the storefront leg must be treated as failed, loudly.
|
||||
# Verify the pair is byte-identical by registry-reported shasum — divergence
|
||||
# here means the storefront leg must be treated as failed, loudly. RETRIED
|
||||
# with raw curl: npmjs metadata propagates with a lag measured in minutes,
|
||||
# and a one-shot npm-view probe fired a false DIVERGENCE on 10.0.0 while a
|
||||
# raw curl of the registry document already confirmed byte-identity. The
|
||||
# probe now reads the registry JSON directly (no npm cache in the path) and
|
||||
# gives propagation up to 5 minutes before calling the pair divergent.
|
||||
NPMJS_VERIFY_ATTEMPTS=20
|
||||
NPMJS_VERIFY_INTERVAL_S=15 # 20 × 15s = 5 minutes of propagation grace
|
||||
SOURCE_SHA=$(npm view "@soulcraft/brainy@${NEW_VERSION}" dist.shasum "--@soulcraft:registry=${SOURCE_NPM_REG}" 2>/dev/null || echo "source-unavailable")
|
||||
NPMJS_SHA=$(npm view "@soulcraft/brainy@${NEW_VERSION}" dist.shasum "--@soulcraft:registry=https://registry.npmjs.org/" 2>/dev/null || echo "npmjs-unavailable")
|
||||
if [ "$SOURCE_SHA" = "$NPMJS_SHA" ]; then
|
||||
PAIR_IDENTICAL=false
|
||||
for ((attempt = 1; attempt <= NPMJS_VERIFY_ATTEMPTS; attempt++)); do
|
||||
NPMJS_SHA=$(curl -fsSL "https://registry.npmjs.org/@soulcraft%2Fbrainy" 2>/dev/null \
|
||||
| node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{const v=JSON.parse(d).versions[process.argv[1]];console.log(v?v.dist.shasum:'')}catch{console.log('')}})" "${NEW_VERSION}" \
|
||||
|| echo "")
|
||||
if [ -n "$NPMJS_SHA" ] && [ "$SOURCE_SHA" = "$NPMJS_SHA" ]; then
|
||||
PAIR_IDENTICAL=true
|
||||
break
|
||||
fi
|
||||
echo -e "${YELLOW} … npmjs metadata not settled (attempt ${attempt}/${NPMJS_VERIFY_ATTEMPTS}: '${NPMJS_SHA:-absent}' vs '${SOURCE_SHA}'); retrying in ${NPMJS_VERIFY_INTERVAL_S}s${NC}"
|
||||
sleep "$NPMJS_VERIFY_INTERVAL_S"
|
||||
done
|
||||
if [ "$PAIR_IDENTICAL" = true ]; then
|
||||
echo -e "${GREEN}✅ Published to npmjs — byte-identical pair (shasum ${NPMJS_SHA})${NC}\n"
|
||||
else
|
||||
echo -e "${RED}❌ REGISTRY DIVERGENCE: The Source shasum ${SOURCE_SHA} != npmjs shasum ${NPMJS_SHA} — investigate before announcing${NC}\n"
|
||||
echo -e "${RED}❌ REGISTRY DIVERGENCE: The Source shasum ${SOURCE_SHA} != npmjs shasum ${NPMJS_SHA} after ${NPMJS_VERIFY_ATTEMPTS} attempts — investigate before announcing${NC}\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue