mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
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:
co-authored by
Claude Fable 5
parent
3c974d9597
commit
d01185baf7
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user