mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
- #2138 propose_takes: the take_proposals idempotency key was per-page, so ON CONFLICT DO NOTHING silently dropped claim #2+ on multi-claim pages. Migration v125 folds md5(claim_text) into the unique index (expression index: no new column, no backfill); the INSERT's conflict target matches and now counts via RETURNING so extractor-repeated claims don't inflate proposals_inserted. Fresh-install blobs (schema.sql / schema-embedded.ts / pglite-schema.ts) updated in lockstep. - #2289 cli: '--source __all__' was rejected by SOURCE_ID_RE and silently fell back to 'default'. makeContext now maps the sentinel to the op-level source_id='__all__' (resolveRequestedScope spans the brain for local callers), errors loudly on an explicit --source that fails to resolve, and leaves ops that declare their OWN 'source' param (put_raw_data, add_timeline_entry, ...) untouched. - #2412 phantom-redirect: the post-migration straggler fact wipe now passes excludeSourcePrefixes:['cli:'] (parity with extract-facts' #1928 guard) so cli: conversation facts' supersession audit trail survives a redirect. - #1835 doctor: image_assets resolves Windows drive-form storage_path ('D:/...') to the WSL /mnt/<drive>/ mount on non-Windows platforms instead of joining it onto repoRoot and flagging every asset missing. Fixes #2138 Fixes #2289 Fixes #2412 Fixes #1835 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
91 lines
3.6 KiB
TypeScript
91 lines
3.6 KiB
TypeScript
/**
|
|
* #2289 — `gbrain <op> --source __all__` must span all sources, not silently
|
|
* fall back to 'default'.
|
|
*
|
|
* Pre-fix: makeContext passed '__all__' to resolveSourceId, whose
|
|
* SOURCE_ID_RE rejects underscores; the swallowing catch then defaulted
|
|
* sourceId to 'default', silently scoping an "all sources" query to one
|
|
* source. Same catch also swallowed genuinely-bad explicit --source values.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { makeContext } from '../src/cli.ts';
|
|
import { withEnv } from './helpers/with-env.ts';
|
|
import type { BrainEngine } from '../src/core/engine.ts';
|
|
|
|
// Fresh-brain stub: sources table empty, no config. The non-explicit chain
|
|
// terminates at tier 6 ('default').
|
|
const stubEngine = {
|
|
async executeRaw() {
|
|
return [];
|
|
},
|
|
async getConfig() {
|
|
return null;
|
|
},
|
|
} as unknown as BrainEngine;
|
|
|
|
describe('makeContext --source handling (#2289)', () => {
|
|
test("--source __all__ maps to op-level source_id='__all__', not a silent 'default'", async () => {
|
|
await withEnv({ GBRAIN_SOURCE: undefined }, async () => {
|
|
const params: Record<string, unknown> = { source: '__all__' };
|
|
const ctx = await makeContext(stubEngine, params);
|
|
// The sentinel is handed to resolveRequestedScope via params.source_id;
|
|
// local callers (remote:false) span the whole brain there.
|
|
expect(params.source_id).toBe('__all__');
|
|
expect(ctx.remote).toBe(false);
|
|
// ctx.sourceId falls through the normal non-explicit chain.
|
|
expect(ctx.sourceId).toBe('default');
|
|
});
|
|
});
|
|
|
|
test('--source __all__ does not clobber an explicit source_id param', async () => {
|
|
await withEnv({ GBRAIN_SOURCE: undefined }, async () => {
|
|
const params: Record<string, unknown> = { source: '__all__', source_id: 'wiki' };
|
|
await makeContext(stubEngine, params);
|
|
expect(params.source_id).toBe('wiki');
|
|
});
|
|
});
|
|
|
|
test('invalid explicit --source errors loudly instead of defaulting', async () => {
|
|
await withEnv({ GBRAIN_SOURCE: undefined }, async () => {
|
|
await expect(makeContext(stubEngine, { source: 'Not_A_Source' })).rejects.toThrow(
|
|
/Invalid --source/,
|
|
);
|
|
});
|
|
});
|
|
|
|
test('explicit --source naming an unregistered source errors loudly', async () => {
|
|
await withEnv({ GBRAIN_SOURCE: undefined }, async () => {
|
|
await expect(makeContext(stubEngine, { source: 'nope' })).rejects.toThrow(/not found/);
|
|
});
|
|
});
|
|
|
|
test("op-owned `source` param (e.g. put_raw_data's data source) is NOT the brain-source selector", async () => {
|
|
await withEnv({ GBRAIN_SOURCE: undefined }, async () => {
|
|
const op = { params: { source: { type: 'string' } } } as never;
|
|
// 'crustdata' is a data-source tag, not a registered brain source —
|
|
// must neither throw nor be fed into resolveSourceId.
|
|
const params: Record<string, unknown> = { source: 'crustdata' };
|
|
const ctx = await makeContext(stubEngine, params, op);
|
|
expect(ctx.sourceId).toBe('default');
|
|
expect(params.source).toBe('crustdata');
|
|
expect(params.source_id).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
test('no --source: resolution failure still falls back silently (fresh pre-init brain)', async () => {
|
|
const throwingEngine = {
|
|
async executeRaw() {
|
|
throw new Error('relation "sources" does not exist');
|
|
},
|
|
async getConfig() {
|
|
throw new Error('no config yet');
|
|
},
|
|
} as unknown as BrainEngine;
|
|
await withEnv({ GBRAIN_SOURCE: undefined }, async () => {
|
|
const ctx = await makeContext(throwingEngine, {});
|
|
expect(ctx.sourceId).toBe('default');
|
|
});
|
|
});
|
|
});
|