mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
* fix(reindex): add-only tag reconciliation + DB-only re-chunk preserves frontmatter (#1621) reindex --markdown and re-import no longer wipe DB-side enrichment tags. Tag reconciliation is now ADD-ONLY (import-file.ts): re-import adds current frontmatter tags and never deletes, so auto/dream/signal-detector tags survive. The reindex DB-only fallback reconstructs full markdown via serializeMarkdown so re-chunking a page with no on-disk source preserves frontmatter/title/timeline. * fix(migrations): v0.13.1 grandfather chunked, source-safe, soft-delete-filtered (#1581) phaseCGrandfather rewritten from a per-page getPage+putPage loop (which hung 70+ min on an 82K-page PGLite brain) to a chunked bulk SQL pass keyed on pages.id (NOT slug — slug isn't globally unique), filtering deleted_at IS NULL, with a batched rollback log carrying source identity. * fix(migrations): run schema phases in-process to fix Windows getaddrinfo ENOTFOUND (#1605) The 9 'gbrain init --migrate-only' execSync spawns died on Windows+bun+Supabase (child DNS resolution). runMigrateOnlyCore (extracted from initMigrateOnly) runs the schema bring-up in-process for all engines, unblocking schema_version advancement. Includes async-call-site audit, a wall-clock guard, and a runGbrainSubprocess stderr-capture wrapper for the remaining backfill spawns. * fix(sync): ReDoS hardening + diagnostics for schema-pack regexes (#1569) Input-length cap in runRegexBounded + route the unbounded link-inference path through it (closes the only no-timeout ReDoS hole); star-height lint rule warns on nested-quantifier patterns; --no-schema-pack sync escape hatch; GBRAIN_SYNC_TRACE per-file begin heartbeat; PGLite serve/sync concurrency doc. Defensive hardening + diagnostics — the deterministic ~3100-file wedge root cause remains open (no repro). * docs(todos): file v0.41.37.0 fix-wave follow-ups (#1621/#1605/#1569) * chore: bump version and changelog (v0.41.37.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: sync README + CLAUDE.md for v0.41.37.0 critical fix wave Add reindex add-only tag reconciliation (#1621), v0.13.1 grandfather + Windows in-process migration (#1581/#1605), and schema-pack ReDoS hardening + sync --no-schema-pack / GBRAIN_SYNC_TRACE triage (#1569) to CLAUDE.md key-files annotations and README Troubleshooting. Regenerated llms-full.txt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): bump llms-full.txt budget 700KB→750KB (CLAUDE.md crossed 700KB after master merge) The build-llms size-budget test failed: llms-full.txt is 703,244 bytes after the v0.41.37.0 key-files annotations merged on top of master's v0.41.34/35/36 CLAUDE.md additions. Matches the v0.41.9.0 precedent (600→700); the single-fetch bundle still fits comfortably in modern long-context models. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
3.9 KiB
TypeScript
84 lines
3.9 KiB
TypeScript
/**
|
|
* Tests for the v0.13.0 frontmatter relationship indexing migration.
|
|
*
|
|
* Iron rule (regression guard for Bug 1, v0.14.0 upgrade night): phase
|
|
* handlers must shell out to the bare string `gbrain`, NOT to
|
|
* `process.execPath`. On bun-installed trees execPath is the bun runtime;
|
|
* `bun extract ...` gets interpreted as `bun run extract` and the upgrade
|
|
* crashes mid-migration. The canonical shim on PATH is the right target.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { readFileSync } from 'fs';
|
|
import { join } from 'path';
|
|
|
|
const SRC_PATH = join(__dirname, '..', 'src', 'commands', 'migrations', 'v0_13_0.ts');
|
|
|
|
describe('v0.13.0 — Frontmatter relationship indexing migration', () => {
|
|
test('registered in the TS migration registry', async () => {
|
|
const { migrations, getMigration } = await import('../src/commands/migrations/index.ts');
|
|
const versions = migrations.map(m => m.version);
|
|
expect(versions).toContain('0.13.0');
|
|
const m = getMigration('0.13.0');
|
|
expect(m).not.toBeNull();
|
|
expect(typeof m!.orchestrator).toBe('function');
|
|
});
|
|
|
|
test('phase functions exported for unit testing', async () => {
|
|
const { __testing } = await import('../src/commands/migrations/v0_13_0.ts');
|
|
expect(typeof __testing.phaseASchema).toBe('function');
|
|
expect(typeof __testing.phaseBBackfill).toBe('function');
|
|
expect(typeof __testing.phaseCVerify).toBe('function');
|
|
});
|
|
|
|
test('dry-run skips all side-effect phases', async () => {
|
|
const { v0_13_0 } = await import('../src/commands/migrations/v0_13_0.ts');
|
|
const result = await v0_13_0.orchestrator({ yes: true, dryRun: true, noAutopilotInstall: true });
|
|
expect(result.version).toBe('0.13.0');
|
|
for (const phase of result.phases) {
|
|
expect(phase.status).toBe('skipped');
|
|
expect(phase.detail).toBe('dry-run');
|
|
}
|
|
});
|
|
|
|
// ── Regression guards (Bug 1) ──────────────────────────────
|
|
|
|
test('source does NOT reference process.execPath (Bug 1 regression)', () => {
|
|
// process.execPath on a bun install is the bun runtime itself, so
|
|
// `${process.execPath} extract` becomes `bun run extract` and dies.
|
|
// See v0.14.0 upgrade-night postmortem.
|
|
const src = readFileSync(SRC_PATH, 'utf-8');
|
|
expect(src).not.toContain('process.execPath');
|
|
});
|
|
|
|
test('source does NOT build commands from a GBRAIN constant (Bug 1 regression)', () => {
|
|
// Earlier revisions used `const GBRAIN = process.execPath` and built
|
|
// commands as `${GBRAIN} extract ...`. The constant was the vector.
|
|
const src = readFileSync(SRC_PATH, 'utf-8');
|
|
expect(src).not.toMatch(/const\s+GBRAIN\s*=/);
|
|
expect(src).not.toMatch(/\$\{GBRAIN\}/);
|
|
});
|
|
|
|
test('phases use in-process schema + bare `gbrain` subprocess (v0.41.37.0 #1605)', () => {
|
|
const src = readFileSync(SRC_PATH, 'utf-8');
|
|
// Schema bring-up is now IN-PROCESS (was execSync('gbrain init
|
|
// --migrate-only'), which died with getaddrinfo ENOTFOUND on Windows).
|
|
expect(src).toContain('runMigrateOnlyCore()');
|
|
expect(src).not.toContain("execSync('gbrain init --migrate-only'");
|
|
// Backfill extract goes through the stderr-capturing wrapper (still bare
|
|
// `gbrain` so the canonical shim on PATH wins).
|
|
expect(src).toContain("runGbrainSubprocess('gbrain extract links --source db --include-frontmatter'");
|
|
// Stats readback still shells out (reads stdout); bare gbrain.
|
|
expect(src).toContain("execSync('gbrain call get_stats'");
|
|
});
|
|
|
|
test('phase commands never reference `bun` or `.ts` paths (Bug 1 regression)', () => {
|
|
// Belt-and-suspenders: even if someone reintroduces a runtime-path
|
|
// helper, they must not produce `bun ...` or `<path>.ts` as the spawn
|
|
// target.
|
|
const src = readFileSync(SRC_PATH, 'utf-8');
|
|
expect(src).not.toMatch(/execSync\([^)]*\bbun\b/);
|
|
expect(src).not.toMatch(/execSync\([^)]*\.ts/);
|
|
});
|
|
});
|