Files
gbrain/test/operations-descriptions.test.ts
Garry TanandClaude Fable 5 c5952b8714 v0.43.0.0 feat(mcp,search): MEMORY_VERBS v1 five-verb protocol + concept-question query steering (cathedral 1, #2416) (#2172)
* 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>
2026-08-09 17:25:00 -07:00

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