mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 00:48:18 +00:00
* feat(verbs): MEMORY_VERBS v1 core — five frozen verbs over existing machinery recall extended in place (G1B additive superset: protocol_version + string fact_id/provenance on every response; query arm via cheap-hybrid with keyword-only degradation when no embedding provider; op-layer budget packing facts-first via the new generic packToBudget). remember/entity/synthesize/ forget as first-class Operations in src/core/verbs.ts (runtime leaf — TDZ-safe both import directions) with MEMORY VERB descriptions, ToolAnnotations, populated suggestions on every error (verbError + protocol ErrorCodes + additive detail/protocol_version on OperationError.toJSON). writeSingleFact (facts/write-single.ts): zero-LLM single-fact seam reusing the pipeline's dedup; X1 deterministic supersession (same entity+kind, similar, text differs -> supersedeId / fence expire+link); validUntil threaded through FenceInputFact (was silently dropped). buildEntityCard (verbs/entity-card.ts): reflex resolution arms + parallel depth-1 reads, per-arm degradation on pre-page_aliases brains, near-miss suggestions with create_safety. runThink gains additive usage aggregation; synthesize converts the no-LLM graceful result to the unavailable protocol error and prices the cost block via canonical model-pricing. RESPONSE_SCHEMAS registry + ERROR_SCHEMA for the conformance kit. Cathedral 1 stage 1/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(mcp): MEMORY_VERBS v1 surface mode + fail-closed dispatch + usage sidecar gbrain serve --surface verbs|full (config mcp_surface registered in the merge allowlist; default full — existing installs unchanged). Enforcement is two-layer and fail-closed: filtered ListTools AND dispatchToolCall's new allowedOps set, applied on stdio + BOTH HTTP paths (serve-http.ts OAuth and the second http-transport.ts). ToolAnnotations passthrough in buildToolDefs + the HTTP inline mapper, emitted only when an op defines them (byte-stability for existing tools). Verb validation/internal errors speak the protocol envelope (suggestion + protocol_version) at the dispatch layer. E4 observability: per-verb JSONL sidecar (usage-log.ts — local only, never uploaded; O_APPEND line-atomic, lock-free 10MB rotation) written from dispatch so param-validation failures count; brain_id via gbrainPath; doctor gains the memory_verbs_usage check. CLI: human-readable rendering for remember/entity/ synthesize with trailing --json escape; gbrain recall gains --query + --budget-tokens routed through the recall OP (same arm MCP exercises) with the budget footer; the forget verb carries no cliHint (CLI_ONLY forget dispatches first and would shadow it). Cathedral 1 stage 2/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(protocol): gbrain protocol — schemas, conformance kit, stats + TTHW gbrain protocol [--json] emits the live verb input schemas + the hand-authored RESPONSE_SCHEMAS + error contract + versioning policy. gbrain protocol conformance certifies ANY MCP endpoint (default: self-spawned stdio; --target http://...[--token] or a stdio command) against the embedded fixture set — shape/enum/behavior/round-trip only, deterministic; entity-page cases seed via put_page when available and skip honestly on verbs-only targets; synthesize is cost-gated behind --synthesize (asserts the clean unavailable error on key-less CI). Fixtures double as BrainBench seeds (test/fixtures/memory-verbs/cases.json mirror). Self-certification passes 23/23 live. gbrain protocol stats [--days N]: per-verb calls/error-rate/latency, budget drops, entity hit rate, and measured TTHW (init stamps protocol_installed_at; delta to the sidecar's first verb call) — local JSONL only, never uploaded. init epilogues print the 3-command quickstart (mcp add --surface verbs + key-free resurrection demo) with the visibility consent line. entity-card fix: raw namespaced-slug input now matches exactly (slugify flattens slashes). Cathedral 1 stage 3/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test+docs(protocol): conformance suite, p99 gate, MEMORY_VERBS_v1 doc, funnel test/memory-verbs-conformance.test.ts: 26 tests through dispatchToolCall — G1B superset regression (legacy fields byte-equal, additions allowed), budget math incl. budget<first-item, keyword degradation (never an error), remember provenance/ttl-trap/null-echo, remote round-trip via the world default + private facts hidden, entity card schema-validated with the chat gateway rigged to throw (zero-LLM guard) + all arms + fence test, synthesize [EXPENSIVE + annotations + clean unavailable, forget idempotency/not_found, writeSingleFact X1 supersession via the embed-transport seam + degraded dedup, the F3 negative self-test (the runner FAILS lying servers: dropped fields, bad enums, re-typed ids), fixture-mirror drift guard. test/mcp-surface.test.ts: verbs=exactly 5, full=identity, dispatch-layer fail-closed, strict flag parsing. test/entity-card-perf.slow.test.ts: 20K pages/100K links/30K aliases/40K facts — measured p99 22.9ms vs the 100ms contract + a recalibrated 50x ratio guard; wired into the CI perf job along with stdio self-certification (23/23 CONFORMANT live). tool-defs byte- stability test now pins per-op: ops WITHOUT annotations keep the exact pre-v1 shape. docs/protocol/MEMORY_VERBS_v1.md: the frozen contract — envelopes, error codes, additive-forever policy, ttl forms (P30D rejected), status-not- status_text, surface modes + why default full, per-harness installs, consent line, conformance pass criteria + copyable --target examples, local-only stats privacy, 200K manual bench recipe. README quickstart + docs/mcp pages moved to --surface verbs with the claude-not-found recovery; llms-config registered + bundles rebuilt; KEY_FILES entries for the 7 new modules. Cathedral 1 stage 4/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(verbs): verify-gate compliance — test seam, allow-comment placement, import hygiene usage-log gains __setUsageLogPathForTests so the verb tests redirect the sidecar without mutating process.env.GBRAIN_HOME (test-isolation lint); the gbrain-allow-direct-insert rationale moves onto the insertFact call line (system-of-record guard greps per-line); protocol.ts drops its unused `operations` import (operations-filter-bypass guard — operationsByName only). The synthesize unit test now drives the chat-transport seam (deterministic success envelope + priced cost block, zero real API spend); the keyless `unavailable` conversion stays covered end-to-end by the CI conformance step, which runs credential-free. Cathedral 1 verify pass (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(verbs): close 3 ship-review P1s — forget cross-source, entity backlink leak, conformance exit code Pre-landing review (codex, on the committed diff) caught three P1s the plan-stage reviews flagged only in the abstract: P1.1 trust boundary — forget expired by GLOBAL fact id with no source/visibility scope: a remote caller in source A could expire facts in source B (incl. private) by guessing ids. forgetFactInFence gains sourceId + worldOnly opts (scope-check before any state inspection; out-of-scope → not_found, no existence leak); the forget verb passes ctx.sourceId + worldOnly=remote. P1.2 source isolation — entity card incoming edges + backlink_count leaked across sources: getBacklinks(slug,{sourceId}) scopes only the TARGET page and getBacklinkCounts has no source param, so a foreign-source page's slug surfaced in a same-named entity's card. Replaced both with a both-sides-scoped query (f.source_id = t.source_id = entity source, mentions excluded) inside the card module — no engine-interface change. P1.3 CLI exit code — protocol conformance/unknown-subcommand set process.exitCode directly, which the PGLite/WASM force-exit seam ignores (it reads the gbrain-owned setCliExitVerdict). A NOT CONFORMANT target printed the banner but exited 0 — CI would green-light a non-conformant server. Now routes through setCliExitVerdict; proven: unknown subcommand exits 1. Regression tests: cross-source forget → not_found + foreign fact stays active; remote can't forget a private fact; entity card omits foreign-source backlinks from both edges and the count. Cathedral 1 ship-review fixes (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.43.0.0 feat(mcp): MEMORY_VERBS v1 — frozen five-verb memory protocol + conformance kit (cathedral 1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: surface --surface verbs in local-stdio quickstart paths for v0.43.0.0 The MEMORY_VERBS v1 quickstart flag (gbrain serve --surface verbs) was added to the README quickstart, CLAUDE_CODE.md, and CODEX.md during the build but three other docs that show the local-stdio wire-up still presented bare `gbrain serve`: the connect-coding-agent Path B walkthrough (the doc the README links to), INSTALL.md, and DEPLOY.md's Local stdio path. Brought them in line so the five-verb surface is consistent across every local-quickstart entry point. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(doctor): assign memory_verbs_usage check to the SKILL category set The new memory_verbs_usage doctor check (Cathedral 1, E4) was added without a category, tripping the doctor-categories drift guard (every check name must belong to exactly one category set). It's an agent-capability adoption signal, a sibling of retrieval_reflex_health — assigned to SKILL_CHECK_NAMES. Cathedral 1 ship-review fix (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(verbs): propagate MEMORY_VERBS v1 to existing harnesses on upgrade The verbs shipped reachable + self-describing, but an EXISTING install that upgrades to 0.43 and keeps its skills had no active push toward them: no post-upgrade notice (default surface stays 'full'), and no skill/guidance routing — agents kept calling search/query/get_page/extract_facts. Two additive propagation fixes: - Post-upgrade feature pitch: src/commands/migrations/v0_43_0.ts (pitch-only, no-op idempotent orchestrator — the verbs ride existing tables, no schema/ data migration). Registered in the migrations index so and the self-upgrade NOTIFY channel announce the five verbs + + on any upgrade past 0.43. Verified live: the pitch fires for an install upgrading from 0.42.43.0. - Skill routing: skills/query (memory reads) and skills/brain-ops (read/write cycle) now teach the verbs — recall/entity/synthesize vs search/get_page/ query, remember vs extract_facts, forget — with a fall-back-to-classic-ops note for older brains / full surface. The in-MCP tool descriptions already steered tool-list readers; this adds the narrative layer the audit found missing across all 29 skills. apply-migrations future-list fixtures updated for the new 0.43.0.0 registry entry. No DB migration; no contract change; verbs unaffected. Cathedral 1 propagation (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(verbs): green the 3 failing CI shards — 3-seg migration version, hermetic synthesize test, fresh llms Three real CI-shard failures from the propagation commit + master merges: - migrations-registry (shard 10): v0_43_0 used a 4-segment version 0.43.0.0, but the registry enforces 3-segment semver (/^\d+\.\d+\.\d+$/). Changed the migration version to 0.43.0 (the RELEASE stays 0.43.0.0; migrations have always used 3-seg, e.g. 0.32.2). apply-migrations future-list fixtures updated to match. - synthesize conformance test (shard 5): asserted a successful answer via the chat seam, but runThink builds its client through a real-key check (not the seam), so CI (credential-free) took the NO_ANTHROPIC_API_KEY path → the verb's unavailable conversion → isError. Now wraps the call in withEnv with a fake key AND the chat seam, so it's deterministic regardless of ambient credentials. - build-llms (shard 8): the v0.42.44.0 master merge changed an inlined doc; regenerated llms.txt/llms-full.txt. Cathedral 1 CI fixes (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(search): steer concept/landscape questions to query — descriptions, docs, lookup chain, CLI nudge (#2416) The search/query routing guidance shipped a stale and backwards model: SEARCH_DESCRIPTION still led with "Keyword search using full-text search" (search has been cheap-hybrid — vector + keyword + RRF, expansion off — since D4/D15), and the mandatory lookup chain in brain-first.md ordered search-first with query only "if search is thin". For concept/landscape questions ("all the X that do Y"), keyword+vector recall always returns something, so the fallback never fires and expansion-recoverable synonym-phrased matches drop silently. - operations-descriptions.ts: fix the stale SEARCH lead (cheap-hybrid, no LLM expansion); both constants now route concept/synonym/landscape questions to query, cap the claim honestly (both are top-K — exhaustive enumeration belongs to list_pages), and keep every pinned redirect. - brain-first.md: lookup chain is intent-driven (exact token → search; concept/landscape → query FIRST), cost claims corrected, and the anti-pattern is explicit: a nonzero search count is not a completeness signal. Same sweep in brain-ops/SKILL.md and query/SKILL.md, which duplicated the backwards order. - search-modes.md: decision tree + quick-ref updated to the cheap-hybrid reality, new Tricky Spot #6 (count ≠ completeness), example names moved to placeholder people per the privacy convention. RETRIEVAL.md gets the matching one-liner. - query-intent.ts: looksConceptShaped + conceptNudge — deterministic concept-shape classifier in the existing classifier home, tuned to favor false-negatives; cues owned by other routers (find_experts, salience ops) deliberately excluded. cli.ts prints the hint-only stderr nudge on BOTH result paths (local engine + thin-client routed), --quiet-gated; search stays the cheap hot path (never auto-reroutes). - salience-llm-routing e2e: new concept-phrasings block asserts the descriptions actually route concept questions to query (verified live: 20/20 — all 12 personal-query regressions still route to salience ops, all 8 concept phrasings route to query). - KEY_FILES.md: query-intent entry updated to current state (the old entry pointed at a renamed file); llms bundle regenerated (no diff — entry is link-only). - TODOS.md: MCP-envelope hint follow-up filed (remote agents are the higher-value half; needs a return-contract change kept out of this commit). Verification: typecheck clean; 70 targeted unit tests + 20 live routing evals green; all 24 check:all guards green; failing full-suite shards triaged — think-pipeline.serial.test.ts:434 fails identically on the clean tree (pre-existing on this branch, filed separately), the rest are PGLite parallel-load contention that pass in isolation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): pin think's stub-path usage at null per the E2 accounting contract The MEMORY_VERBS [E2] usage-accounting change deliberately moved "no LLM ran" from `undefined` to `null` (the frozen cost-block spelling), but the older #1698 stub-path test still pinned `undefined` and failed on every run of this branch. The protected semantic is unchanged: no fake usage or cost on the stub path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(changelog): cover the #2416 query-steering wave under v0.43.0.0 The 0.43.0.0 entry described only MEMORY_VERBS; the #2416 retrieval-routing wave rides the same release. Adds its user-facing summary and refreshes the release date to the actual ship date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(protocol): reword so the #3502 docs-lint doesn't read a phantom command Master's docs-lint (landed after this branch forked) scans for `gbrain <verb>` references; the MEMORY_VERBS diagram's "gbrain reference implementation" phrasing parsed as a nonexistent `gbrain reference` command. Post-merge-only failure; both sides were green alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: update project documentation for v0.43.0.0 Post-ship /document-release drift pass over the MEMORY_VERBS v1 + #2416 query-steering waves: - docs/tutorials/connect-coding-agent.md: the pasteable brain-first protocol still taught the pre-#2416 "Search first" rule and named only full-surface tools; step 1 now routes by question shape (exact tokens -> search, concept/landscape -> query first) and both steps map the five-verb surface names (recall/synthesize/remember). find_experts and query callouts marked with their surface. - README.md + tutorial: "90-tool wall" count refreshed to 110 (the actual operation catalog). - CLAUDE.md: contract-first op count ~90 -> ~110 with the v0.43.0.0 MEMORY_VERBS clause; reference-map row for the memory-verbs protocol / --surface / conformance docs. - TODOS.md: blank line before the v0.42.74.0 section header. - llms-full.txt regenerated (build:llms; freshness test green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: apply cross-model doc-review fixes for v0.43.0.0 Verified findings from the /document-release Codex pass: - MEMORY_VERBS_v1.md: disclose that conformance WRITES (marker-suffixed seed page + remember/forget facts, write-scope creds, artifacts not auto-deleted); quickstart header says 4 commands because it has 4. - CHANGELOG v0.43.0.0: cost claim scoped per verb (budget/latency/cost block); remember "updates in place" -> supersedes-with-history (what write-single.ts actually does). - DEPLOY.md: legacy bearer fallback works on BOTH engines now (both schemas carry access_tokens per http-transport.ts) — was Postgres-only. - CLAUDE_CODE.md: list_skills/core-tools verify note marked full-surface (on --surface verbs those tools aren't on the surface). - skills/query/SKILL.md: --surface full INCLUDES the verbs; fallback is for pre-0.43 servers only. - skills/brain-ops/SKILL.md: search/query tool labels updated to the #2416 reality (cheap hybrid vs hybrid + LLM expansion). - search-modes.md: stale "keyword search returns chunks" label. - RETRIEVAL.md: note the query op defaults expand:true per call. - Counts: 43 skills -> 52 (README x2, INSTALL), 30 skills -> 52 (CLAUDE.md), "30+ tools over MCP" -> 110 (README). - TODOS.md: filed 2 code-vs-spec follow-ups (recall filter composition + limit cap; synthesize unavailable-mapping breadth) instead of silently editing the frozen contract. - llms-full.txt + skills.lock.json regenerated; freshness guards green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): regenerate the flag registry for the cathedral-1 command set Master's #2185 strict flag validation ships a committed, generated CLI_FLAG_REGISTRY with drift + freshness guards keyed off CLI_ONLY. The merge added this branch's `protocol` command to CLI_ONLY, but the committed registry was generated on master without it, failing CI shard 7's drift guard. Regenerated via `bun run build:flag-registry` on the merged tree (95 commands; picks up `protocol` and the `--surface` help-text mentions from the verbs work). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
212 lines
9.1 KiB
TypeScript
212 lines
9.1 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import {
|
|
GET_RECENT_SALIENCE_DESCRIPTION,
|
|
FIND_ANOMALIES_DESCRIPTION,
|
|
GET_RECENT_TRANSCRIPTS_DESCRIPTION,
|
|
LIST_PAGES_DESCRIPTION,
|
|
QUERY_DESCRIPTION,
|
|
SEARCH_DESCRIPTION,
|
|
LIST_SKILLS_DESCRIPTION,
|
|
GET_SKILL_DESCRIPTION,
|
|
SKILL_CATALOG_INSTRUCTIONS,
|
|
SKILL_CLIENT_GUIDANCE,
|
|
} from '../src/core/operations-descriptions.ts';
|
|
import { operations, operationsByName } from '../src/core/operations.ts';
|
|
import { BRAIN_TOOL_ALLOWLIST } from '../src/core/minions/tools/brain-allowlist.ts';
|
|
|
|
/**
|
|
* Tool descriptions are LLM-facing strings that drive routing. v0.29 adds
|
|
* three new ops + redirects on three existing ones. These tests pin the
|
|
* key phrases that the routing decision depends on so accidental edits
|
|
* (description rewrites, AI cleanup, voice changes) fail CI.
|
|
*/
|
|
|
|
describe('v0.29 — get_recent_salience description', () => {
|
|
test('matches the operation registration', () => {
|
|
expect(operationsByName['get_recent_salience'].description).toBe(GET_RECENT_SALIENCE_DESCRIPTION);
|
|
});
|
|
|
|
test('contains the explicit "Use this when" trigger phrase', () => {
|
|
expect(GET_RECENT_SALIENCE_DESCRIPTION).toContain("Use this when the user asks");
|
|
});
|
|
|
|
test('lists the personal-query trigger keywords', () => {
|
|
expect(GET_RECENT_SALIENCE_DESCRIPTION).toContain("what's been going on");
|
|
expect(GET_RECENT_SALIENCE_DESCRIPTION).toContain("anything crazy happening");
|
|
expect(GET_RECENT_SALIENCE_DESCRIPTION).toContain("notable");
|
|
});
|
|
|
|
test('explicitly bans semantic search for the same intent', () => {
|
|
expect(GET_RECENT_SALIENCE_DESCRIPTION).toContain("Do NOT run a semantic search for these");
|
|
});
|
|
});
|
|
|
|
describe('v0.29 — find_anomalies description', () => {
|
|
test('matches the operation registration', () => {
|
|
expect(operationsByName['find_anomalies'].description).toBe(FIND_ANOMALIES_DESCRIPTION);
|
|
});
|
|
|
|
test('mentions the cohort framing', () => {
|
|
expect(FIND_ANOMALIES_DESCRIPTION).toContain("grouped by cohort");
|
|
expect(FIND_ANOMALIES_DESCRIPTION).toContain("(tag or type)");
|
|
});
|
|
|
|
test('lists the unusual / what-stood-out trigger phrases', () => {
|
|
expect(FIND_ANOMALIES_DESCRIPTION).toContain("stood out");
|
|
expect(FIND_ANOMALIES_DESCRIPTION).toContain("unusual");
|
|
});
|
|
|
|
test('does not promise year-cohort behavior (deferred to v0.30)', () => {
|
|
// v0.29 ships tag + type only. The phrase below confirms the description
|
|
// does not lie about coverage — surfacing year would route the LLM to
|
|
// call the op for date-bucket questions it can't actually serve.
|
|
expect(FIND_ANOMALIES_DESCRIPTION).toContain("Cohort kinds: tag, type");
|
|
});
|
|
});
|
|
|
|
describe('v0.29 — get_recent_transcripts description', () => {
|
|
test('matches the operation registration', () => {
|
|
expect(operationsByName['get_recent_transcripts'].description).toBe(GET_RECENT_TRANSCRIPTS_DESCRIPTION);
|
|
});
|
|
|
|
test('mandates priority over query/search for personal questions', () => {
|
|
expect(GET_RECENT_TRANSCRIPTS_DESCRIPTION).toContain("FIRST");
|
|
});
|
|
|
|
test('explains raw vs polished distinction', () => {
|
|
expect(GET_RECENT_TRANSCRIPTS_DESCRIPTION).toContain("NOT polished");
|
|
expect(GET_RECENT_TRANSCRIPTS_DESCRIPTION).toContain("canonical source");
|
|
});
|
|
|
|
test('discloses the local-only constraint to the LLM', () => {
|
|
expect(GET_RECENT_TRANSCRIPTS_DESCRIPTION).toContain("Local-only");
|
|
expect(GET_RECENT_TRANSCRIPTS_DESCRIPTION).toContain("permission_denied");
|
|
});
|
|
});
|
|
|
|
describe('v0.29 — redirect hints on existing ops', () => {
|
|
test('list_pages mentions sort=updated_desc as the recency-question answer', () => {
|
|
expect(operationsByName['list_pages'].description).toBe(LIST_PAGES_DESCRIPTION);
|
|
expect(LIST_PAGES_DESCRIPTION).toContain("sort=updated_desc");
|
|
expect(LIST_PAGES_DESCRIPTION).toContain("what did I touch this week");
|
|
});
|
|
|
|
test('query redirects personal/emotional queries to the v0.29 ops', () => {
|
|
expect(operationsByName['query'].description).toBe(QUERY_DESCRIPTION);
|
|
expect(QUERY_DESCRIPTION).toContain("get_recent_salience");
|
|
expect(QUERY_DESCRIPTION).toContain("find_anomalies");
|
|
expect(QUERY_DESCRIPTION).toContain("get_recent_transcripts");
|
|
});
|
|
|
|
test('query warns the LLM not to assume "crazy" means impressive', () => {
|
|
expect(QUERY_DESCRIPTION).toContain("Do NOT assume");
|
|
expect(QUERY_DESCRIPTION).toContain("difficult or emotionally charged");
|
|
});
|
|
|
|
test('search has the shorter redirect hint', () => {
|
|
expect(operationsByName['search'].description).toBe(SEARCH_DESCRIPTION);
|
|
expect(SEARCH_DESCRIPTION).toContain("get_recent_salience");
|
|
});
|
|
});
|
|
|
|
describe('#2416 — concept/landscape routing between search and query', () => {
|
|
test('search describes the cheap-hybrid default, not the keyword-era model', () => {
|
|
expect(SEARCH_DESCRIPTION).toContain("Cheap hybrid search");
|
|
expect(SEARCH_DESCRIPTION).toContain("no LLM expansion");
|
|
expect(SEARCH_DESCRIPTION).not.toContain("Keyword search using full-text search");
|
|
});
|
|
|
|
test('search declares the completeness boundary and both escape routes', () => {
|
|
expect(SEARCH_DESCRIPTION).toContain("NOT proof of coverage");
|
|
expect(SEARCH_DESCRIPTION).toContain("landscape");
|
|
expect(SEARCH_DESCRIPTION).toContain("list_pages");
|
|
});
|
|
|
|
test('query owns concept/landscape questions but does not oversell coverage', () => {
|
|
expect(QUERY_DESCRIPTION).toContain("landscape");
|
|
expect(QUERY_DESCRIPTION).toContain("expansion recovers synonym");
|
|
expect(QUERY_DESCRIPTION).toContain("Still top-K");
|
|
expect(QUERY_DESCRIPTION).toContain("list_pages");
|
|
expect(QUERY_DESCRIPTION).toContain("cheaper");
|
|
});
|
|
});
|
|
|
|
describe('v0.29 — subagent allow-list', () => {
|
|
test('includes get_recent_salience and find_anomalies', () => {
|
|
expect(BRAIN_TOOL_ALLOWLIST.has('get_recent_salience')).toBe(true);
|
|
expect(BRAIN_TOOL_ALLOWLIST.has('find_anomalies')).toBe(true);
|
|
});
|
|
|
|
test('excludes get_recent_transcripts (codex C3 — would be a remote=true footgun)', () => {
|
|
// The op throws permission_denied for remote=true callers, and all subagent
|
|
// calls run with remote=true. Including it in the allow-list would mean
|
|
// every subagent call to it returns an error — looks like a bug.
|
|
expect(BRAIN_TOOL_ALLOWLIST.has('get_recent_transcripts')).toBe(false);
|
|
});
|
|
|
|
test('all v0.29 ops in the allow-list resolve to a registered Operation', () => {
|
|
// brain-allowlist invariant: every name maps to an entry in operations.ts
|
|
// OPERATIONS array. This guard catches rename drift.
|
|
for (const name of ['get_recent_salience', 'find_anomalies']) {
|
|
expect(operationsByName[name]).toBeDefined();
|
|
}
|
|
});
|
|
|
|
test('list_pages has new sort + updated_after params surfaced to MCP', () => {
|
|
const op = operationsByName['list_pages'];
|
|
expect(op.params.sort).toBeDefined();
|
|
expect(op.params.updated_after).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('v0.29 — operations array carries the three new ops', () => {
|
|
test('all three are registered (one allow-listed pair + one local-only)', () => {
|
|
const names = operations.map(o => o.name);
|
|
expect(names).toContain('get_recent_salience');
|
|
expect(names).toContain('find_anomalies');
|
|
expect(names).toContain('get_recent_transcripts');
|
|
});
|
|
});
|
|
|
|
describe('PR1 — skill catalog descriptions', () => {
|
|
test('list_skills / get_skill match the operation registration', () => {
|
|
expect(operationsByName['list_skills'].description).toBe(LIST_SKILLS_DESCRIPTION);
|
|
expect(operationsByName['get_skill'].description).toBe(GET_SKILL_DESCRIPTION);
|
|
});
|
|
|
|
test('descriptions teach "prose, not executable code" + the follow-the-prose protocol', () => {
|
|
expect(LIST_SKILLS_DESCRIPTION).toContain('NOT executable code');
|
|
expect(LIST_SKILLS_DESCRIPTION).toContain('get_skill');
|
|
expect(GET_SKILL_DESCRIPTION).toContain('nothing to');
|
|
expect(GET_SKILL_DESCRIPTION).toContain('same-named MCP tool');
|
|
});
|
|
|
|
test('descriptions surface usable/unavailable tool honesty', () => {
|
|
expect(LIST_SKILLS_DESCRIPTION).toContain('usable_tools');
|
|
expect(LIST_SKILLS_DESCRIPTION).toContain('unavailable_tools');
|
|
expect(GET_SKILL_DESCRIPTION).toContain('unavailable_tools');
|
|
});
|
|
|
|
test('both ops are read-scope, non-localOnly (thin clients reach them over HTTP)', () => {
|
|
for (const n of ['list_skills', 'get_skill']) {
|
|
expect(operationsByName[n].scope).toBe('read');
|
|
expect(operationsByName[n].localOnly).toBeFalsy();
|
|
expect(operationsByName[n].mutating).toBeFalsy();
|
|
}
|
|
});
|
|
|
|
test('instructions envelope is shaped + load-bearing', () => {
|
|
expect(SKILL_CATALOG_INSTRUCTIONS.summary).toContain('not executable tools');
|
|
expect(SKILL_CATALOG_INSTRUCTIONS.how_to_use.length).toBeGreaterThanOrEqual(3);
|
|
expect(SKILL_CATALOG_INSTRUCTIONS.how_to_use.join(' ')).toContain('get_skill');
|
|
expect(SKILL_CLIENT_GUIDANCE.nature).toContain('not code to execute');
|
|
expect(SKILL_CLIENT_GUIDANCE.protocol.length).toBeGreaterThanOrEqual(3);
|
|
});
|
|
|
|
test('operations array carries both new ops', () => {
|
|
const names = operations.map(o => o.name);
|
|
expect(names).toContain('list_skills');
|
|
expect(names).toContain('get_skill');
|
|
});
|
|
});
|