mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
* feat(minions): pool-recovery audit + reconnect reason-threading + shared drain helper (#1685 GAP B, 5A) - pool-recovery-audit.ts: reap_detected (CONNECTION_ENDED) vs reconnect_other; recovered/failed split - postgres-engine reconnect(ctx?) classifies the triggering error so only true pooler reaps are tagged (CODEX #8) - retry.ts reconnect callback widened to thread the error; retry-matcher isConnectionEndedError - runExtractAtomsDrainForSource shared helper (cycleLockIdFor + withRefreshingLock) — one drain path (5A) - supervisor-audit readRecentSupervisorEvents (current+prev ISO week, CODEX #7) - extract-atoms-drain PROTECTED; autopilot.auto_drain.* config keys * feat(doctor): worker_oom_loop + pool_reap_health checks + cause-ranked top_issues (#1685 GAP A/B/C) - computeWorkerOomLoopCheck: unions supervisor rss_watchdog + minion_jobs watchdog-abort (CODEX #5), cap fallback to resolveDefaultMaxRssMb (CODEX #6) - computePoolReapHealthCheck: reaps-not-recovering fail, thrash warn - doctor-cause-rank rankIssues: tier ordering + grounded downstream_of (CODEX #9) + drift guard (4A) - supervisor causeStr + queue_health cross-reference worker_oom_loop (DRY 1C) - register both checks in doctor-categories ops * feat(autopilot): per-source extract_atoms auto-drain + handler + dream --drain refactor (#1685 GAP D) - autopilot per-source gate: enabled + !packDeclares + backlog>threshold + daily cap; time-sloted idempotency key (CODEX #2) - extract-atoms-drain Minion handler (thin wrapper, LockUnavailableError -> deferred) - dream --drain routes through the shared helper (5A) * chore: bump version and changelog (v0.42.12.0) #1685 brain-health-as-solved-problem: cause-ranked doctor, worker_oom_loop line, per-source auto-drain, pool-reap health. Layers on #1678/#1735. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(todos): file #1685 GAP E + remote-path follow-ups (v0.42.12.0) * fix(#1685): pre-landing review — multi-source auto-drain, honest pool-reap signal, lock-renewal reap labeling - autopilot: drop maxWaiting (coalesces by name+queue not source → only one source drained + cap over-count); pre-check idempotency key so only genuinely-new sources submit+count - pool_reap_health: fail on reconnect FAILURES (the real signal), not reaps>0&&failures>0 (false causality when a recovered reap + unrelated failure co-occur) - lock-renewal-tick threads its triggering error to reconnect() so a CONNECTION_ENDED pooler reap is labeled reap_detected not reconnect_other (pool_reap_health now fires for the #1678 incident path) * chore: re-version v0.42.12.0 → v0.42.16.0 (#1685) Slot collision avoidance per queue. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: restore slim CLAUDE.md + move #1685 entries to KEY_FILES.md (fix check:doc-history) The master merge wrongly kept the pre-restructure 577KB CLAUDE.md; the check:doc-history guard caps it at 60KB. Take master's slim CLAUDE.md and record the #1685 files (doctor-cause-rank, pool-recovery-audit, worker_oom_loop + pool_reap_health checks, auto-drain, 5A helper) as current-state prose in docs/architecture/KEY_FILES.md (no release markers). llms regenerated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
139 lines
5.3 KiB
TypeScript
139 lines
5.3 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 routes through the shared helper with the resolved source (5A)', () => {
|
|
// v0.42.10.0 (#1685 GAP D / 5A): the lock+batch+count wiring moved into
|
|
// runExtractAtomsDrainForSource so the CLI, the Minion handler, and
|
|
// autopilot share ONE drain path. dream threads resolvedSourceId so the
|
|
// helper picks cycleLockIdFor(resolvedSourceId) — the same lock the routine
|
|
// cycle holds for that source. The lock-id contract is now pinned in
|
|
// test/extract-atoms-drain.test.ts ("shared wiring helper holds the cycle lock").
|
|
expect(dreamSrc).toContain('runExtractAtomsDrainForSource');
|
|
expect(dreamSrc).toContain('sourceId: resolvedSourceId');
|
|
});
|
|
|
|
test('drain reports remaining + exits non-zero when incomplete', () => {
|
|
expect(dreamSrc).toContain('EXIT_DRAIN_INCOMPLETE');
|
|
expect(dreamSrc).toContain('cycle_already_running');
|
|
});
|
|
});
|
|
});
|