Files
gbrain/test/think-env-knobs.test.ts
T
80606d5df0 fix(doctor,think): file-plane pack resolution, page-vs-page conflicts, cycle-freshness tuning
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>
2026-07-21 15:02:50 -07:00

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);
});
});
});