Skip to content

v0.23.0 feat: gbrain dream synthesizes conversations into brain pages (v0.23.0)#462

Merged
garrytan merged 14 commits intomasterfrom
garrytan/transcript-dream
Apr 30, 2026
Merged

v0.23.0 feat: gbrain dream synthesizes conversations into brain pages (v0.23.0)#462
garrytan merged 14 commits intomasterfrom
garrytan/transcript-dream

Conversation

@garrytan
Copy link
Copy Markdown
Owner

@garrytan garrytan commented Apr 26, 2026

Summary

Extends gbrain dream from a 6-phase brain-maintenance cycle into an 8-phase one that consolidates yesterday's conversations into long-term memory. Two new phases run alongside the existing tidying:

Synthesize reads conversation transcripts (OpenClaw session corpus, meeting transcripts, ad-hoc files via --input) and writes brain-native pages: reflections at wiki/personal/reflections/YYYY-MM-DD-<topic>-<hash[:6]>, originals at wiki/originals/ideas/..., and timeline entries on existing people pages. Cheap Haiku verdicts cached in a new dream_verdicts table filter routine ops sessions before any Sonnet call. One subagent per worth-processing transcript, dispatched in parallel up to the rate-lease cap.

Patterns runs after extract (so the graph state is fresh) and surfaces recurring themes across recent reflections — when ≥3 reflections within lookback_days (default 30) mention the same motif, a pattern page is written to wiki/personal/patterns/<theme> citing every reflection that constitutes its evidence.

Phase order: lint → backlinks → sync → synthesize → extract → patterns → embed → orphans.

Trust boundary — subagent put_page calls are bounded to a path allow-list sourced from skills/_brain-filing-rules.json's dream_synthesize_paths.globs. Even on prompt-injection success, writes are bounded to the allow-listed prefixes. The legacy wiki/agents/<id>/... namespace check stays the default for non-cycle subagents (regression guard).

Idempotency — slug includes content-hash suffix, so edited transcripts produce new files alongside the old (no silent overwrite). Idempotency key is dream:synth:<file_path>:<content_hash>. Cooldown via dream.synthesize.last_completion_ts config key (default 12h) bounds spend at ~$1-2/day under autopilot.

Provenance — orchestrator collects slugs from subagent_tool_executions (NOT pages.updated_at — would pick up unrelated writes). Subagent never gets fs-write access; orchestrator does the dual-write at phase boundary.

Auto-commit / push and daily token budget deferred to v1.1 per codex finding #5.

Commits (chronological):

  • feat: dream_verdicts schema + engine methods — schema migration v30 + engine interface
  • feat: trusted-workspace allow-list for subagent put_pageallowedSlugPrefixes context, matchesSlugAllowList helper, IRON RULE security regression tests
  • feat: cycle scaffolding — 8-phase order + transcript discoveryALL_PHASES extension, yieldDuringPhase hook, transcript discovery module
  • feat: synthesize + patterns phases — gbrain dream actually dreams — both phase implementations, CLI flags, all unit + E2E tests
  • docs: dream cycle v0.23.0 — skills, CLAUDE.md, migration, changelog — release notes, migration narrative, skill updates
  • Merge origin/master into garrytan/transcript-dream — merge of v0.21.0 (Cathedral II) + v0.22.0 (source ranking)
  • test: add patterns E2E + 8-phase cycle E2E + bump synth-cooldown timeouts — additional E2E coverage
  • fix: ship-prep — typecheck fixes, llms.txt regen, 8-phase test update — typecheck fixes after merge

Test Coverage

NEW CODE PATHS (33 paths, 100% covered)
[+] src/core/cycle/synthesize.ts — 8 branches all tested (★★★ × 7, ★★ × 1)
[+] src/core/cycle/patterns.ts — 5 branches all tested (★★★ × 4, ★★ × 1)
[+] src/core/operations.ts (allow-list path) — 9 cases covering glob, ALLOW/REJECT, namespace fallback regression, FAIL-CLOSED
[+] src/core/cycle.ts ALL_PHASES + yieldDuringPhase — full 8-phase E2E
[+] src/commands/dream.ts CLI flags — argv parsing + conflict detection
[+] dream_verdicts table + engine methods — schema migration + roundtrip E2E

