mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
* v0.31.4.1 chore: align VERSION/package.json with #795 + mandate MAJOR.MINOR.PATCH.MICRO
PR #795 (takes v2) landed on master with `v0.31.4` in its commit subject but
never bumped VERSION, package.json, or CHANGELOG.md. Master shipped at 0.31.3.
This corrective release:
- Bumps VERSION + package.json to 0.31.4.1 (the dot-suffix follow-up channel
documented in CLAUDE.md, so the patch number doesn't churn to 0.31.5)
- Adds the v0.31.4.1 CHANGELOG entry covering takes v2 (lessons from a 100K-take
production extraction), the auth-on-Postgres regression fix, and the new
`gbrain eval takes-quality` CLI surface
- Updates CLAUDE.md to mandate `MAJOR.MINOR.PATCH.MICRO` for every new release.
Historical 3-segment versions in git log + migration filenames stay valid;
do not rewrite. Going forward only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: regenerate llms-full.txt for v0.31.4.1 doc edits
The build-llms regen-drift guard caught that llms-full.txt was stale relative
to the CHANGELOG + CLAUDE.md edits in the prior commit. Per CLAUDE.md the
bundle is auto-derived: bump VERSION/CHANGELOG/CLAUDE.md, then run
`bun run build:llms`. Did the second part now.
llms.txt unchanged (it's just the curated index). Only llms-full.txt picks
up the v0.31.4.1 CHANGELOG entry and the new "Version format is mandatory"
section in CLAUDE.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ci): exclude *.serial.test.ts from test-shard.sh hash buckets
Root cause of test (2) failing on the v0.31.4.1 PR (and on master since
#795 landed): CI's scripts/test-shard.sh hashed every test file into 4
shards via FNV-1a, INCLUDING *.serial.test.ts files. Serial files share
file-wide state (top-level mock.module, module singletons) that's
supposed to be quarantined by the .serial.test.ts naming + local
run-serial-tests.sh running them at --max-concurrency=1.
In CI the quarantine didn't apply. eval-takes-quality-runner.serial.test.ts
(new in #795) hashes into shard 2, where it calls:
mock.module('../src/core/ai/gateway.ts', () => ({
chat: async (opts) => { ... },
configureGateway: () => undefined,
}));
That replaces every export of gateway.ts at module-load time for the
WHOLE shard process. voyage-multimodal.test.ts also lives in shard 2
(both files happen to hash there), and it imports `embedMultimodal` from
gateway.ts. After the serial file loads, `embedMultimodal` is undefined
inside the shard process, and all 18 of voyage-multimodal's
embedMultimodal tests fail. Tests still passed locally because
run-unit-shard.sh excludes .serial files from its parallel pass.
Fix:
- scripts/test-shard.sh: add `-not -name '*.serial.test.ts'` to the
find expression so serial files no longer compete for shard buckets.
Add --dry-run-list flag to mirror run-unit-shard.sh's interface so
the regression test can introspect without spawning bun test.
- .github/workflows/test.yml: add a `bun run test:serial` step that
runs on shard 1 (which already runs `bun run verify`). Uses the
existing scripts/run-serial-tests.sh which invokes bun test at
--max-concurrency=1, matching local behavior.
- test/scripts/test-shard.slow.test.ts: 4 regression cases that pin
the contract (no serial files in any shard, no e2e files in any
shard, plain files partitioned without overlap). .slow.test.ts
because it shells out 4× with pure-bash FNV-1a hashing (~14s
wallclock); excluded from the local fast loop, runs in CI via the
same hash bucketing as other slow tests.
- CLAUDE.md: update the CI vs local divergence section so this
intentional asymmetry is documented going forward.
Build-llms drift in test (1) was fixed in the prior commit (c99a4af1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: regenerate llms-full.txt for the CI-fix CLAUDE.md edits
The prior commit updated the "CI vs local: intentionally divergent file sets"
section in CLAUDE.md, which drifted llms-full.txt. Per CLAUDE.md the bundle
is auto-derived: edit CLAUDE.md, then run `bun run build:llms`. Did the
second part now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
98 lines
2.9 KiB
Bash
Executable File
98 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Partition unit test files into N shards by stable hash and run one shard.
|
|
#
|
|
# Usage: scripts/test-shard.sh <shard-index> <total-shards>
|
|
# shard-index: 1-based (1..N)
|
|
# total-shards: positive integer
|
|
#
|
|
# E2E tests under test/e2e/ are excluded — they need DATABASE_URL and run via
|
|
# bun run test:e2e separately.
|
|
#
|
|
# Stable partitioning: a file's shard is `(hash(path) % N) + 1`. Same file
|
|
# lands in the same shard on every run, regardless of how many other files
|
|
# exist, so retries are reproducible. Hash is FNV-1a — pure shell, no jq.
|
|
set -euo pipefail
|
|
|
|
DRY_RUN_LIST=0
|
|
if [ "${1:-}" = "--dry-run-list" ]; then
|
|
DRY_RUN_LIST=1
|
|
shift
|
|
fi
|
|
|
|
if [ "$#" -ne 2 ]; then
|
|
echo "usage: scripts/test-shard.sh [--dry-run-list] <shard-index> <total-shards>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
SHARD_INDEX="$1"
|
|
TOTAL_SHARDS="$2"
|
|
|
|
if ! [[ "$SHARD_INDEX" =~ ^[0-9]+$ ]] || ! [[ "$TOTAL_SHARDS" =~ ^[0-9]+$ ]]; then
|
|
echo "error: shard index and total must be positive integers" >&2
|
|
exit 1
|
|
fi
|
|
if [ "$SHARD_INDEX" -lt 1 ] || [ "$SHARD_INDEX" -gt "$TOTAL_SHARDS" ]; then
|
|
echo "error: shard index $SHARD_INDEX out of range 1..$TOTAL_SHARDS" >&2
|
|
exit 1
|
|
fi
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
# Find all unit test files, deterministic order. Excludes test/e2e/ and
|
|
# *.serial.test.ts. Serial files share file-wide state (top-level
|
|
# mock.module, module singletons) that leaks across files in the same
|
|
# `bun test` shard process — see scripts/check-test-isolation.sh R2.
|
|
# CI runs them via `bun run test:serial` (scripts/run-serial-tests.sh) at
|
|
# --max-concurrency=1 in a separate step on shard 1. Local `bun run test`
|
|
# already excludes them from the parallel pass and runs them after the
|
|
# same way. Portable: avoid `mapfile` (bash 4+) so this runs on macOS
|
|
# bash 3.2 too.
|
|
FILES=()
|
|
while IFS= read -r line; do
|
|
FILES+=("$line")
|
|
done < <(find test -name '*.test.ts' -not -name '*.serial.test.ts' -not -path 'test/e2e/*' | sort)
|
|
|
|
if [ "${#FILES[@]}" -eq 0 ]; then
|
|
echo "no test files found under test/" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# FNV-1a 32-bit hash of a string — implemented in pure bash so we don't depend
|
|
# on python/openssl/etc on the runner. Output is decimal.
|
|
fnv1a() {
|
|
local str="$1"
|
|
local h=2166136261 # FNV offset basis
|
|
local i ord
|
|
for (( i=0; i<${#str}; i++ )); do
|
|
ord=$(printf '%d' "'${str:$i:1}")
|
|
h=$(( (h ^ ord) & 0xFFFFFFFF ))
|
|
h=$(( (h * 16777619) & 0xFFFFFFFF ))
|
|
done
|
|
echo "$h"
|
|
}
|
|
|
|
SHARD_FILES=()
|
|
for f in "${FILES[@]}"; do
|
|
hash=$(fnv1a "$f")
|
|
bucket=$(( hash % TOTAL_SHARDS + 1 ))
|
|
if [ "$bucket" -eq "$SHARD_INDEX" ]; then
|
|
SHARD_FILES+=("$f")
|
|
fi
|
|
done
|
|
|
|
if [ "$DRY_RUN_LIST" = "1" ]; then
|
|
if [ "${#SHARD_FILES[@]}" -eq 0 ]; then
|
|
exit 0
|
|
fi
|
|
printf '%s\n' "${SHARD_FILES[@]}"
|
|
exit 0
|
|
fi
|
|
|
|
echo "shard $SHARD_INDEX/$TOTAL_SHARDS: ${#SHARD_FILES[@]}/${#FILES[@]} files"
|
|
if [ "${#SHARD_FILES[@]}" -eq 0 ]; then
|
|
echo "warning: shard $SHARD_INDEX has no files (rehash or reduce shard count)" >&2
|
|
exit 0
|
|
fi
|
|
|
|
exec bun test --timeout=60000 "${SHARD_FILES[@]}"
|