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>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
7267462311
commit
943e7b9dec
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Regression test: scripts/test-shard.sh exclusion symmetry.
|
||||
*
|
||||
* Pins the contract that CI's hash-bucketed shard script EXCLUDES
|
||||
* *.serial.test.ts and test/e2e/ from every shard. Serial files share
|
||||
* file-wide state (top-level mock.module, module singletons) that leaks
|
||||
* across files in the same `bun test` shard process. Before v0.31.4.1
|
||||
* they were hashed into the same buckets as parallel files, which broke
|
||||
* the quarantine — `eval-takes-quality-runner.serial.test.ts` stubbed
|
||||
* `gateway.ts` and broke every `gateway.embedMultimodal` test in
|
||||
* `voyage-multimodal.test.ts` on shard 2.
|
||||
*
|
||||
* Without this guard, a future refactor that drops the `-not -name
|
||||
* '*.serial.test.ts'` clause from test-shard.sh would silently undo the
|
||||
* fix and re-introduce the contention flake.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll } from 'bun:test';
|
||||
import { execFileSync } from 'child_process';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const REPO_ROOT = resolve(import.meta.dir, '..', '..');
|
||||
const SHARD_SH = resolve(REPO_ROOT, 'scripts/test-shard.sh');
|
||||
|
||||
// Pure-bash FNV-1a per shard takes ~4s on a M-series Mac because the script
|
||||
// computes a hash for every test file. Compute all 4 shards once in beforeAll
|
||||
// and reuse across cases so the suite finishes in one shell-out per shard.
|
||||
const shardCache: Record<number, string[]> = {};
|
||||
|
||||
function dryRunList(shard: number, total: number): string[] {
|
||||
if (shardCache[shard]) return shardCache[shard];
|
||||
const out = execFileSync('bash', [SHARD_SH, '--dry-run-list', String(shard), String(total)], {
|
||||
cwd: REPO_ROOT,
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
shardCache[shard] = out.split('\n').map(s => s.trim()).filter(Boolean);
|
||||
return shardCache[shard];
|
||||
}
|
||||
|
||||
describe('test-shard.sh exclusion symmetry', () => {
|
||||
beforeAll(() => {
|
||||
for (const shard of [1, 2, 3, 4]) dryRunList(shard, 4);
|
||||
}, 60_000);
|
||||
it('includes plain *.test.ts files in at least one shard', () => {
|
||||
const allFiles = [1, 2, 3, 4].flatMap(s => dryRunList(s, 4));
|
||||
expect(allFiles.length).toBeGreaterThan(0);
|
||||
expect(allFiles.some(f => /\.test\.ts$/.test(f) && !/\.serial\.test\.ts$/.test(f))).toBe(true);
|
||||
});
|
||||
|
||||
it('excludes every *.serial.test.ts file from every shard', () => {
|
||||
for (const shard of [1, 2, 3, 4]) {
|
||||
const files = dryRunList(shard, 4);
|
||||
const leaks = files.filter(f => /\.serial\.test\.ts$/.test(f));
|
||||
expect(leaks, `shard ${shard} contains serial files`).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('excludes the test/e2e/ subtree from every shard', () => {
|
||||
for (const shard of [1, 2, 3, 4]) {
|
||||
const files = dryRunList(shard, 4);
|
||||
const leaks = files.filter(f => f.startsWith('test/e2e/'));
|
||||
expect(leaks, `shard ${shard} contains e2e files`).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('partitions plain files across shards without overlap', () => {
|
||||
const seen = new Map<string, number>();
|
||||
for (const shard of [1, 2, 3, 4]) {
|
||||
for (const f of dryRunList(shard, 4)) {
|
||||
if (seen.has(f)) {
|
||||
throw new Error(`file ${f} appears in shard ${seen.get(f)} AND shard ${shard}`);
|
||||
}
|
||||
seen.set(f, shard);
|
||||
}
|
||||
}
|
||||
expect(seen.size).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user