COVERAGE: 33/33 (100%)  |  QUALITY: ★★★:30 ★★:3  |  GAPS: 0
Tests: +91 new (66 unit + 25 E2E across 8 new test files)

Coverage gate: PASS (100%). Full suite: 2793 pass / 0 fail / 258 skip in 722s.

Pre-Landing Review

Covered by /plan-eng-review at plan stage (15 issues found and resolved across architecture / code quality / tests / performance) plus codex outside voice (8 findings, all resolved per the resolution bundle). Implementation followed the resolved plan; the test evidence covers the result. No new structural issues surfaced during ship.

Highlights from the plan-stage review (all addressed):

Eval Results

No prompt-related Rails files changed — gbrain doesn't use the eval harness in this branch's diff. Eval suites for synthesis quality + significance accuracy ship in the sibling gbrain-evals repo as a follow-up.

Plan Completion

Plan: ~/.claude/plans/system-instruction-you-are-working-iridescent-volcano.md

PLAN COMPLETION: 17/17 DONE
- dream_verdicts schema (migration v30) — DONE
- Engine getDreamVerdict / putDreamVerdict (Postgres + PGLite) — DONE
- OperationContext.allowedSlugPrefixes + put_page allow-list check — DONE
- matchesSlugAllowList helper with glob support — DONE
- Auto-link enabled for trusted-workspace path — DONE
- Subagent handler-data plumbing (allowed_slug_prefixes) — DONE
- skills/_brain-filing-rules.json dream_synthesize_paths.globs — DONE
- src/core/cycle.ts 8-phase order + yieldDuringPhase hook — DONE
- src/core/cycle/transcript-discovery.ts (word-boundary regex, date filters) — DONE
- src/core/cycle/synthesize.ts (orchestrator dual-write, slug-from-children) — DONE
- src/core/cycle/patterns.ts (gather + reconcile, runs after extract) — DONE
- src/commands/dream.ts CLI flags (--input/--date/--from/--to) — DONE
- skills/migrations/v0.23.0.md — DONE
- skills/maintain/SKILL.md + RESOLVER.md + filing-rules.md updates — DONE
- 4 unit + 4 E2E test files (91 cases total) — DONE
- Auto-commit deferred to v1.1 (codex finding #5) — DONE
- Token budget deferred to v1.1 (cooldown is the v1 spend cap) — DONE

Verification Results

No dev server (CLI feature) — plan verification skipped. Functional verification covered by the 91-case unit + E2E suite.

TODOS

No TODOS.md items closed by this branch — net-new feature, not a fix for tracked work.

Documentation

Two non-skill docs updated for the v0.23.0 dream cycle (most user-facing docs were already covered in commit 6b80e07):

  • README.mdmaintain skill row now notes the synthesize + patterns phases; the gbrain dream command-reference block describes the 8-phase pipeline and the new --input <file>, --date YYYY-MM-DD, --from/--to ad-hoc/backfill flags.
  • INSTALL_FOR_AGENTS.md — Step 7 (Recurring Jobs) dream-cycle bullet calls out v0.23+ conversation synthesis and cross-session pattern detection so new installs understand what nightly cron earns them.

Already up to date (v0.23.0 was prepped in earlier branch commits): CHANGELOG.md, CLAUDE.md, VERSION, skills/maintain/SKILL.md, skills/RESOLVER.md, skills/_brain-filing-rules.{md,json}, skills/migrations/v0.23.0.md, llms.txt, llms-full.txt.

Test plan

  • Full bun test suite passes (2793 pass / 0 fail / 258 skip across 3051 tests in 177 files)
  • All 91 dream-related tests pass (66 unit + 25 E2E)
  • All regression-sensitive tests pass (parity, cli, brain-allowlist, migrate)
  • Schema migration v30 applies cleanly on fresh PGLite + Postgres
  • gbrain dream --help documents the 8-phase pipeline and new flags
  • gbrain dream --phase synthesize --dry-run runs Haiku verdict, skips Sonnet, produces zero pages

🤖 Generated with Claude Code

garrytan and others added 11 commits April 26, 2026 14:15
Adds the v25 schema migration creating the dream_verdicts table
(file_path, content_hash, worth_processing, reasons, judged_at;
PRIMARY KEY (file_path, content_hash); RLS-enabled when running as
a BYPASSRLS role).

Distinct from raw_data (which is page-scoped) — transcripts being
judged for synthesis aren't pages. The (file_path, content_hash)
key means edited transcripts re-judge automatically.

BrainEngine gains:
- DreamVerdict + DreamVerdictInput types
- getDreamVerdict(filePath, contentHash) → DreamVerdict | null
- putDreamVerdict(filePath, contentHash, verdict) — ON CONFLICT upsert

Both engines implement (postgres-engine.ts, pglite-engine.ts).

This commit alone is functionally inert — nothing reads/writes the
table yet. The synthesize phase (later commit) is the consumer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds OperationContext.allowedSlugPrefixes — when set, put_page
enforces slug membership in the allow-list instead of the legacy
wiki/agents/<id>/... namespace. The trust signal is the SUBMITTER
(PROTECTED_JOB_NAMES gates subagent submission so MCP can't reach
this field), not the runtime ctx.remote flag — every subagent tool
call has remote=true for auto-link safety, so basing trust on
remote is incoherent.

matchesSlugAllowList(slug, prefixes) helper supports glob suffix
'/*' (recursive — wiki/originals/* matches ideas/foo/bar) and
exact match for unsuffixed entries.

put_page check shape:
  if (viaSubagent && allowedSlugPrefixes set) → allow-list check
  else if (viaSubagent) → existing namespace check (regression guard)
  else → no check (regular CLI)

Auto-link is re-enabled for the trusted-workspace path so the cycle's
extract phase doesn't have to recompute every edge after synthesize
writes. Untrusted remote writes still skip auto-link as before.

SubagentHandlerData.allowed_slug_prefixes is the wire field; the
synthesize/patterns phases (later commit) populate it from a single
source of truth in skills/_brain-filing-rules.json's
dream_synthesize_paths.globs array. The model's tool schema description
mirrors the allow-list so it writes correct slugs on the first try.

IRON RULE security tests:
- test/operations-allow-list.test.ts: allow-list ALLOW/REJECT, glob
  semantics, regression guard for the v0.15 namespace fallback when
  allow-list is unset, FAIL-CLOSED when subagentId is missing.
- test/e2e/dream-allow-list-pglite.test.ts: end-to-end on PGLite,
  poisoned-transcript style write outside allow-list → REJECTED.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends ALL_PHASES from 6 → 8: synthesize between sync and extract,
patterns between extract and embed. Codex finding #7: patterns MUST
run after extract because subagent put_page sets ctx.remote=true and
skips auto-link/timeline by default — extract is the canonical edge
materialization step. Without that ordering, patterns reads stale
graph state.

Final order:
  lint → backlinks → sync → synthesize → extract → patterns → embed → orphans

CycleOpts gains:
- yieldDuringPhase callback — generic in-phase keepalive for long
  waits (synthesize fan-out, patterns roll-up). Renews cycle-lock TTL
  + worker job lock. Mirrors yieldBetweenPhases shape.
- synthInputFile / synthDate / synthFrom / synthTo — forwarded to
  runPhaseSynthesize for the CLI's --input/--date/--from/--to flags.

CycleReport.totals additively grows (no schema_version bump):
  transcripts_processed, synth_pages_written, patterns_written.

src/core/cycle/transcript-discovery.ts is a pure filesystem walk:
- .txt files only, sorted by path for determinism
- date-prefixed basename filter (--date / --from / --to)
- min_chars filter (default 2000)
- exclude_patterns auto-wraps bare words as \b<word>\b regex (Q-3),
  power users may pass full regex with anchors
- compileExcludePatterns is exported for unit tests

Phase implementations land in the next commit; this one only adds
the dispatcher slots so commit-by-commit bisect doesn't crash on
import-not-found.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Synthesize phase (src/core/cycle/synthesize.ts) reads conversation
transcripts from dream.synthesize.session_corpus_dir and writes
brain-native pages: reflections to wiki/personal/reflections/...,
originals to wiki/originals/ideas/..., timeline entries on existing
people pages.

Pipeline:
  1. discoverTranscripts (filesystem walk + filters)
  2. cooldown check via dream.synthesize.last_completion_ts config
     (default 12h; bypassed by --input/--date/--from/--to)
  3. cheap Haiku verdict per transcript, cached in dream_verdicts
     table keyed by (file_path, content_hash) — backfill re-runs
     skip already-judged transcripts at zero cost
  4. fan-out: one Sonnet subagent per worth-processing transcript
     dispatched with allowed_slug_prefixes (read from
     skills/_brain-filing-rules.json's dream_synthesize_paths.globs)
     and idempotency_key dream:synth:<file_path>:<content_hash>
  5. wait via waitForCompletion; yieldDuringPhase ticks every child
     terminal so the cycle-lock TTL refreshes on long backfills
  6. collect slugs from subagent_tool_executions for each child
     (codex finding #2: NOT pages.updated_at, which would pick up
     unrelated writes)
  7. orchestrator dual-write — query each new page from DB,
     reverse-render via serializeMarkdown, write file to brain_dir.
     Subagent never gets fs-write access.
  8. deterministic summary index page at dream-cycle-summaries/<date>
     (codex finding #4: slug shape is regex-compatible — no
     underscores, no .md extension)
  9. write completion timestamp ONLY on successful runs

Patterns phase (src/core/cycle/patterns.ts) runs after extract so
the graph state is fresh. Single Sonnet subagent gathers reflections
within dream.patterns.lookback_days (default 30); names a pattern
only when ≥dream.patterns.min_evidence (default 3) reflections
support it. Same allow-list path as synthesize.

CLI flags on `gbrain dream` (src/commands/dream.ts):
  --input <file>      ad-hoc transcript synthesis (implies
                      --phase synthesize; bypasses cooldown)
  --date YYYY-MM-DD   restrict synthesize to one date
  --from <d> --to <d> backfill range
  --dry-run           runs Haiku verdict (cached), skips Sonnet
                      synthesis. NOT zero LLM calls (codex #8).

Conflict detection: --input + --date/--from/--to exits 2.
ISO 8601 date format validated; range start > end exits 2.

Auto-commit / push deferred to v1.1 (codex finding #5). v1 writes
files to brain_dir; user or autopilot handles git.

Tests:
- test/cycle-patterns.test.ts: structural assertions on the patterns
  phase (queue + waitForCompletion wired, allow-list threading,
  subagent_tool_executions provenance, no raw_data dependency).
- test/dream-cli-flags.test.ts: argv parsing, conflict detection,
  ISO date validation, --input implies --phase synthesize, dry-run
  semantics doc string.
- test/e2e/dream-synthesize-pglite.test.ts: 8 cases on PGLite
  in-memory exercising not_configured, empty corpus, no API key
  skip path, dry-run, cooldown active vs --input bypass, and the
  dream_verdicts cache hit path. Per-test rig isolation (each
  test creates and tears down its own engine) avoids
  cross-test PGLite WASM contention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- skills/maintain/SKILL.md: synthesize + patterns phases documented
  with quality bar (Iron Law for synthesis), trust boundary, idempotency,
  cooldown semantics, CLI invocation patterns. New triggers added so
  "process today's session" / "synthesize my conversations" route here.
- skills/RESOLVER.md: dream cycle triggers route to maintain.
- skills/_brain-filing-rules.md: directory table for the five output
  types (reflections, originals, patterns, people enrichment, cycle
  summary) with slug shape per row; Iron Law repeated.
- skills/migrations/v0.27.0.md: agent-readable migration narrative.
  Schema migration v25 runs automatically on `gbrain apply-migrations`;
  synthesize ships disabled by default — opt-in via
  dream.synthesize.session_corpus_dir + dream.synthesize.enabled.
- CLAUDE.md: file inventory updated with new files (cycle/synthesize.ts,
  cycle/patterns.ts, cycle/transcript-discovery.ts), the 8-phase
  ordering, the trusted-workspace allow-list trust model, and the v25
  schema migration line in the migrate.ts entry.
- VERSION: 0.20.4 → 0.27.0
- CHANGELOG.md: v0.27.0 release-summary section per CLAUDE.md voice
  rules (numbers that matter table, what-this-means closer, "to take
  advantage of" block), followed by the itemized changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges v0.21.0 (Code Cathedral II — call-graph edges, two-pass
retrieval, parent-scope chunking) and v0.22.0 (source-aware search
ranking) from master into the transcript-dream branch.

Conflict resolutions:
- VERSION: kept this branch's reserved 0.27.0 slot (master at 0.22.0;
  intermediate slots 0.22.1-0.26.0 are claimed by sibling branches).
- CHANGELOG.md: kept v0.27.0 entry at top, then master's v0.22.0 +
  v0.21.0 entries below in version order.
- src/core/migrate.ts: master added migrations v25-v29 (Cathedral II);
  this branch's dream_verdicts migration moved from v25 → v30 to slot
  cleanly after master's last migration.

The dream_verdicts table DDL is unchanged. Only the migration version
number changed. Tests pass: 66/66 dream tests, 91/91 regression-
sensitive tests (parity, cli, brain-allowlist, migrate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…outs

Two new E2E test files on PGLite (no DATABASE_URL or API key required):

- test/e2e/dream-patterns-pglite.test.ts (6 cases) — exercises
  runPhasePatterns skip paths against a real engine: disabled,
  default-enabled-but-insufficient-evidence, no-API-key, dry-run.
  Sibling of dream-synthesize-pglite.test.ts; same per-test rig
  pattern for engine isolation.

- test/e2e/dream-cycle-eight-phase-pglite.test.ts (5 cases) —
  end-to-end runCycle with the v0.27 8-phase order. Asserts:
  ALL_PHASES is the documented 8 phases in the right sequence,
  the dry-run report's phases array preserves that order,
  CycleReport.totals carries the new transcripts_processed /
  synth_pages_written / patterns_written fields, --phase synthesize
  and --phase patterns each run only that phase, and synthInputFile
  is plumbed correctly through runCycle to runPhaseSynthesize.

Bump per-test timeout to 30s on the two synthesize-cooldown E2E
tests that create two PGLite engines back-to-back. Default Bun 5s
budget is tight under sustained suite pressure (PGLite WASM init
costs ~1-2s per engine on macOS); each test passes alone but flakes
in the full E2E suite. The third arg `30_000` is Bun's standard
test-timeout knob.

Full E2E suite (test/e2e/) now: 86 pass / 0 fail / 258 skip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/core/cycle/synthesize.ts + patterns.ts: PageType 'default' → 'note'
  (TS strict typecheck rejected 'default'; 'note' is a valid PageType
  for orchestrator-written summary index pages and reverse-render fallback).
- src/core/pglite-engine.ts: re-import DreamVerdict + DreamVerdictInput
  types after the master merge dropped them from the import line.
- test/e2e/dream-allow-list-pglite.test.ts: ToolCtx now requires
  remote: true literal; thread it through every put_page tool call.
- test/e2e/dream-patterns-pglite.test.ts: PageType 'default' → 'note'
  in the seedReflections helper.
- test/core/cycle.test.ts: bump expected hook-call count + phase count
  6 → 8 to match v0.27 ALL_PHASES extension.
- llms-full.txt: regenerate against the updated CHANGELOG + CLAUDE.md
  so the committed snapshot matches what the generator now produces.

Full bun test suite: 2793 pass / 0 fail / 258 skip (3051 tests, 177 files).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README: maintain skill row mentions synthesize/patterns; gbrain dream
command-reference block describes the 8-phase pipeline and the new
--input/--date/--from/--to flags.

INSTALL_FOR_AGENTS: dream cycle bullet calls out v0.27 conversation
synthesis + cross-session pattern detection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Merges 5 master commits since last merge: v0.22.1 autopilot fix wave (#447),
v0.22.2 minions worker reliability (#458), v0.22.4 frontmatter-guard (#448),
sourceId in cycle sync phase (#475), and post-migration schema verification (#488).

Conflict resolutions:
- VERSION: kept this branch's reserved 0.27.0 slot (master at 0.22.6).
- CHANGELOG.md: kept v0.27.0 entry at top, then master's v0.22.6 → v0.21.0 entries below in order.
- CLAUDE.md: merged the v0.27 cycle bullet (8 phases, synthesize, patterns, transcript-discovery, dream CLI flags) with master's v0.22.1/v0.22.5 cycle additions (signal: AbortSignal, willRunExtractPhase, resolveSourceForDir).
- src/core/cycle.ts: kept v0.27 yieldDuringPhase + synthInputFile/synthDate/synthFrom/synthTo CycleOpts fields AND added master's v0.22.1 signal: AbortSignal field. Both coexist.
- llms-full.txt: regenerated against the merged tree.

The dream_verdicts schema migration moved v25 → v30 in the prior merge.
Master ended at v29 (cathedral_ii_code_edges_rls); v30 is uncontested.

Tests pass post-merge: 105/105 dream + cycle tests across 9 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Master is at v0.22.5; v0.23.0 is the next natural slot for the dream-cycle
synthesize + patterns release. Bulk rename across VERSION, package.json,
CHANGELOG, migration file, source comments, skills, and llms.txt bundles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@garrytan garrytan changed the title feat: gbrain dream synthesizes conversations into brain pages (v0.27.0) feat: gbrain dream synthesizes conversations into brain pages (v0.23.0) Apr 28, 2026
…-dream

# Conflicts:
#	CHANGELOG.md
#	CLAUDE.md
#	VERSION
#	llms-full.txt
#	package.json
@garrytan garrytan changed the title feat: gbrain dream synthesizes conversations into brain pages (v0.23.0) v0.23.0 feat: gbrain dream synthesizes conversations into brain pages (v0.23.0) Apr 29, 2026
garrytan added a commit that referenced this pull request Apr 30, 2026
PR #506 claims v0.22.15, PR #521 claims v0.22.10, intermediate slots
(.11/.12/.13/.14) are claimed by other open PRs. v0.22.16 is the next
clean PATCH slot. v0.23.0 is claimed by PR #462 so MINOR isn't free.
This release fits the 0.22.x train; v0.23.0 lands when #462 ships.

Updates VERSION, package.json, CHANGELOG.md header, TODOS.md follow-up
labels. Code is unchanged.
garrytan added a commit that referenced this pull request Apr 30, 2026
…arness (#522)

* feat: hermeticity migration — every $GBRAIN_HOME write site honors the env override

configDir() in src/core/config.ts already implemented $GBRAIN_HOME as a
parent-dir override (returns <override>/.gbrain), but ~12 consumers built paths
from os.homedir() directly and bypassed it. Critically, loadConfig/saveConfig
themselves used a private getConfigDir() that ignored the env. Fixed.

Migrated every write site to gbrainPath() — fail-improve, validator-lint, cycle
lock, shell-audit, backpressure-audit, sync-failures, integrity logs,
integrations heartbeat, init pglite path, migrate-engine manifest, import
checkpoint, v0_13_1 rollback, v0_14_0 host-work. Read-side host-detection in
init.ts (~/.claude / ~/.openclaw probes) intentionally NOT migrated; that's a
v1.1 follow-up under a separate $GBRAIN_HOST_HOME override.

Adds gbrainPath(...segments) sugar plus path validation: $GBRAIN_HOME must be
absolute and contain no '..' segments (throws GbrainHomeInvalidError).

test/gbrain-home-isolation.test.ts proves write-isolation across all migrated
sites. test/migrations-v0_14_0.test.ts updated to use $GBRAIN_HOME instead of
the old HOME-swap pattern.

Closes part of the claw-test E2E harness preconditions (D13 + D21).

* feat: gbrain friction {log,render,list,summary} — agent friction reporter

Append-only JSONL writer at $GBRAIN_HOME/friction/<run-id>.jsonl. Schema is a
flat extension of StructuredAgentError (D20), one envelope shape across both
agent-emitted entries and harness-wrapped command failures. Run-id resolves
from --run-id > $GBRAIN_FRICTION_RUN_ID > 'standalone'.

Subcommands stay ≤30 LOC each; core lives in src/core/friction.ts (writer +
reader + renderer + redactor). render --redact (default for md output) strips
\$HOME / \$CWD to placeholders so reports paste safely in PRs/issues.

Severity: confused | error | blocker | nit. Kind: friction | delight (D7) |
phase-marker | interrupted. Readers tolerate malformed lines (skip + warn).

40 unit tests; this is the channel the claw-test harness writes to and that
agents emit through during live-mode runs.

* feat: gbrain claw-test — end-to-end fresh-install friction harness

Two modes: scripted (CI gate, no agent) and --live (real agent subprocess).
Phases: setup → install_brain (gbrain init --pglite) → import (--no-embed) →
query → extract all --source fs → verify (gbrain doctor --json, asserts
status==='ok' and progress.jsonl phase coverage).

AgentRunner interface + registry — interface stays narrow (detect, invoke,
optional postInstallHook). v1 ships only OpenClawRunner; the registry pattern
lets v1.1 land hermes/codex as ~50-line additions without refactoring callers.
OpenClaw invocation: 'openclaw agent --local --agent <name> --message <brief>'
matching test/e2e/skills.test.ts (NOT --prompt-file, which doesn't exist).

transcript-capture: spawns child with piped stdio, async-drains via
fs.createWriteStream + 'drain' events so 256KB+ bursts don't stall the child
(D17 backpressure). Writes <run>/transcript.jsonl with schema_version + ts +
channel + byte_offset + bytes_b64. Friction entries' transcript_offset field
references byte offsets here so render --transcripts can resolve back.

progress-tail: parses gbrain's --progress-json events out of child stderr.
Phase verification asserts each scenario.expected_phases entry (dotted names
like import.files, extract.links_fs, doctor.db_checks) saw at least one event
from the actual command — proves the COMMAND ran, not that the agent obeyed
prompts.

seed-pglite: ~50 LOC SQL replay primitive for the upgrade-from-v0.18 scenario.
Existing migration helpers (test/e2e/helpers.ts) are Postgres-only; PGLite has
no equivalent. seedPglite opens a fresh PGLite, executes each statement
individually (errors name the failing one), then disconnects so gbrain init
can take over and walk forward.

53 unit tests covering registry selection, runner detection, multi-byte UTF-8
chunk-boundary safety, PIPE buffer drain, scenario load+validate, progress
event parsing, and SQL splitter.

* feat: claw-test scenario fixtures + friction-protocol skills convention

Two scenarios ship in v1 — fresh-install and upgrade-from-v0.18. Each is a
self-contained directory: brain/ (markdown pages), BRIEF.md (live-mode prompt),
expected.json (scripted-mode assertions), scenario.json (kind, expected_phases,
optional from_version + seed paths). Schema is owned by src/core/claw-test/
scenarios.ts.

upgrade-from-v0.18 ships scaffolded — seed/dump.sql is the v1.1 follow-up
(needs a real v0.18-shape PGLite dump; seed/README.md documents the gen
procedure). The harness gracefully no-ops the seed phase when dump.sql is
absent.

skills/_friction-protocol.md is a cross-cutting convention skill (like
_brain-filing-rules.md). Tells agents when to call gbrain friction log and how
to choose severity. Skills the claw-test exercises will gain a > Convention:
callout pointing here in a v1.1 sweep.

13 unit tests for the scenario loader + 'shipped scenarios load cleanly' for
both.

* feat: register gbrain claw-test + gbrain friction; CLAUDE.md + llms sync

Wires both commands into src/cli.ts CLI_ONLY allow-list and adds dispatch
in handleCliOnly so neither command requires a brain engine connection.

CLAUDE.md gains entries for src/commands/{friction,claw-test}.ts +
src/core/claw-test/ + skills/_friction-protocol.md, and a Commands section
listing all 8 new gbrain claw-test ... and gbrain friction ... invocations
with the v0.23 marker. Documents the GBRAIN_HOME write-isolation contract
and the v1 caveat (read-side host-fingerprint detection deferred to v1.1).
llms.txt + llms-full.txt regenerated via 'bun run build:llms' so the
committed generator-output gate passes.

test/e2e/claw-test.test.ts is the scripted-mode E2E. Builds a tiny shim that
delegates to 'bun run src/cli.ts' (NOT bun --compile, which doesn't bundle
PGLite's runtime assets), points the harness at it via GBRAIN_BIN_OVERRIDE,
runs --scenario fresh-install end-to-end. Asserts exit 0, zero error/blocker
friction. Includes a deliberate-break test that proves the friction signal
fires when a phase command rejects.

test/claw-test-cli.test.ts covers shipped-scenario load + agent registry +
OpenClawRunner detection (relative-path / .. / missing-bin guards) + the
GBRAIN_FRICTION_RUN_ID env handoff between harness and friction CLI.

Closes the v0.23 claw-test E2E feature.

* chore: bump version and changelog (v0.24.0)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(tests): typecheck failures + spawnWithCapture timeout headroom in CI

Three CI fixes after PR #522 landed:

1. test/agent-runner.test.ts:89 — UnavailableRunner.invoke() returns
   Promise<void> by default but the AgentRunner contract requires
   Promise<InvokeResult>. Annotate the throw-only invoke explicitly so tsc
   sees the contract is satisfied (the throw makes the body unreachable as
   far as the return type is concerned).

2. test/seed-pglite.test.ts — bun:test signature is test(name, fn, timeoutMs:
   number), not test(name, opts: {timeout}, fn). The {timeout: 30_000} object
   form was a guess that tsc on bun 1.3.13 rejects. Move the 30s cap to the
   trailing positional number arg on each PGLite-using test.

3. test/transcript-capture.test.ts — `spawnWithCapture > timeout fires
   SIGTERM/SIGKILL` blew the 10s outer cap on the GitHub runner. Two fixes:
   (a) use `exec sleep` so the child we spawn IS sleep — SIGTERM goes
   directly to it, no `/bin/sh` fork-vs-exec process-group ambiguity that
   could orphan the sleep and force the SIGKILL grace path. (b) bump outer
   cap to 30s for headroom even when the runner is slow and SIGKILL after
   the 5s grace is what actually ends the child.

* chore: rebump to v0.22.16 (next free 0.22.x patch slot per queue)

PR #506 claims v0.22.15, PR #521 claims v0.22.10, intermediate slots
(.11/.12/.13/.14) are claimed by other open PRs. v0.22.16 is the next
clean PATCH slot. v0.23.0 is claimed by PR #462 so MINOR isn't free.
This release fits the 0.22.x train; v0.23.0 lands when #462 ships.

Updates VERSION, package.json, CHANGELOG.md header, TODOS.md follow-up
labels. Code is unchanged.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
garrytan and others added 2 commits April 30, 2026 00:14
…-dream

# Conflicts:
#	CHANGELOG.md
#	CLAUDE.md
#	VERSION
#	llms-full.txt
#	package.json
The dry-run full-cycle test asserted 6 phases. v0.23 added synthesize
and patterns, bringing the total to 8. The unit-side equivalent
(test/core/cycle.test.ts) was already updated; this catches the
E2E sibling that surfaced after the latest master merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@garrytan garrytan merged commit 527b87b into master Apr 30, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant