mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
* fix(minions): self-identifying RSS watchdog + cgroup-aware default + pooler-reap self-heal (#1678) Problem 1: distinct WORKER_EXIT_RSS_WATCHDOG exit code + cause-keyed supervisor breaker (bypasses the stable-run reset that hid the 400x/24h loop) + rss_watchdog audit bucket + 80% soft-warn; cgroup-aware resolveDefaultMaxRssMb replaces the flat 2048 default at every spawn site. Problem 2: CONNECTION_ENDED classified retryable; postgres-engine sql getter throws a retryable error on a reaped instance pool instead of the misleading module-singleton fallthrough; promoteDelayed reconnect-retry; claim recovers on the next poll tick (no double-claim); lock-renewal tick reconnect-once dep. * feat(cycle): surface silent extract_atoms backlog + bounded --drain + fix lint clobbering the shared DB connection (#1678) Problem 3: extract_atoms_backlog doctor check + pack_gated skip marker + shared countExtractAtomsBacklog; `gbrain dream --phase extract_atoms --drain [--window N]` single-hold bounded drain (same cycleLockIdFor, rediscover each batch, reports remaining, exits non-zero while work remains). Also fixes a real production bug found via E2E: the cycle lint phase's resolveLintContentSanity created + disconnected a module-style engine that nulled the shared db singleton mid-cycle, breaking every later phase with "connect() has not been called". Lint now reuses the caller's live engine (cycle + Minion handlers thread it; standalone CLI keeps the create-own path). * chore: bump version and changelog (v0.41.39.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#1678): pre-landing review — route transaction/withReservedConnection through the sql getter + drain treats failed count as incomplete Codex adversarial review findings: - #2: transaction(), withReservedConnection(), and one other site bypassed the v0.42.2.0 sql-getter self-heal via `this._sql || db.getConnection()`, so a reaped instance pool fell through to the module singleton there. Route all three through `this.sql` so they throw the retryable instance-pool error and recover consistently (MinionQueue.transaction hits this). - #4: `gbrain dream --drain` treated a null backlog count (query failure) as success via `remaining ?? 0`; now null exits EXIT_DRAIN_INCOMPLETE so automation never believes an unverified backlog drained. - #1 (claim orphan) + #3 (PGLite drain lock) documented as follow-ups in TODOS. * docs: document v0.42.2.0 #1678 modules + behavior in CLAUDE.md Adds Key Files entries for worker-exit-codes.ts, rss-default.ts, and extract-atoms-drain.ts, plus v0.42.2.0 annotations on worker.ts, child-worker-supervisor.ts, lock-renewal-tick.ts, and dream.ts. Regenerated llms-full.txt to match (test/build-llms.test.ts gate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: re-version v0.42.2.0 → v0.42.5.0 across VERSION/package.json/CHANGELOG/docs/comments Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
133 lines
4.9 KiB
TypeScript
133 lines
4.9 KiB
TypeScript
/**
|
|
* Structural tests for `gbrain dream` argv parsing (v0.21).
|
|
*
|
|
* Verifies the help text + parser source contains the new flags
|
|
* (--input, --date, --from, --to) and that conflict detection is wired.
|
|
* The actual parseArgs is internal; we exercise it via the source file
|
|
* structure to avoid spinning up a process per test.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { readFileSync } from 'fs';
|
|
|
|
const dreamSrc = readFileSync(new URL('../src/commands/dream.ts', import.meta.url), 'utf-8');
|
|
|
|
describe('dream CLI flag wiring', () => {
|
|
test('declares --input flag with file argument', () => {
|
|
expect(dreamSrc).toContain("'--input'");
|
|
expect(dreamSrc).toContain('inputFile');
|
|
});
|
|
|
|
test('declares --date / --from / --to flags', () => {
|
|
expect(dreamSrc).toContain("'--date'");
|
|
expect(dreamSrc).toContain("'--from'");
|
|
expect(dreamSrc).toContain("'--to'");
|
|
});
|
|
|
|
test('validates ISO date format', () => {
|
|
expect(dreamSrc).toMatch(/ISO_DATE_RE/);
|
|
expect(dreamSrc).toContain('YYYY-MM-DD');
|
|
});
|
|
|
|
test('--input + --date conflict detection', () => {
|
|
expect(dreamSrc).toContain('--input cannot be combined with --date');
|
|
});
|
|
|
|
test('--input implies --phase synthesize', () => {
|
|
expect(dreamSrc).toContain("phase = 'synthesize'");
|
|
});
|
|
|
|
test('--from > --to range validation', () => {
|
|
expect(dreamSrc).toContain('empty range');
|
|
});
|
|
|
|
test('forwards synth fields to runCycle', () => {
|
|
expect(dreamSrc).toContain('synthInputFile');
|
|
expect(dreamSrc).toContain('synthDate');
|
|
expect(dreamSrc).toContain('synthFrom');
|
|
expect(dreamSrc).toContain('synthTo');
|
|
});
|
|
|
|
test('totals line includes synth + patterns counters', () => {
|
|
expect(dreamSrc).toContain('synth_transcripts');
|
|
expect(dreamSrc).toContain('synth_pages');
|
|
expect(dreamSrc).toContain('patterns=');
|
|
});
|
|
|
|
test('help text documents dry-run synthesis semantics (Codex finding #8)', () => {
|
|
expect(dreamSrc).toContain('skips the Sonnet');
|
|
expect(dreamSrc.toLowerCase()).toContain('zero llm calls');
|
|
});
|
|
|
|
// v0.41.13: --source / --source-id flag wiring (supersedes PR #1559).
|
|
// Structural-only tests; behavioral tests live in test/dream.test.ts.
|
|
describe('--source / --source-id wiring (v0.41.13)', () => {
|
|
test('declares --source flag in argv parsing', () => {
|
|
expect(dreamSrc).toContain("'--source'");
|
|
});
|
|
|
|
test('declares --source-id alias in argv parsing', () => {
|
|
expect(dreamSrc).toContain("'--source-id'");
|
|
});
|
|
|
|
test('forwards resolved sourceId to runCycle', () => {
|
|
// The runCycle call must pass sourceId; gate name "sourceId"
|
|
// not "source" because CycleOpts.sourceId is the contract.
|
|
expect(dreamSrc).toMatch(/sourceId:\s*resolvedSourceId/);
|
|
});
|
|
|
|
test('imports resolveSourceId from canonical source-resolver helper', () => {
|
|
expect(dreamSrc).toContain("from '../core/source-resolver.ts'");
|
|
expect(dreamSrc).toContain('resolveSourceId');
|
|
});
|
|
|
|
test('declares isResolverUserError predicate for typed-error catch (T3 from eng review)', () => {
|
|
expect(dreamSrc).toContain('function isResolverUserError');
|
|
});
|
|
|
|
test('documents --source in --help output', () => {
|
|
expect(dreamSrc).toContain('--source <id>');
|
|
expect(dreamSrc).toContain('--source-id <id>');
|
|
});
|
|
|
|
test('preserves --help short-circuit ordering comment (IRON RULE)', () => {
|
|
// The comment lives in runDream BEFORE the engine-null gate.
|
|
// Future refactors that reorder these blocks will trip this guard.
|
|
expect(dreamSrc).toContain('IRON RULE: --help short-circuits BEFORE');
|
|
});
|
|
|
|
test('declares engine-null guard for --source', () => {
|
|
expect(dreamSrc).toContain('requires a connected brain');
|
|
});
|
|
|
|
test('declares archived-source guard', () => {
|
|
expect(dreamSrc).toMatch(/source.*is archived/);
|
|
expect(dreamSrc).toContain('gbrain sources restore');
|
|
});
|
|
});
|
|
|
|
// issue #1678 — --drain bounded backlog drain wiring (structural).
|
|
describe('--drain wiring', () => {
|
|
test('declares --drain and --window flags', () => {
|
|
expect(dreamSrc).toContain("'--drain'");
|
|
expect(dreamSrc).toContain("'--window'");
|
|
expect(dreamSrc).toContain('windowSeconds');
|
|
});
|
|
|
|
test('--drain defaults to extract_atoms and rejects other phases', () => {
|
|
expect(dreamSrc).toContain("phase = 'extract_atoms'");
|
|
expect(dreamSrc).toContain('--drain currently supports only --phase extract_atoms');
|
|
});
|
|
|
|
test('drain holds the same cycle lock id (contends with the routine cycle)', () => {
|
|
expect(dreamSrc).toContain('cycleLockIdFor(resolvedSourceId)');
|
|
expect(dreamSrc).toContain('withRefreshingLock');
|
|
});
|
|
|
|
test('drain reports remaining + exits non-zero when incomplete', () => {
|
|
expect(dreamSrc).toContain('EXIT_DRAIN_INCOMPLETE');
|
|
expect(dreamSrc).toContain('cycle_already_running');
|
|
});
|
|
});
|
|
});
|