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>
This commit is contained in:
Garry Tan
2026-06-14 09:46:03 -07:00
co-authored by Claude Fable 5
parent 3c974d9597
commit d01185baf7
5 changed files with 63 additions and 2 deletions
+11
View File
@@ -34,6 +34,17 @@ flows through in both directions.
> **Convention:** See `skills/conventions/brain-first.md` for the 5-step lookup protocol.
> **Convention:** See `skills/conventions/quality.md` for citation and back-link rules.
> **Memory verbs (MEMORY_VERBS v1, gbrain ≥ 0.43).** Over MCP, prefer the five
> frozen memory verbs for the read/write cycle: **`remember(fact, provenance,
> ttl?)`** to save a single durable fact (mandatory provenance; dedupes +
> supersedes), **`recall(query | entity, budget_tokens)`** to read it back
> budget-packed, **`entity(name)`** for a zero-LLM card, **`synthesize(question)`**
> for the expensive cross-page answer, **`forget(id)`** to expire a fact. Use
> `remember` instead of `extract_facts` when you already have ONE formed fact;
> `put_page` / `add_link` / `add_timeline_entry` stay the page/graph write path.
> Fall back to the classic ops when the verbs aren't on the surface. Contract:
> `docs/protocol/MEMORY_VERBS_v1.md`.
## Contract
This skill guarantees:
+15
View File
@@ -33,6 +33,21 @@ mutating: false
Answer questions using the brain's knowledge with 3-layer search and synthesis.
> **Memory verbs (MEMORY_VERBS v1, gbrain ≥ 0.43).** When connected to a brain
> over MCP, prefer the five frozen memory verbs for memory work — they carry
> provenance, evidence, and a server-enforced token budget:
> - **`recall(query | entity, budget_tokens)`** — the budget-packed memory read.
> Use it instead of bare `search` for "what do we know that we SAVED about X".
> - **`entity(name)`** — a zero-LLM person/company/project card (aliases,
> last-touched, open threads, top edges). Use it instead of `get_page` +
> `get_backlinks` when you just need the card.
> - **`synthesize(question)`** — the explicitly-expensive cross-page answer; the
> heavy version of `query`. Reach for it only when the answer must combine
> evidence across pages.
> Fall back to `search`/`query`/`get_page` when the verbs aren't on the surface
> (older brains, or `gbrain serve --surface full`). See
> `docs/protocol/MEMORY_VERBS_v1.md`.
## Contract
This skill guarantees:
+2
View File
@@ -26,6 +26,7 @@ import { v0_28_0 } from './v0_28_0.ts';
import { v0_29_1 } from './v0_29_1.ts';
import { v0_31_0 } from './v0_31_0.ts';
import { v0_32_2 } from './v0_32_2.ts';
import { v0_43_0 } from './v0_43_0.ts';
export const migrations: Migration[] = [
v0_11_0,
@@ -43,6 +44,7 @@ export const migrations: Migration[] = [
v0_29_1,
v0_31_0,
v0_32_2,
v0_43_0,
];
/** Look up a migration by exact version string. */
+33
View File
@@ -0,0 +1,33 @@
/**
* v0.43.0.0 migration — MEMORY_VERBS v1 (Cathedral 1).
*
* PITCH-ONLY. There is NO schema or data migration: the five frozen memory
* verbs (recall/remember/entity/synthesize/forget) ride the existing facts,
* pages, and typed-graph tables. This entry exists solely so `gbrain
* post-upgrade` / the self-upgrade NOTIFY channel actively tells an existing
* install that the verbs landed and how to switch a harness onto them — the
* propagation path the verbs otherwise lacked (the default surface stays
* `full`, so an upgrade alone does NOT steer agents to the verbs).
*
* The orchestrator is a no-op that reports `complete` immediately —
* idempotent by construction (it does nothing), so apply-migrations records
* the ledger row and moves on.
*/
import type { Migration, OrchestratorOpts, OrchestratorResult } from './types.ts';
async function orchestrator(_opts: OrchestratorOpts): Promise<OrchestratorResult> {
// No schema/data work — MEMORY_VERBS v1 is a façade over existing tables.
return { version: '0.43.0.0', status: 'complete', phases: [] };
}
export const v0_43_0: Migration = {
version: '0.43.0.0',
featurePitch: {
headline:
'Five memory verbs — recall, remember, entity, synthesize, forget — are now the agent-facing memory protocol (MEMORY_VERBS v1).',
description:
'Point any MCP harness at `gbrain serve --surface verbs` to expose exactly these five self-describing tools instead of the full op wall: remember(fact, provenance) writes durable facts; recall(query|entity, budget_tokens) returns budget-packed memory; entity(name) is a zero-LLM card; synthesize(question) is the explicitly-expensive cross-page answer; forget(id) expires a fact. Existing `gbrain serve` (full surface) keeps working and now also lists the verbs, but `--surface verbs` is the clean agent surface. Set a default with `gbrain config set mcp_surface verbs`. Verify any endpoint with `gbrain protocol conformance`; see usage with `gbrain protocol stats`. Full contract: docs/protocol/MEMORY_VERBS_v1.md.',
},
orchestrator,
};
+2 -2
View File
@@ -108,7 +108,7 @@ describe('buildPlan — diff against completed + installed VERSION', () => {
// autopilot cooperative, v0.16.0 = subagent runtime, v0.18.0 = multi-
// source brains, v0.18.1 = RLS hardening, v0.21.0 = Cathedral II
// (renumbered from v0.20.0 after master shipped v0.20.x in parallel).
expect(plan.skippedFuture.map(m => m.version)).toEqual(['0.12.0', '0.12.2', '0.13.0', '0.13.1', '0.14.0', '0.16.0', '0.18.0', '0.18.1', '0.21.0', '0.22.4', '0.28.0', '0.29.1', '0.31.0', '0.32.2']);
expect(plan.skippedFuture.map(m => m.version)).toEqual(['0.12.0', '0.12.2', '0.13.0', '0.13.1', '0.14.0', '0.16.0', '0.18.0', '0.18.1', '0.21.0', '0.22.4', '0.28.0', '0.29.1', '0.31.0', '0.32.2', '0.43.0.0']);
});
test('already applied → v0.11.0 lands in `applied` bucket, not pending', () => {
@@ -148,7 +148,7 @@ describe('buildPlan — diff against completed + installed VERSION', () => {
// v0.22.4, v0.28.0, v0.29.1, v0.31.0 were added later; installed=0.12.0
// means they belong in skippedFuture, not pending. v0.11.0 and v0.12.0
// stay pending despite being ≤ installed — that is the H9 invariant.
expect(plan.skippedFuture.map(m => m.version)).toEqual(['0.12.2', '0.13.0', '0.13.1', '0.14.0', '0.16.0', '0.18.0', '0.18.1', '0.21.0', '0.22.4', '0.28.0', '0.29.1', '0.31.0', '0.32.2']);
expect(plan.skippedFuture.map(m => m.version)).toEqual(['0.12.2', '0.13.0', '0.13.1', '0.14.0', '0.16.0', '0.18.0', '0.18.1', '0.21.0', '0.22.4', '0.28.0', '0.29.1', '0.31.0', '0.32.2', '0.43.0.0']);
});
test('--migration filter narrows to one version', () => {