mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
* 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>
73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
/**
|
|
* `get_status_snapshot` MCP op contract.
|
|
*
|
|
* Pins the v0.41.19.0 wave's per-op decisions:
|
|
* - scope: 'admin' (codex MAJOR-9 / D10 — prevents read-scoped clients
|
|
* from seeing brain-host operational state).
|
|
* - localOnly: false (remote thin-client `gbrain status` callers need
|
|
* it via HTTP MCP — that's the whole point).
|
|
* - payload returns ONLY {schema_version: 1, sync, cycle}. Locks /
|
|
* Workers / Queue / Autopilot are deliberately omitted from the
|
|
* remote shape; the local CLI's `gbrain status` renders them as
|
|
* "N/A on remote brain" instead.
|
|
*
|
|
* Hermetic — stubs the engine. The cycle / sync helpers degrade
|
|
* gracefully when their queries throw, so a stubbed `executeRaw` that
|
|
* returns `[]` is enough to exercise the shape.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { operations, operationsByName } from '../src/core/operations.ts';
|
|
|
|
describe('get_status_snapshot op definition', () => {
|
|
test('exists and is registered in the operations array', () => {
|
|
expect(operationsByName.get_status_snapshot).toBeDefined();
|
|
expect(operations.find((o) => o.name === 'get_status_snapshot')).toBeDefined();
|
|
});
|
|
|
|
test('scope is admin', () => {
|
|
const op = operationsByName.get_status_snapshot;
|
|
expect(op.scope).toBe('admin');
|
|
});
|
|
|
|
test('localOnly is false (must be remote-callable for thin-client status)', () => {
|
|
const op = operationsByName.get_status_snapshot;
|
|
expect(op.localOnly).toBe(false);
|
|
});
|
|
|
|
test('takes no params', () => {
|
|
const op = operationsByName.get_status_snapshot;
|
|
expect(op.params).toEqual({});
|
|
});
|
|
});
|
|
|
|
describe('get_status_snapshot handler shape', () => {
|
|
test('returns only {schema_version, sync, cycle} keys (no Locks/Workers/Queue/Autopilot)', async () => {
|
|
const op = operationsByName.get_status_snapshot;
|
|
// Stub engine that returns empty rows for any executeRaw and a minimal
|
|
// BrainEngine shape. The sync helper degrades to an empty sources list
|
|
// and a synthetic SyncStatusReport; the cycle helper degrades to
|
|
// {last_full: null, last_targeted: null}.
|
|
const stubEngine: any = {
|
|
kind: 'pglite',
|
|
executeRaw: async () => [],
|
|
getConfig: async () => null,
|
|
};
|
|
const ctx: any = {
|
|
engine: stubEngine,
|
|
config: {},
|
|
logger: { info: () => {}, warn: () => {}, error: () => {} },
|
|
dryRun: false,
|
|
remote: true,
|
|
};
|
|
const result = (await op.handler(ctx, {})) as Record<string, unknown>;
|
|
expect(result.schema_version).toBe(1);
|
|
expect(result).toHaveProperty('sync');
|
|
expect(result).toHaveProperty('cycle');
|
|
expect(result).not.toHaveProperty('locks');
|
|
expect(result).not.toHaveProperty('workers');
|
|
expect(result).not.toHaveProperty('queue');
|
|
expect(result).not.toHaveProperty('autopilot');
|
|
});
|
|
});
|