mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-29 19:01:39 +00:00
Takeover of three community PRs, each salvaged to its correct core and rebased onto master: - #2469: onboard checks (type_proliferation, pack_upgrade_available) passed cfg:null to loadActivePack, so a schema pack selected via `gbrain schema use` (tier-6 file-plane ~/.gbrain/config.json) never resolved and the checks silently compared against the default pack. Now pass loadConfig(); register schema_pack in KNOWN_CONFIG_KEYS. Dropped the fork grab-bag (~22 unrelated files) from the original PR. - #2479: the think synthesis prompt limited the Conflicts rule to take-vs-take, so pages-only brains surfaced zero conflicts. Expanded the rule to conflicts across pages and/or takes with severity tags + a VARIANTS rule, using generic placeholder slugs per the privacy rule (the original hardcoded contributor-brain slugs). Added env knobs GBRAIN_THINK_GATHER_LIMIT / GBRAIN_THINK_EXCERPT_LEN / GBRAIN_THINK_MAX_OUTPUT_TOKENS (defaults unchanged). - #2505: checkCycleFreshness thresholds were env-only; nightly-cadence brains need DB-plane keys + source scoping. Added doctor.cycle_freshness.{warn,fail}_hours and .source_{allowlist,ignorelist} (env wins over config, pace-mode precedence). Dropped the original PR's DREAM_SOURCE_ALLOWLIST fallback — a fork-private cron env var that exists nowhere upstream. Co-authored-by: onchito-walks <onchito-walks@users.noreply.github.com> Co-authored-by: Adityaranjan-VeloxAI <Adityaranjan-VeloxAI@users.noreply.github.com> Co-authored-by: sene1337 <sene1337@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
3.0 KiB
TypeScript
72 lines
3.0 KiB
TypeScript
/**
|
|
* #2479 — think pipeline fixes:
|
|
* 1. The synthesis prompt's Conflicts rule covers page-vs-page (and
|
|
* page-vs-take) disagreements, not just take-vs-take — pages-only brains
|
|
* surfaced zero conflicts before.
|
|
* 2. Retrieval breadth / output-cap env knobs (GBRAIN_THINK_GATHER_LIMIT,
|
|
* GBRAIN_THINK_EXCERPT_LEN, GBRAIN_THINK_MAX_OUTPUT_TOKENS) tune
|
|
* conflict/variant recall per-install without changing defaults.
|
|
*/
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { THINK_SYSTEM_PROMPT_BASE } from '../src/core/think/prompt.ts';
|
|
import { renderPagesBlock, envInt } from '../src/core/think/gather.ts';
|
|
import { maxOutputTokensFor } from '../src/core/think/index.ts';
|
|
import { withEnv } from './helpers/with-env.ts';
|
|
|
|
describe('think prompt — conflicts across pages AND takes (#2479)', () => {
|
|
test('Conflicts rule covers pages and/or takes, with severity tags', () => {
|
|
expect(THINK_SYSTEM_PROMPT_BASE).toContain('pages and/or takes');
|
|
expect(THINK_SYSTEM_PROMPT_BASE).toContain('[HIGH]');
|
|
expect(THINK_SYSTEM_PROMPT_BASE).toContain('VARIANTS');
|
|
});
|
|
|
|
test('example slugs are generic placeholders (privacy rule)', () => {
|
|
expect(THINK_SYSTEM_PROMPT_BASE).toContain('finance/acme-pricing');
|
|
expect(THINK_SYSTEM_PROMPT_BASE).not.toMatch(/velox|warm_dm/i);
|
|
});
|
|
});
|
|
|
|
describe('think env knobs (#2479)', () => {
|
|
test('envInt: valid positive int wins, garbage/absent falls back', async () => {
|
|
await withEnv({ GBRAIN_TEST_KNOB: '7' }, () => {
|
|
expect(envInt('GBRAIN_TEST_KNOB', 40)).toBe(7);
|
|
});
|
|
await withEnv({ GBRAIN_TEST_KNOB: 'banana' }, () => {
|
|
expect(envInt('GBRAIN_TEST_KNOB', 40)).toBe(40);
|
|
});
|
|
await withEnv({ GBRAIN_TEST_KNOB: '-3' }, () => {
|
|
expect(envInt('GBRAIN_TEST_KNOB', 40)).toBe(40);
|
|
});
|
|
expect(envInt('GBRAIN_TEST_KNOB_UNSET', 40)).toBe(40);
|
|
});
|
|
|
|
test('renderPagesBlock excerpt length honors GBRAIN_THINK_EXCERPT_LEN', async () => {
|
|
const pages = [
|
|
{ slug: 'notes/long', compiled_truth: 'x'.repeat(1000) },
|
|
] as unknown as Parameters<typeof renderPagesBlock>[0];
|
|
|
|
// Default stays 600.
|
|
const dflt = renderPagesBlock(pages);
|
|
expect(dflt).toContain('x'.repeat(600));
|
|
expect(dflt).not.toContain('x'.repeat(601));
|
|
|
|
await withEnv({ GBRAIN_THINK_EXCERPT_LEN: '50' }, () => {
|
|
const out = renderPagesBlock(pages);
|
|
expect(out).toContain('x'.repeat(50));
|
|
expect(out).not.toContain('x'.repeat(51));
|
|
});
|
|
});
|
|
|
|
test('maxOutputTokensFor honors GBRAIN_THINK_MAX_OUTPUT_TOKENS', async () => {
|
|
await withEnv({ GBRAIN_THINK_MAX_OUTPUT_TOKENS: '9000' }, () => {
|
|
expect(maxOutputTokensFor('openai:gpt-4o')).toBe(9000);
|
|
expect(maxOutputTokensFor('anthropic:claude-sonnet-5')).toBe(9000);
|
|
});
|
|
// Defaults unchanged without the env var.
|
|
await withEnv({ GBRAIN_THINK_MAX_OUTPUT_TOKENS: undefined }, () => {
|
|
expect(maxOutputTokensFor('openai:gpt-4o')).toBe(4000);
|
|
expect(maxOutputTokensFor('anthropic:claude-sonnet-5')).toBe(16000);
|
|
});
|
|
});
|
|
});
|