Files
gbrain/test/status-sections.test.ts
T
a74e5d90fc v0.41.20.0 feat: gbrain status + doctor --scope=brain (fix wave 2: items #6 + #7) (#1544)
* feat(doctor): doctor-categories foundation — BRAIN/SKILL/OPS/META sets + drift guard

Categorizes every doctor check name into exactly one of four categories. Exported
constants + categorizeCheck(name) helper are the single source of truth for the
v0.41.20.0 brain_checks_score + category_scores + --scope=brain wave. Drift guard
test parses doctor.ts source for both inline {name: 'foo'} and helper
const name = 'foo' patterns; CI fires if any check name lacks a category.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(doctor): brain_checks_score + category_scores + --scope=brain skip-computation

Extends Check with optional category and DoctorReport with brain_checks_score +
category_scores (additive — schema_version stays at 2; back-compat health_score
math byte-identical). buildChecks gains --scope=brain with explicit early-skip
gates around the SKILL check group (resolver_health + skill_conformance +
skill_brain_first + whoknows_health). Sub-second doctor on a brain with thousands
of skills. computeDoctorReport tags every check via categorizeCheck() at compute
time. Human output leads with the brain figure and renders the weighted
BrainHealth.brain_score alongside.

Test seam fix in test/doctor-home-dir-in-worktree.test.ts: the pre-existing
fragile JSON parser walked back from "checks" to find the envelope's outer
brace; v0.41.20.0's new nested category_scores object broke that heuristic.
Anchored on the canonical {"schema_version" envelope prefix instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(status): gbrain status — single-screen brain health dashboard + get_status_snapshot MCP op

NEW gbrain status command (src/commands/status.ts) composes 6 sections:
sync (per-source last_sync_at + staleness via buildSyncStatusReport),
cycle (TWO rows: last autopilot-cycle + last autopilot-* of any kind —
reflects v0.36.4.0 health-aware autopilot's targeted handler routing;
totals read from result.report.totals per the canonical handler shape),
locks (gbrain_cycle_locks active rows), workers (readSupervisorEvents +
summarizeCrashes), queue (LIVE counts NO time-window — old stuck jobs
are exactly what status surfaces), autopilot (PID liveness via kill -0).

Stable --json envelope (schema_version: 1). Exit codes 0=ok / 1=snapshot
failed / 2=usage. --section filter.

Thin-client mode routes Sync + Cycle through NEW get_status_snapshot MCP
op (admin scope, NOT localOnly; payload deliberately omits Locks /
Workers / Queue / Autopilot so feature creep can't quietly widen the
admin-scoped data exposure). Local-only sections render "local-only —
N/A on remote brain" honestly instead of pretending the local install's
empty state is the remote brain's.

CLI dispatch: pre-engine-bind branch for thin-client (no PGLite needed)
+ engine-connected dispatch case for local mode. CLI-only architecture
per codex MAJOR-4 (status owns its own thin-client branch inside
runStatus, not routed through op dispatch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump version to v0.41.20.0 + CHANGELOG + TODOS + llms regen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(doctor-categories): categorize batch_retry_health from v0.41.19.0 Supavisor wave

The drift guard correctly caught a new check introduced by master's v0.41.19.0
Supavisor Retry Cathedral (PR #1537). batch_retry_health surfaces batch-write
retry events from the new src/core/audit/batch-retry-audit.ts module — OPS
category (infrastructure liveness).

This is exactly why the drift guard exists: any future check added to doctor.ts
without a category entry fails CI immediately instead of silently degrading
to 'meta'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:47:05 -07:00

77 lines
2.7 KiB
TypeScript

/**
* Pure-function unit tests for `gbrain status` orchestrator helpers.
*
* Hermetic — no PGLite, no DB. Drives the exported helpers (parseSectionFlag,
* runStatus with engine=null in thin-client-disabled mode) and asserts:
* - JSON envelope shape stability (schema_version: 1)
* - --section filter validation (unknown → exit 2)
* - exit code policy (0 success/degraded, 1 snapshot failure, 2 usage)
* - thin-client local-only-N/A render for Locks/Workers/Queue/Autopilot
* (we exercise this via a stubbed cfg that mimics thin-client mode)
*
* The E2E test at test/e2e/status-pglite.test.ts covers the full PGLite +
* fake-minion_jobs + fake-supervisor-audit path.
*/
import { describe, test, expect } from 'bun:test';
import { parseSectionFlag, runStatus } from '../src/commands/status.ts';
describe('parseSectionFlag', () => {
test('no --section flag → undefined (all sections)', () => {
expect(parseSectionFlag([])).toBeUndefined();
expect(parseSectionFlag(['--json'])).toBeUndefined();
});
test('--section <name> form returns the set', () => {
const r = parseSectionFlag(['--section', 'sync']);
expect(r).toBeInstanceOf(Set);
expect((r as Set<string>).has('sync')).toBe(true);
});
test('--section=<name> form returns the set', () => {
const r = parseSectionFlag(['--section=cycle']);
expect(r).toBeInstanceOf(Set);
expect((r as Set<string>).has('cycle')).toBe(true);
});
test('unknown section returns usage_error', () => {
expect(parseSectionFlag(['--section', 'bogus'])).toBe('usage_error');
expect(parseSectionFlag(['--section=nonsense'])).toBe('usage_error');
});
test('every valid section is accepted', () => {
for (const s of ['sync', 'cycle', 'locks', 'workers', 'queue', 'autopilot']) {
const r = parseSectionFlag(['--section', s]);
expect(r).toBeInstanceOf(Set);
expect((r as Set<string>).has(s)).toBe(true);
}
});
});
describe('runStatus exit codes', () => {
test('--section invalid → exit 2 (usage error)', async () => {
let captured = '';
const r = await runStatus(null, ['--section', 'bogus'], {
stdout: () => {},
stderr: (s: string) => {
captured += s;
},
});
expect(r.exitCode).toBe(2);
expect(captured).toContain('invalid --section');
});
test('local mode with engine=null → exit 1 (snapshot failure)', async () => {
let captured = '';
const r = await runStatus(null, [], {
stdout: () => {},
stderr: (s: string) => {
captured += s;
},
});
// Without a config + engine, status can't build the local snapshot.
expect(r.exitCode).toBe(1);
expect(captured).toMatch(/snapshot failed|no engine connected/);
});
});