mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 08:53:22 +00:00
9c1a4b8fce14ddca8b5f7129e5300a5c2880dc88
178
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3523d8fd7e |
fix(jobs): unify-types worker defaults to dry-run per its handler contract (#1575) (#3574)
Co-Authored-By: Garry Tan <garrytan@gmail.com> |
||
|
|
13d95ba0ab |
fix(schema): apply mutation batches atomically so a mid-batch failure leaves the pack untouched (#2581) (#3446)
Adversarial review: survived a hostile reviewer plus two independent refuters, each told to assume the PR was broken and to default to refuting when uncertain. 19 of 62 PRs cleared that bar. Applying a schema mutation batch was not atomic: a failure partway through left earlier mutations permanently written. Reproduced on disk — a failure at index 2 left mutation 0 applied with no way to tell from the pack's state that it was half-done. The fix validates the whole batch first and writes once, which makes partial application impossible by construction rather than by careful ordering. Verified before merge: the failure was reproduced by injecting one rather than reasoning about it; the PR's own tests fail when the fix is reverted; typecheck clean; MERGEABLE/CLEAN at 22/22 on the current base after batches 1-4 landed. Sequenced last deliberately — it collides with #3531 on docs/architecture/KEY_FILES.md and with #3667 on src/core/operations.ts, both of which landed earlier today. Known gap, recorded rather than hidden: lock contention under concurrent writers was reasoned about, not stress-tested. |
||
|
|
022a443e9b |
fix(pricing): correct voyage-4-large rate and add the missing voyage-4 family entries (#3480)
Adversarial review: survived a hostile reviewer plus two independent refuters, each told to assume the PR was broken and to default to refuting when uncertain. 19 of 62 PRs cleared that bar. voyage-4-large was billed at the voyage-3-large rate — $0.18 against a published $0.12 — so every cost estimate using it was wrong by 50%. Corrected in the canonical table only, per CLAUDE.md's rule that every other pricing table is a derived view, and the drift guard passes. Rate checked against the live vendor page. Verified before merge: the PR's own tests fail when the production change is reverted; typecheck clean; MERGEABLE/CLEAN with 22/22 checks on the current base after batches 1-3 landed. Known gap, recorded rather than hidden: this PR previously failed the JSONB parity guard on a 32-commit-stale base. I rebased it onto current master and re-ran rather than accepting 'flaky' — the guard passes on the real base, 22/22 green. |
||
|
|
dba0ae7b1e |
fix(validation): qualify backlink endpoint identity (#3667)
Adversarial review: survived a hostile reviewer plus two independent refuters, each told to assume the PR was broken and to default to refuting when uncertain. 19 of 62 PRs cleared that bar. The back-link validator compared bare slugs, so a same-slug page in another source masked a genuinely missing reverse edge — silent under-reporting in exactly the multi-source setup where it matters. Now keyed on the full 4-tuple, per the `(source_id, slug)` uniqueness invariant. Verified on real Docker Postgres with 28/28 parity, which the PR itself had skipped. Verified before merge: the PR's own tests fail when the production change is reverted; typecheck clean; MERGEABLE/CLEAN on the current base after batches 1 and 2 landed, not a stale one. Known gap, recorded rather than hidden: remote MCP serialization of the additive Link fields is untested; the fields are additive JSON. |
||
|
|
335e470394 |
fix(ai): fold dashscope + google keys into gateway env, drop the retired Gemini default (#3500, #3510) (#3531)
Adversarial review: survived a hostile reviewer plus two independent refuters, each told to assume the PR was broken and to default to refuting when uncertain. 19 of 62 PRs cleared that bar. dashscope and google keys now fold into the gateway env — verified live end-to-end — and the retired gemini-1.5-pro default is swept from 9 files. Reverting the change fails 11 of 98 tests at fixed seams, and the budget-cap claim in the description reproduced. Landing first in the gateway/config-key seam, so #3648 rebases onto it. Verified before merge: the PR's own tests fail when the production change is reverted; typecheck clean; MERGEABLE/CLEAN on the current base after batch 1 landed, not a stale one. Known gap, recorded rather than hidden: no live provider call was made; the gemini retirement was taken from issue history rather than a vendor check. Minor follow-up to file: deriveEnvKey('google_api_key') yields a dead GOOGLE_API_KEY in the minion shell-inherit path. |
||
|
|
b4a9c7683d |
fix(search): fold the FTS configuration name into knobs_hash — stop stale rows surviving a reindex-search-vector language switch (#3677)
Adversarial review: survived a hostile reviewer plus two independent refuters, each told to assume the PR was broken and to default to refuting when uncertain. `GBRAIN_FTS_LANGUAGE` was absent from the query-cache key, so a language switch served stale pre-switch rows. The hash now folds it (v14→15) with all five pin sites updated; reverting the fix fails 4 of 15 tests at the exact claimed step. Landing first in the knobs_hash cluster — the constant is single-writer, so #3617 rebases onto this and takes 16. Verified before merge: the PR's own tests fail when the production change is reverted (11 of the previous 32 PRs failed exactly there — one had 7 of 8 new tests passing on master); typecheck clean; MERGEABLE/CLEAN with 22/22 checks green on the current base, not a stale one. |
||
|
|
3c61e25503 |
fix(reindex-frontmatter): reuse the connected engine instead of self-deadlocking on the PGLite lock (#1963) (#3558)
Adversarial review: survived two independent refuters — the only PR of 32 reviewed this way to do so. The bug: `gbrain reindex-frontmatter` and `gbrain backfill <kind>` were 100% dead on PGLite. cli.ts takes the data-dir lock, the command modules built a second engine on the same dir, and acquireLock never reaps a live PID — 30s timeout, exit 1, with the error naming the waiting process itself as the holder. Reproduced on the parent commit at 33.2s; passes in 5.0s with the fix. Root-cause fix at the dispatch layer, not a softening of the lock, and the sibling census confirmed these were the only two affected callers. Postgres path verified before merge (it was the review's one open gap, since the bug is PGLite-only and all verification had gone there while the change itself is connection-teardown ownership). Against real Postgres 16 + pgvector: reindex-frontmatter and all three registered backfills exit 0 with zero residual connections, zero advisory locks, and zero cycle-lock rows — byte-identical output and identical teardown to master on the same database, confirming the change is behavior-neutral there. Merged tree re-verified after rebase: typecheck clean, pglite-lock + reindex-frontmatter 16 pass, llms bundle fresh, 23/23 CI green. |
||
|
|
945fed6105 |
fix(engine): enforce static engine-live import boundaries (#3596)
* docs: design engine dynamic-import reconciliation Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): reconcile dynamic import hardening Co-Authored-By: Claude <noreply@anthropic.com> * test(engine): guard dynamic import policy * docs: plan engine dynamic-import reconciliation Record the approved TDD sequence for selective engine-path hardening, repository guard wiring, documentation, and local verification. Preserve the no-version-bump and no-publication boundaries for the remaining work. Co-Authored-By: Claude <noreply@anthropic.com> * docs(engine): record static import invariant * fix(engine): parse block comments in import guard Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): parse dynamic imports with TypeScript Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): close import guard bypasses Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): close parser guard edge cases Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): aggregate parser diagnostics Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): bound dynamic import marker directive Require the line-level opt-out marker to be standalone inside real comment trivia so negated or incidental longer tokens cannot authorize an import. Preserve the existing general marked-line contract and pin it with focused regression coverage. Co-Authored-By: Claude <noreply@anthropic.com> * fix(engine): close Unicode marker boundary bypasses Treat Unicode identifier continuations as marker-token characters and inspect adjacent text by code point so supplementary-plane characters cannot turn longer comment tokens into approvals.\n\nCo-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3df20f9f18 |
v0.42.67.0 fix(build): force LF for shell scripts and route package.json checks through bash (#3506)
* v0.42.67.0 fix(build): force LF for shell scripts and route package.json checks through bash Two independent defects left `bun run test`, `verify`, `ci:local` and `test:e2e` dead on Windows. All four dispatch through bash. First, every tracked *.sh is checked out with CRLF. The committed blobs are clean LF; system-level core.autocrlf=true rewrites them on checkout, and a strict bash then dies at run-unit-parallel.sh line 23 with "$'\r': command not found". A root .gitattributes pinning `*.sh text eol=lf` overrides autocrlf regardless of the contributor's git config. Second, 33 package.json scripts invoked `scripts/foo.sh` directly, which bun cannot exec via shebang on Windows. They now go through `bash`, matching the 11 that already did; all 59 tracked *.sh files are bash-shebanged (52 `#!/usr/bin/env bash`, 7 `#!/bin/bash`), so the change is uniform. The five `scripts/*.ts` entries still run under bun. Measured on this base, `bun run verify` goes from pass=1 fail=31 to pass=25 fail=7. Every one of the baseline's 29 `command not found: scripts/...` errors is gone; those were the shebang defect, and they account for the measured delta. The line-ending defect is verified structurally rather than by that number, because the bash on PATH for this measurement tolerates CR and so cannot exhibit it: under the new attribute all 59 tracked *.sh check out LF-only (0/59 carry a CR byte, against 59/59 before), and `git add --renormalize .` is a no-op, confirming the index was always correct and only the working tree was wrong. Zero content churn. All 7 residual failures also fail on the pristine baseline: four exceed the harness's 120s cap (standalone `bun run typecheck` exits 0), and check:wasm, check:skill-brain-first and check:resolver are pre-existing content or environment issues. check:resolver is not even a shell script. No behavior change on Linux or macOS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: sync docs to v0.42.67.0 CONTRIBUTING.md gains a Windows section: the `.gitattributes` LF pin makes a fresh clone correct with no extra steps, working copies cloned earlier need a one-time `git rm --cached -r . -q && git reset --hard`, and new shell-script checks must be registered as `bash scripts/<name>.sh`. docs/TESTING.md records the shell-dispatch convention alongside the command-tier table, and notes that the table's wallclock figures are Mac numbers: on Windows `check:privacy`, `check:test-names`, `check:test-isolation` and `typecheck` can exceed run-verify-parallel.sh's 120s per-check cap while passing on Linux and macOS. It also flags that the Cygwin bash shipped with Git for Windows tolerates CRLF where a strict bash does not, so a green local run is not evidence that a script is CRLF-clean. CHANGELOG.md's itemized list covers both doc updates. `bun run build:llms` regenerates byte-identical bundles: docs/TESTING.md is linked rather than inlined, so llms.txt / llms-full.txt do not move. `bun test test/build-llms.test.ts` passes 12/12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
56aac51a08 |
feat(extract): quarantine lane for auto-extracted entities from untrusted input (#160) (#3458)
* feat(extract): quarantine lane for auto-extracted entities from untrusted input (#160) extractAndEnrich regex-extracts entity names from arbitrary ingested text and creates people/ + companies/ stub pages. Those writes are now trust- gated end to end: - src/core/extraction-review.ts: new marker module (sibling of quarantine.ts / embed-skip.ts, frontmatter-key pattern, no migration). Untrusted-input stubs carry `provenance: auto-extracted` + `status: unverified`; the shared unverifiedExtractionFragment() is the single SQL source of truth for every consumer. - enrichment-service: enrichEntity/enrichEntities/extractAndEnrich take EnrichmentTrustOptions; only an explicit trusted:true writes authoritative pages (fail-closed, mirrors the OperationContext.remote invariant). Also threads sourceId through the write path. - retrieval: unverified stubs rank as ordinary content — skipped by the compiled-truth fusion boost (stampUnverifiedExtractions pre-fusion on all three hybrid paths + keyword-only opt-out) and by the people// companies/ namespace source-boost (guard inside buildSourceFactorCase, shared by both engines' search SQL). Results carry `unverified: true`. New engine method getUnverifiedExtractionPageIds in BOTH engines. - ops (contract-first): extract_entities (direct write only for ctx.remote === false + --trusted-extraction; everything else quarantines), extraction_pending (read, source-scoped list), extraction_review (owner-only batch promote/reject; promote flips status to verified keeping provenance for audit, reject soft-deletes). - doctor: unverified_extractions check warns on stubs older than N days (default 7) with the exact review commands. Tests: test/extraction-review.test.ts (PGLite: fail-closed matrix incl. remote-unset, fusion boost skip, review queue, doctor, hostile-transcript e2e proving fake entities land quarantined and rank below a verified page of equal lexical relevance) + test/e2e/extraction-review-postgres.test.ts (live Postgres parity, verified against pgvector:pg16). sql-ranking expectations updated to current state. Docs: KEY_FILES + RETRIEVAL + llms rebuild. Closes #160 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(extract): close vector-arm source-boost gap + harden extract_entities (#160 review round) Adversarial review of the quarantine lane found the people//companies/ 1.2x source factor still applied to unverified stubs inside searchVector's pre-LIMIT re-rank (a different multiplier from the fusion-level 2.0x the lane already cancels — and applied early enough to evict legitimate pages from the candidate pool, which nothing downstream can restore). - buildSourceFactorCase gains an optional unverifiedGuardColumn for the bare-slug re-rank form; both engines' hnsw_candidates CTEs now project the guard predicate as `unverified_stub` and the factor CASE checks it first. Wrong "fusion covers the vector arm" comment corrected. - extract_entities resource guards: 200k-char input cap (loud reject), 200-entity cap surfaced as `truncated` + `entities_found`; the library extractAndEnrich gets the same default cap. (OperationContext has no abort signal field — caps are the bound.) - extraction_review promote is now a targeted JSONB-merge UPDATE instead of putPage, so non-carried columns (page_kind, content_hash) can't be reset by the upsert. - extraction_pending applies buildVisibilityClause (archived-source stubs no longer list). - Wording: op description + module header now state the marker-strip assumption plainly (markers are ordinary frontmatter; the boundary against wholesale rewrite is put_page write authz) and document the CREATE-only scope of the lane. Tests: vector-arm factor-1.0 pinned on BOTH engines (PGLite unit + live Postgres e2e, identical basis embeddings → score ratio is the factor); resource-guard test (oversize reject + 300-entity flood capped at 200); guard-column form pinned in the buildSourceFactorCase unit test. search/ suite (340), sql-ranking, searchvector-maxpool, title-retrieval- arm, rrf-source-key, doctor, ops, cli suites all green; JSONB guards clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0bbaed2e48 |
v0.42.67.0 feat(migrate): provider-agnostic embedding migration — the path off ZeroEntropy (#3390, fixes #3391) (#3459)
* feat(migrate): provider-agnostic embedding migration service — the path off ZeroEntropy (#3390) - gbrain migrate embeddings --to <provider:model> (alias: retrieval-upgrade): plan + cost preflight, consent gate (--yes / TTY confirm / non-TTY exit 2), live probe against the target provider before any mutation, env-override gate, schema dimension transition via the shared runSchemaTransition, dual-plane config write, NULL-signature-inclusive invalidation, query-cache purge, resumable re-embed through the standard embed pipeline (single-flight locks, backoff, pacing, stderr progress). Killed runs resume by re-running the same command; the NULL-embedding column is the checkpoint. - #3391 root-cause fix (both engines): countStaleChunks / sumStaleChunkChars / invalidateStaleSignatureEmbeddings accept includeNullSignature to lift the v108 grandfather clause; embed --stale warns loudly when a model swap leaves NULL-signature pages in the old embedding space, and --include-null-signature re-embeds them. Default sweep behavior unchanged. - knobs_hash v=12 → v=13 (prov=default legacy callers must not be served pre-migration cache rows). - migrate_embeddings op: scope admin, localOnly, hidden cliHints, hard remote refusal, needs_confirmation without yes=true. - One-shot post-upgrade ZE-sunset banner (ze_sunset_notice_shown) for brains resolving to a zeroentropyai:* embedding model or reranker. - doctor's dimension-mismatch repair hint now names the real command. - Docs: docs/guides/embedding-migration.md, KEY_FILES entries, spend-controls gate row. Tests: PGLite unit + full-lifecycle flow (interrupted-run resume), real-Postgres e2e (pgvector DDL path + #3391 predicate parity). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): satisfy check:test-isolation + bump the remaining knobs_hash pins - test/migrate-embeddings-flow.test.ts → .serial.test.ts: the file holds a temp GBRAIN_HOME + an installed fake embed transport for its whole lifecycle (beforeAll → afterAll), which withEnv() can't wrap. This also fixes the CI shard-pollution failure in test/ai/recipes-existing-regression.test.ts (that file passes solo on both master and this branch; the flow test's configureGateway + provider-key deletion was leaking into it inside the same shard process). - test/embedding-migration.test.ts: env-override case now uses withEnv(). - Bump the three remaining KNOBS_HASH_VERSION pins to 13 (cross-modal-phase1, search-alias-resolved-boost, search/knobs-hash-reranker). - Docs + llms bundles follow the test rename. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(test): wire the new Postgres e2e into the smart e2e selector map Changes to embed.ts / embedding-migration.ts / retrieval-upgrade-planner.ts / postgres-engine.ts now trigger test/e2e/migrate-embeddings-postgres.test.ts — the #3391 stale predicates and runSchemaTransition's DDL path behave differently on real pgvector than on PGLite, so the smart selector has to know. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(migrate): consult spend.posture in the embedding-migration consent gate The brief asked the gate to honor spend.posture; it previously didn't read it at all. Now it does — but deliberately does NOT bypass on tokenmax: posture waives the spend CEILING, and this gate also guards a destructive schema rebuild (existing vectors dropped, retrieval degraded until the re-embed finishes). Under tokenmax the dollar figure is marked informational on stderr and the confirmation is still asked; --yes stays the single scripted bypass. Pinned by a new case in the flow test so a later refactor can't quietly turn posture into a bypass. Guide + spend-controls table updated to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * wip: blocker fixes --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4320527785 |
feat: support OpenRouter API key in config (#1714)
* feat: support OpenRouter API key in config * fixup: dedupe openrouter_api_key vs master, drop no-op compile-guard test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
10079efe40 |
feat(sync): --missing-path skip — classify absent-local_path sources in --all instead of failing (#3426)
sources.local_path is machine-specific state in a brain-wide table. Any brain whose sources were registered from more than one machine — or a sanctioned setup mid-migration (topologies.md Topology 2, or the system-of-record git flow before every repo is cloned) — has sources whose checkout is not present on the machine running sync --all. Each surfaced as a hard failure and forced rc=1 every run; on one observed fleet that was 12 phantom failures per hour, training operators to ignore the exit code. --missing-path skip classifies them honestly: ⊘ in the human aggregate, status skipped_missing_path + local_path in the --json envelope, new skipped_count, excluded from error_count and the rc=1 gate. Using the flag outside --all warns instead of silently no-oping. Default stays fail: on a single-machine brain a missing local_path usually means an unmounted volume or deleted checkout, and silently skipping would hide data loss. Skip is explicit opt-in. Pure helpers (parseMissingPathMode, partitionMissingPathSources) exported and unit-tested in the sync-all-parallel style — no DB, no fs. Docs: sync --help, docs/TESTING.md inventory, KEY_FILES.md sync entry. CHANGELOG/VERSION deliberately untouched per the release process. Co-authored-by: Ziggy <lazyclaw137@gmail.com> Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Lazydayz137 <Lazydayz137@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
16782aee7f |
fix(sources): recover corrupted config shapes (#3420)
* fix(sources): recover corrupted config shapes (#3401) Use one canonical normalizer for nested string and array-shaped source configs across federation reads, config writes, archive/restore, and doctor remediation.\n\nFixes #3401\nFixes #3402\nFixes #3403 Signed-off-by: arisgysel-design <arisgysel-design@users.noreply.github.com> * fix(sources): bind restoreSource federated patch via ::text::jsonb (#2339 class) restoreSource bound a JS JSON string to a bare $1::jsonb placeholder; postgres.js double-encodes that into a jsonb string scalar, so on the Postgres engine the coerced object || string-scalar concat evaluates as array-concat and restore RE-CORRUPTS the exact config shape this PR repairs. PGLite masks the bug (its driver parses the bind natively). Fix: bind through $1::text::jsonb per the repo JSONB rule. Adds the DATABASE_URL-gated Postgres regression (test/e2e/restore-source-config-jsonb-postgres.test.ts): seeds a corrupted string-scalar config, runs archive -> restore, asserts jsonb_typeof(config) = 'object' with the federated flag applied and pre-existing keys preserved. Verified red on the bare ::jsonb bind (config became a jsonb array) and green on the fix against a real pgvector Postgres; skips cleanly without DATABASE_URL. Co-authored-by: arisgysel-design <arisgysel-design@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Signed-off-by: arisgysel-design <arisgysel-design@users.noreply.github.com> Co-authored-by: arisgysel-design <arisgysel-design@users.noreply.github.com> Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
07901b1886 | fix(doctor): honor explicit subagent model config (#3408) | ||
|
|
7a65f182aa |
v0.42.66.1 fix: honor pgvector HNSW dimension limits (#3440)
* fix(doctor): honor pgvector HNSW dimension limits * fix(ci): stabilize local Docker verification * chore: bump version and changelog (v0.42.66.1) Co-Authored-By: OpenAI Codex <noreply@openai.com> --------- Co-authored-by: OpenAI Codex <noreply@openai.com> |
||
|
|
d9ac24744c |
fix(pricing): register claude-opus-5 in the Anthropic recipe allowlist and canonical pricing table (#3398)
Anthropic released Claude Opus 5, at the same $5/$25 pricing tier as
Opus 4.8. Neither the chat recipe allowlist nor CANONICAL_PRICING knew
about it, so operators could not opt into it via models.tier.deep /
models.default without gbrain rejecting the id.
- src/core/ai/recipes/anthropic.ts: add claude-opus-5 to the models list.
- src/core/model-pricing.ts: add anthropic:claude-opus-5 { input: 5.00,
output: 25.00 } (plus cache rates, matching Opus 4.8's ratios).
- src/core/takes-quality-eval/pricing.ts: add it to SUPPORTED_MODELS so
eval takes-quality run --budget-usd doesn't reject it during preflight.
- Refreshed the stale pricing-verification date and the Opus list in
docs/architecture/KEY_FILES.md.
- Tests: pinned-value regression in test/model-pricing.test.ts, recipe
membership in test/anthropic-model-ids.test.ts, budget-pricing coverage
in test/eval-takes-quality-pricing.test.ts.
Scope: registration only. TIER_DEFAULTS / DEFAULT_ALIASES /
DEFAULT_CHAT_MODEL are untouched — default-routing bumps are the
separate, already-open #2858; this just makes the id valid/priced for
operators who opt in explicitly.
|
||
|
|
32d42454e9 |
v0.42.66.0 fix(extract): make conversation backfill outcomes durable (takeover of #3293) (#3373)
* v0.42.66.0 fix(extract): make conversation backfill outcomes durable (takeover of #3293) Versioned, snapshot-bound terminal audit rows become the durable authority for conversation fact backfill completion; checkpoint GC can no longer repeat completed model work, and best-effort empty results no longer mask provider/output failures as complete. Supersedes #3293 (rebased onto current master; only version-trio conflicts). Co-authored-by: FloridaStyle <danwiggins@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop version-trio bump — individual fixes do not carry version bumps (release PRs do) * merge: reconcile durable-outcome skip accounting with master's LLM fallback tests The two fallback replay tests from #3371 asserted the legacy checkpoint pages_skipped counter; under this PR's durable-outcome authority a completed page is skipped via pages_skipped_completed before any parse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: FloridaStyle <danwiggins@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f64505b75f |
v0.42.66.0 feat(conversation-parser): wire the opt-in LLM fallback (#2247) (#3371)
* v0.42.66.0 feat(conversation-parser): wire the opt-in LLM fallback (#2247) (takeover of #3292) Rebase of PR #3292 onto current master (version trio re-resolved to 0.42.66.0; code applied cleanly). Wires the existing conversation-parser LLM fallback into conversation fact extraction behind the exact, default-off conversation_parser.llm_fallback_enabled=true privacy gate. Deterministic parsing stays first; dry runs never call a provider. Co-authored-by: FloridaStyle <daniel.wiggins@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop version-trio bump — individual fixes do not carry version bumps (release PRs do) --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: FloridaStyle <daniel.wiggins@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
38cc7198b7 |
feat(conversation-parser): parse normalized Slack markdown (takeover of #3289) (#3372)
Adds the bold-time-dash built-in pattern: **Speaker** HH:MM <dash> text (em dash, en dash, or ASCII hyphen), valid 24-hour times only, date from page frontmatter/date headings, multi-line continuation bodies. Opt-in score_continuations_as_body scoring keeps long multiline messages parseable while preserving the sparse-prose false-positive floor (needs two anchors or a first-line anchor before candidate-only scoring kicks in). Hardens validatePatternEntry to reject non-integer / out-of-range capture indexes including text_group. Adds maintainer doc, JSONL fixtures, and adversarial coverage. Takeover of #3289 (fork branch went CONFLICTING against master on the version trio); code applied 3-way, version/CHANGELOG bump dropped per fleet release convention. Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: FloridaStyle <daniel.wiggins@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ef1133df3e |
docs(security): Docker network isolation for co-located self-hosted Postgres (#3270) (#3331)
OAuth/source scoping only guards the serve --http path; a container sharing Docker's default bridge with the brain's Postgres can open a direct DB session without a token. Adds a 'Co-located Docker workloads' subsection to docs/mcp/DEPLOY.md with the operator checklist, a trust-boundary paragraph in SECURITY.md, and an ops note + cross-link in the company-brain tutorial. Fixes #3270 Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
26b938c37d |
fix(auth): expose OAuth source grants in whoami (takeover of #3279) (#3332)
Rebase of #3279 onto current master: the whoami oauth shape gains source_id (AuthInfo.sourceId, null when absent) and federated_read (AuthInfo.allowedSources, [] when absent) — read-only self-introspection that widens no grant. Re-applied against the post-#3091 description string (stdio transport shape preserved) and merged the grant tests into the current whoami.test.ts alongside the stdio cases. Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: boundless-forest <boundless-forest@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1392243d3b |
Revert "fix(jobs/autopilot): --install interval persistence, --lock-duration flag, dead-jobs doctor check, deployment-shape docs (#3129)"
This reverts commit
|
||
|
|
8345abce42 |
fix(jobs/autopilot): --install interval persistence, --lock-duration flag, dead-jobs doctor check, deployment-shape docs (#3129)
* fix(jobs/autopilot): interval persistence, --lock-duration flag, dead-jobs doctor check, deployment-shape docs Four backlog items in the jobs/autopilot workers, locks & installers area: - #2794: `gbrain autopilot --install` silently dropped `--interval`. The installer now parses + validates it, persists it to config (autopilot.interval), and threads it into the wrapper's exec line; a later flag-less --install regenerates the wrapper from the persisted value, and the daemon run path falls back to the same config key. - #1014: new `--lock-duration MS` flag (env: GBRAIN_LOCK_DURATION) on `gbrain jobs work` and `gbrain jobs supervisor` to tune the worker stall-lock window (and so the lockDuration x max_stalled wall-clock dead-letter cap). Validated like --health-interval (integer >= 1000ms); the supervisor propagates it to the spawned worker via buildWorkerArgs; shown in the worker startup banner. - Takeover of PR #1185 (@ethanbeard): `gbrain integrations doctor` now surfaces dead minion jobs as a cross-cutting [queue] check. Reworked from the original: consumes a new machine-readable `gbrain jobs list --json` surface instead of screen-scraping the human table (long job names shift the columns), and scopes to a 24h finished_at window so one ancient dead job can't flag ISSUES forever (parity with main doctor's queue checks). - #631: documented the production deployment shape for autopilot vs jobs supervisor in docs/guides/minions-deployment.md — recommend the `autopilot --no-worker` + `jobs supervisor` split, warn against running both worker lanes, and cross-link the --no-worker liveness probe. Co-authored-by: ethanbeard <ethanbeard@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(autopilot): make #2794 wrapper-script tests hermetic — fake gbrain on PATH writeWrapperScript calls resolveGbrainCliPath(), which shells out to `which gbrain` and throws on CI runners where no gbrain binary is installed. The two new --interval threading tests failed only in CI (dev machines have gbrain on PATH). Prepend a fake executable to PATH for the describe block so resolution is deterministic everywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: ethanbeard <ethanbeard@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
920aea5eb8 |
Notify about the conflict between gbrain serve (MCP) and CLI commands (#3243)
* Notify about gbrain serve and CLI conflict * Handle serve flags in PGLite lock notice --------- Co-authored-by: Francois de Fitte <4712833+fdefitte@users.noreply.github.com> |
||
|
|
00bcd66c3e |
fix(sync): failed git pull with zero imports reports partial (pull_failed) instead of up_to_date (#3068) (#3253)
* fix(sync): report partial (pull_failed) instead of up_to_date when git pull fails with zero imports (#3068) A warn-and-continue internal git pull failure (e.g. a local-path origin rejected by protocol.file.allow=never) combined with a zero-import run previously reported `up_to_date`, exited 0, and bumped the last_sync_at freshness heartbeat. A permanently-failing pull was therefore invisible forever: doctor's sync_freshness never fired and every scheduled sync looked clean while the source silently went stale. Now, when the pull failed and the run imported nothing, sync returns `partial` with the new reason `pull_failed`, leaves last_commit AND last_sync_at untouched (so staleness monitoring fires), and prints a dedicated non-success message. The fall-through-to-working-tree design is unchanged: local commits still import when the remote is unreachable, and the anchor still advances over commits that were actually imported. Addresses the report in #3068. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sync): surface pull_failed to CLI exit codes, sync --all JSON, and the cycle phase (#3068 review round) Codex review round 1 follow-ups: - Single-source `gbrain sync` sets exit code 1 on partial/pull_failed (timeout-class partials keep exit 0 — they converge on retry; a failing pull does not). - `sync --all` exits 1 when any source reports pull_failed, and the --json envelope carries the per-source partial `reason`. - The autopilot cycle's sync phase maps partial/pull_failed to `warn` with a dedicated summary and a `syncReason` detail, so a scheduled cycle no longer reports a clean run over a wedged source. - The regression test now isolates GBRAIN_HOME to a temp dir so the first full sync cannot touch the real sync-failure ledger. - Current-state docs: KEY_FILES.md sync.ts entry + TESTING.md inventory describe the pull_failed contract and the new test. Addresses the report in #3068. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sync): route pull_failed exit through the owned verdict channel; make the regression test serial (#3068 review round 2) Codex review round 2 follow-ups: - Single-source exit now uses setCliExitVerdict(1) instead of a raw process.exitCode assignment, which the CLI teardown deliberately ignores (PGLite's Emscripten runtime clobbers process.exitCode mid-run; the owned channel in src/core/cli-force-exit.ts is the only trusted verdict). Pinned by test/cli-exit-verdict-pin.test.ts. - The regression test is renamed to *.serial.test.ts because it pins GBRAIN_HOME for the whole file (scripts/check-test-isolation.sh R1); docs updated to the new name. Addresses the report in #3068. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
22fca8f891 |
fix(schema-pack): merge extends chain + borrow_from into the resolved manifest (#1749) (#3181)
Takeover of #2856 (fork-head PR). Applied cleanly onto origin/master; llms bundles regenerated (byte-identical — touched docs are not inlined). Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: coder8080 <67740875+coder8080@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
79f6d1bfee |
fix(gateway): fall back to the pooler when the derived direct host is unreachable (#1641) (#3088)
deriveDirectUrl() swaps the Supabase pooler host to db.<ref>.supabase.co:5432, which is IPv6-only without the paid IPv4 add-on. On IPv4-only networks the direct pool could never connect, and initDirectPool()'s throw killed 'gbrain init --url' and migrations with ENOTFOUND/ECONNREFUSED. getDirectPool() now classifies network-unreachable errors (ENOTFOUND, ECONNREFUSED, ENETUNREACH, EHOSTUNREACH, ETIMEDOUT, CONNECT_TIMEOUT) via the new isNetworkUnreachableError(), self-activates the kill-switch, logs one stderr line pointing at GBRAIN_DIRECT_DATABASE_URL / GBRAIN_DISABLE_DIRECT_POOL, and returns the read pool. Auth/SQL errors still throw (misconfig, not unreachability). The failed pool is ended via endPoolBounded so it can't leak sockets into the now-continuing process. Also surfaces the kill-switch + override envs in the init.ts IPv6 warnings and docs/guides/live-sync.md (they were previously undocumented outside connection-manager.ts). Fixes #1641 Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b0f74017d7 |
fix(calibration): resolve owner holder via config (default 'self') (#3077)
* fix(calibration): resolve owner holder via config (default 'self'), fixes #2464 Takeover of #2467 (rebased onto master). consolidate writes owner takes with holder='self' while calibration-profile, calibration CLI/op, think's calibration block, emotional-weight, and doctor's calibration_freshness all defaulted to a hardcoded 'garry' — so getScorecard returned 0 resolved and the calibration profile never built on non-upstream brains. New src/core/owner-holder.ts is the single source of truth: resolveOwnerHolder({override, configValue}) = override > emotional_weight.user_holder config > 'self'. All six call sites route through it; doctor's freshness SQL is parameterized (). Upgrade note: upstream-owner brains with historical holder='garry' profiles should `gbrain config set emotional_weight.user_holder garry` to keep reading them. Co-authored-by: devty <devty@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(calibration): replace real-name holder fixture with charlie-example placeholder Privacy iron rule: no real people's names in checked-in code. The sanctioned placeholder mapping uses people/charlie-example. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: devty <devty@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Garry Tan <garrytan@gmail.com> |
||
|
|
d21f34e96d |
fix(search): project email citation metadata (#2873)
Co-authored-by: Amit Agarwal <5302320+amtagrwl@users.noreply.github.com> |
||
|
|
d9eb027bdd |
fix(openclaw): declare gbrain plugin manifest entry (takeover of #2551) (#3185)
Add the OpenClaw-required top-level id to openclaw.plugin.json, export a direct register(api) entrypoint from src/openclaw-context-engine.ts, add a manifest regression test, and document that skillpack harvest must preserve OpenClaw-native manifest fields (id, configSchema, contracts). llms bundles regenerated (bun run build:llms) — no content drift. Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Filip <FilipHarald@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
62e009d192 |
fix(skillopt): emit proposed.md in no-mutate mode (#2635) (#3182)
Takeover of #2719 (fork head; rebased onto origin/master). - writeProposed now writes both best.md (current-best pointer) and proposed.md (stable human-review artifact); returns the proposal path. - Orchestrator reports the real proposed.md path for accepted --no-mutate runs. - Tutorial updated; llms bundles regenerated (no content drift — tutorial is not inlined in the bundle). Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Ziyang Guo <121015044+RerankerGuo@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d61808d806 |
v0.42.64.0 fix: harden confidential OAuth token revocation (takeover of #3017) (#3032)
* fix(oauth): validate confidential revoke secrets * fix(oauth): harden confidential token revocation * chore: bump version and changelog (v0.42.64.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> --------- Co-authored-by: Robin <rayme@boltdsolutions.com> Co-authored-by: OpenAI Codex <noreply@openai.com> Co-authored-by: Garry Tan <garrytan@gmail.com> |
||
|
|
11eebc3605 |
fix(conversation): extract iMessage facts with real timestamps (#2756) (#2958)
Co-authored-by: Sameer Bopardikar <203024074+sameerbopardikar@users.noreply.github.com> |
||
|
|
2934c53c1d |
fix(sources): validate --path is a git repo with committed content at registration (#2707) (#2975)
* fix(sources): validate --path is a git repo at registration time (#2707) `sources add --path <dir>` accepted any existing non-git directory with zero validation, deferring the failure to the first `gbrain sync` ("Not inside a git repository: ..."). By the time that surfaces, the source has been silently stale for however long nobody read the sync logs. Add a registration-time check (git-remote.ts:isInsideGitRepo, mirroring sync.ts's discoverGitRoot walk-up so subdir-of-git-repo sources still pass) that rejects an existing-but-non-git --path directory with an actionable error pointing at `git init && git add -A && git commit`. Non-existent paths are unaffected (out of scope — different, pre-existing failure mode) and `--force` opts out for callers who want to register before git-init exists. This is registration-time validation ONLY — it never auto-`git init`s the directory, preserving the consent boundary #2967 established for sync-time self-heal (a --path source is the user's own external directory; gbrain must not mutate it without explicit ask). Also documents the git requirement (docs/guides/multi-source-brains.md), including the "files must be committed, not just present" gotcha and that a stale/unreachable sync anchor already self-heals on plain `gbrain sync` (verified manually against HEAD — no reset-anchor command needed). * fix(sources): require a committed HEAD + shell-quote remediation cmd (codex round 1) Codex review round 1 on #2707 found two real gaps: 1. isInsideGitRepo alone accepts a `git init`ed-but-never-committed directory (rev-parse --show-toplevel succeeds with no HEAD), so registration would still pass a source that fails sync's own "No commits in repo ... Make at least one commit before syncing." Add hasGitCommits (git rev-parse HEAD) as a second required check. 2. The remediation command in the error message interpolated the raw path unquoted — spaces, $(), backticks, etc. would break or, worse, execute unintended shell syntax if pasted. POSIX single-quote it (mirrors src/commands/connect.ts:shellQuote; duplicated locally rather than imported, since commands/ depends on core/ not the reverse). * fix(sources): require tracked content in HEAD, not just a resolvable HEAD (codex round 2) Codex review round 2 P1: hasGitCommits (rev-parse HEAD) accepted a repo with an empty commit (git commit --allow-empty) followed by untracked files — HEAD resolves fine (to git's well-known empty-tree object), so registration passed, but the first sync would "succeed" importing nothing and then silently never notice the untracked files change. The exact same gap applied to an untracked subdirectory of an otherwise- real git repo (monorepo case). Replace hasGitCommits with hasTrackedContent (`git ls-tree HEAD -- .`, non-recursive — one entry is enough, no need to walk the whole subtree). `-C path` + pathspec `.` scopes correctly to both a repo toplevel and a subdirectory-of-a-repo source, and an empty tree lists zero entries where a bare `rev-parse HEAD` would still succeed. Also subsumes the "no commits at all" case hasGitCommits covered (ls-tree on an unborn repo fails the same way), so this is one check instead of two. Updated the error copy and docs/guides/multi-source-brains.md to match what's actually verified now. * fix(sources): O(1)-output tree-emptiness probe, avoid maxBuffer overflow (codex round 3) Codex review round 3 found the round-2 `git ls-tree HEAD -- .` listing buffers the whole (non-recursive) tree — a real repo with ~17-20K directly-tracked entries exceeds execFileSync's default 1 MiB maxBuffer, throws ENOBUFS, and the catch-all incorrectly rejects a perfectly valid registration. Replace the listing with `git rev-parse --verify HEAD:./` (resolves the tree object for `path` specifically, correct for both toplevel and subdirectory sources same as before) compared against git's canonical empty-tree SHA-1 (4b825dc6...) — a fixed ~40-byte read regardless of how many entries the tree has, structurally immune to this class of bug rather than just raising the threshold. Added a 300-file regression test locking this in. Declined a second round-3 finding (P1: reject a tree if ANY untracked file exists anywhere under the path, not just when the tree is entirely empty) — untracked files never being synced is standard, existing git-source behavior throughout this codebase (identical for --url managed clones), not a bug specific to this validation. Enforcing zero-untracked-files at registration would reject ordinary repos with gitignored build output, .DS_Store, editor swapfiles, etc. Out of scope relative to what #2707 actually asks for (a directory with real, committed content that will sync) and how every other git source in this system already behaves. * fix(sources): derive empty-tree OID per repo instead of hardcoding SHA-1 (codex round 4) Codex review round 4 P2, confirmed by directly testing against a `git init --object-format=sha256` repo: the hardcoded SHA-1 empty-tree constant only matches SHA-1 repositories. An empty SHA-256 repo's real empty-tree OID is a different (64-char) hash, so the SHA-1 comparison silently mismatched and let an empty/untracked SHA-256 source through — exactly the case this validation exists to catch. Replace the constant with `emptyTreeOid()`: `git hash-object -t tree --stdin < /dev/null` computed in the target repo's own context, so it returns the correct empty-tree OID for whichever object format that repo actually uses, without gbrain needing to know or care which one. Added gated regression tests (git 2.29+ / --object-format=sha256, test.skipIf on older git) for both the empty-repo-rejected and real-content-registers-fine cases. Converging here (4 review rounds; this is the last outstanding finding from round 4, and round 4 raised only this one issue). * fix(test): --force the incidental non-git second source in #1434 routing test (#2707) CI caught a real regression from this PR's registration-time git validation: test/sync-sole-non-default-routing.test.ts's "2+ non-default sources" case registers a bare mkdtempSync temp dir (no git init) as a second source purely to have 2 sources present — the directory's content was never meant to be exercised, only its existence as a distinct local_path. #2707's new validation correctly rejects that dir at registration time, since nothing else in the test suite told it otherwise. --force is the right fix, not adding unnecessary git-init/commit boilerplate to secondRepo: it documents that this specific registration intentionally doesn't care about git-validity, matching what a real caller opting into the legacy lenient behavior would do. Verified: the specific test (3/3 pass), plus every other test file in the repo using `sources add --path` (sources.test.ts, sources-ops.test.ts already covered by the PR's own commits; repos-alias.test.ts, sync-cost-gate.serial.test.ts — 11/11 pass, no similar fixture gap). typecheck clean, verify 31/31. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NCVEvUVm15bFuKqskWgfNS --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
8b325041ee |
v0.42.63.0 fix: preserve configured PGLite schema database path (#3016)
* fix(schema): preserve configured PGLite database path * chore: bump version and changelog (v0.42.63.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> --------- Co-authored-by: OpenAI Codex <noreply@openai.com> |
||
|
|
f72de97943 |
feat(sync): --src-subpath + --exclude for monorepo subdir-source support (#753, supersedes #774) (#2942)
* feat(sync): --src-subpath + --exclude for monorepo subdir-source support (#753) Rebased port of PR #774 onto current master. A single git repo can hold N logical sources at subdirectories: git operations run at the discovered repo root (git rev-parse --show-toplevel — worktrees and submodules resolve natively) while file walking, imports, deletes and renames are scoped to the subpath. Passing the subdirectory directly as the repo path (auto-discovery) works through the same code path. Path-containment guards (the point of the feature): - NAV-1/NAV-2: the realpath-resolved scope must live inside the realpath-resolved git root — ../-traversal and symlinked subdirs pointing outside the repo are rejected before any git op. - NAV-1 TOCTOU: per-file realpath re-validation during the incremental import drain and rename reimport; symlink-escape files are recorded as failures (fail-closed — the bookmark cannot advance past an escape). - NAV-4: an --exclude set that filters out every candidate warns loudly. Scoped syncs use git-root-relative slugs + source_path in BOTH the full and incremental paths (runImport gains slugRoot), fixing the original PR's full/incremental slug divergence in the auto-discovery flow. --exclude matches scope-relative paths in both paths; exclusion never deletes previously-imported pages. The full-sync delete reconcile is scope-restricted and relativizes against the slug base so a healthy scoped source can't trip the #2828 mass-delete valve. .gitignore management resolves to the git root at every call site. Preserves all master-side sync work since the original branch: the #2828 mass-delete safety valve, #1794 resumable checkpoints + pinned targets, #1950 stall watchdog, #2335 heartbeat bump, #1970 bookmark reachability, and the git-ls-files walker (#2315/#2462/#2678, whose symlink/cycle hardening is untouched). Co-authored-by: Jeremy Knows <jeremy@veefriends.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: listEverCommittedPaths uses gitContextRoot after #753 root-triple refactor Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Jeremy Knows <jeremy@veefriends.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f8d11f67a3 |
feat(search): configurable FTS language + reindex command (lands #580/#581/#582) (#2941)
Squashed superset takeover of the FTS-language trilogy by @rafaelreis-r (#580 env-var language for query+write side, #581 migration backfill, #582 gbrain reindex-search-vector), rebased onto current master with the security review's required fixes applied: - Migration renumbered v116 -> v123 (master's v116 was already claimed by code_edges_source_backfill; master is at v122). - Restored the v120/#1647 search_path hardening: all four CREATE OR REPLACE trigger-function bodies (migration handler + reindex command) now carry SET search_path = pg_catalog, public, since CREATE OR REPLACE resets proconfig and would otherwise strip the hardening on upgrade. - reindex-search-vector: shared progress reporter (stderr phases reindex_search_vector.pages/.chunks), id-keyset batched backfill (5000 rows/UPDATE) instead of single whole-table statements, and --json no longer bypasses the --yes/TTY confirmation gate. - Allowlist validation regex + injection tests kept exactly as authored. - Stale v33/v116 comments swept; docs/guides/multi-language-fts.md written (README referenced it but no PR added it); llms bundles regenerated via bun run build:llms. - Trilogy tests quarantined as *.serial.test.ts (env mutation, per check-test-isolation). Verified live on PGLite: fresh init with GBRAIN_FTS_LANGUAGE=portuguese produces portuguese-stemmed vectors with search_path pinned; the reindex command retokenizes an english brain to portuguese in place. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Rafael Reis <rafael.reis@contabilizei.com.br> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9fe4628d02 |
feat(engine): opt-in Postgres RLS source-scope binding (lands #2387) (#2940)
Takeover of community PR #2387 with the security review's required fixes applied. Original work by @harrisali0101. With GBRAIN_RLS_SCOPE_BINDING=1, source-scoped Postgres read methods wrap their queries in a transaction that binds set_config('app.scopes', $1, true) (federated sourceIds CSV > scalar sourceId > '*') so operator-managed RLS policies can filter rows at the SQL layer — defense-in-depth layer 2 under the mandatory app-layer source filters. Review fixes on top of the original PR: - Flag-off is now a TRUE pass-through: no new per-read transaction wrap (the #1794 PgBouncer pool-exhaustion class). Only the three search methods keep a transaction when off — exactly the sql.begin() + SET LOCAL statement_timeout wrap they already had on master — via the helper's alwaysTransaction option. - Preserved the PR's latent setseed fix: listCorpusSample pins setseed() + SELECT to one connection when seeded (alwaysTransaction gated on opts.seed), so the deterministic path can't split across pooled connections. - Updated the two postgres-engine shape tests to pin the new invariant (search methods route through withScopedReadTransaction with alwaysTransaction; helper owns the sql.begin(); flag-off path is callback(this.sql)). - New behavioral tests (test/postgres-engine-rls-scope.test.ts): flag-off pass-through, flag-off alwaysTransaction, flag-on set_config emission, federated > scalar > '*' precedence, and the CSV as a bound parameter (never interpolated). - Fixed the helper header comment to match the actual branching behavior. - Operator docs in docs/ENGINES.md: env var, policy SQL, the ALTER ROLE ... SET app.scopes='*' default requirement, FORCE ROW LEVEL SECURITY for owner roles, and the honest caveat about unwrapped paths. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Harris <79081645+harrisali0101@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1833d95896 |
fix(dream,chronicle): synthesize/concepts output family — source scope, output root, retrieval reach, durable provenance, honest judge failures (#1586 #2415 #2163 #2569 #2606) (#2939)
Five verified-open fixes to the dream/synthesize output family: - #1586: thread the cycle's resolved sourceId (cycleSourceId) through runPhaseSynthesize -> SubagentHandlerData.source_id -> subagent tool OperationContext, and stamp collected refs + summary page with the same source, so synthesized pages stop landing in 'default'. - #2415: new config knob dream.synthesize.output_root (default 'wiki', zero behavior change unless set) drives the synthesize prompt slug templates, the patterns reflection lookup + prompt, and remaps the filing-rule allow-list globs. Registered in KNOWN_CONFIG_KEYS. - #2163: synthesize_concepts writes concept pages through importFromContent (put_page's parse->chunk->embed pipeline) instead of bare engine.putPage, so concepts/ pages are chunked + embedded and reachable by retrieval. - #2569: stampDreamProvenance persists dream_generated + dream_cycle_date into pages.frontmatter (JSONB merge via executeRawJsonb) at write time, so generated pages are DB-queryable and put_page write-through can't erase the marker. - #2606: chronicle judge detects stopReason 'length' truncation and no-JSON-array parse failures as distinct skipped reasons (judge_truncated / judge_parse_failed) instead of a false terminal no_events; output cap raised to 4000 and configurable via chronicle.judge_max_tokens. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Garry Tan <garrytan@gmail.com> |
||
|
|
42375bded5 |
fix(sync): stop the sync data-loss family — ops/ prune, DB-only write-through, full-sync gate drift (#2404, #2426, #2607) (#2938)
Three verified-open defects, one family: sync silently destroying or diverging on content it should preserve. #2404 (P0) — 'ops' was hardcoded in PRUNE_DIR_NAMES (a v0.2.0-era carve-out), so any path with an ops segment was 'pruned-dir': committed ops/*.md never imported, and modified ops/* files hit the unsyncableModified delete loop (whose #1433 guard only spared 'metafile'), silently deleting put-created pages like the bundled daily-task-manager's canonical ops/tasks on every sync. Fix: remove 'ops' from the prune list (ordinary user content; the vendor/generated entries stay), and harden the delete loop to also skip 'pruned-dir' — a page under a pruned dir can only exist via a deliberate put_page. #2426 (P0) — write-through content stayed DB-only and was deleted by sync --full. All three compounding bugs fixed: 1. writePageThrough now best-effort commits the artifact (path-limited git commit) on durability-hardened repos, so the post-commit hook can push it; result carries committed?: boolean. 2. scripts/brain-commit-push.sh stages+commits BEFORE any pull — the old fetch+pull-rebase-first order aborted on any dirty tree, so the helper could never commit a MODIFIED page; brain_push's rebase-on-reject already handles an advanced remote. 3. The full-sync delete-reconcile partitions stale pages by git history (listEverCommittedPaths): never-committed source_paths are DB-only write-through — pages are KEPT and re-exported to the working tree instead of soft-deleted. Builds on the #2828 mass-delete valve (covers the below-valve cases). #2607 — the sync --full git ls-files fast path bypassed pruneDir, so a full pass imported (and resurrected soft-deleted) pages under dot-dirs and vendored trees that incremental sync excludes. Fix: isCollectibleForWalker applies the same segment-level pruneDir gate as classifySync, so full and incremental enumeration agree. One regression test per defect (all verified failing against master src): test/sync-ops-pages.serial.test.ts, test/write-through-commit.serial.test.ts, the #2426 helper-order test in test/brain-durability-hook.serial.test.ts, test/sync-reconcile-db-only.serial.test.ts, test/import-git-fastpath-prune.test.ts. Fixes #2404 Fixes #2426 Fixes #2607 Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e1cefd0654 |
fix(subagent): orchestration fix-wave G — configurable timeouts/caps, honest child outcomes, fenced timeline writes (#2937)
Four verified-open issues in the subagent-orchestration family, one PR: - #1594: dream synthesize subagent job/wait timeouts promoted from hardcoded 30/35-min constants to config keys dream.synthesize.subagent_timeout_ms / subagent_wait_timeout_ms. Approach ported from stale PR #1596 (credit @ai920wisco). - #2778: add_timeline_entry joins the subagent brain-tool allowlist, fenced fail-closed by the shared enforceSubagentSlugFence (extracted from put_page's inline check — same trusted-workspace allow-list / wiki/agents/<id>/ namespace policy). The per-turn output cap is now resolveMaxOutputTokens (data.max_tokens → agent.max_output_tokens → 8192, was hardcoded 4096); a max_tokens stop surfaces as stop_reason 'max_tokens' instead of a silent end_turn, and a mid-tool-round cap hit injects a truncation note so the model re-issues the dropped call. - #2782: patterns phase status now reflects the child outcome — non-complete outcome with zero writes → fail (PATTERNS_CHILD_<OUTCOME>), partial writes → warn. Patterns timeouts get the same config-key pair (dream.patterns.subagent_timeout_ms / subagent_wait_timeout_ms). - #2113: facts extraction cap is config facts.extraction_max_tokens (default 4000, was hardcoded 1500); stopReason 'length' is checked, retried once at 2x the cap, and surfaced on stderr instead of silently extracting zero facts. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: ai920wisco <ai920wisco@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
da1bab532a |
fix(import): make checkpoints staging-first — canonical dir identity + self-describing metadata (#2935)
Port of #1731 (diazMelgarejo) onto current master. gbrain import wrote ~/.gbrain/import-checkpoint.json with the caller's raw dir argument, so a checkpoint left behind by an interrupted run (e.g. SIGTERM) could carry "." or a symlinked spelling — an identity that resolves to whatever CWD the next consumer happens to run from. Downstream tooling that treated the checkpoint dir as an owned staging boundary could then act on the wrong directory. - runImport captures the import target ONCE via resolveImportTargetDir (resolve + realpathSync) and threads that canonical value through collection, checkpoint load/save, and resume filtering - checkpoints are self-describing (schema_version: 1, owner: "gbrain", kind: "import"); loadCheckpoint tolerates absent metadata (legacy path-based files) but rejects present-and-wrong metadata and any relative dir - checkpoint contract documented in docs/guides/live-sync.md (llms bundle regenerated) - test/import-resume.test.ts fixture now realpaths its tmpdir so planted checkpoints match the canonicalized dir (macOS /var -> /private/var) Fixes #1728 Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Lawrence Melgarejo <Lawrence@cyre.me> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fe6838ffac |
docs: add macOS 26.x Tahoe PGLite WASM workaround + native Postgres setup guide (#1671)
PGLite's embedded WASM engine crashes on macOS 26.x (Tahoe) on Apple Silicon during engine initialization. This adds: - A Troubleshooting section in docs/INSTALL.md with step-by-step instructions for using native Homebrew PostgreSQL 17 + pgvector as a workaround - A callout in README.md's Troubleshooting section pointing users to the detailed setup guide Tested on macOS 26.5 (arm64), Bun 1.3.14, gbrain 0.41.29.0, PostgreSQL 17.10 (Homebrew), pgvector 0.8.0. All 102 schema migrations pass. gbrain doctor green. Co-authored-by: Saurav Roy <roysaurav@users.noreply.github.com> |
||
|
|
ff2eb6ff3a |
docs: post-release reference-doc sync for v0.42.59.0 (#2798)
Cross-referenced the v0.42.59.0 five-fix rollup (#2735-#2739) against the reference docs and updated every entry that no longer described current behavior: - KEY_FILES.md: migrate-engine.ts (source-catalog copy + target-aware resume manifest), pglite/postgres bootstrap probe set (timeline_entries.event_page_id), searchTakes/searchTakesVector source scope, think op scope threading through runGather via thinkSourceScopeOpts, new fence-shared.ts entry (escape-aware parseRowCells as escapeFenceCell's inverse). - TESTING.md: one-liners for the three new e2e suites (think-source-isolation-pglite, facts-fence-reconcile-postgres, migrate-engine-sources-postgres) + the new multi-source-bug-class case. - TODOS.md: new v0.42.59.0 follow-ups section (6 items); refreshed the two existing items the wave partially resolved (think gather scope plumbing, #2200 takes_search engine-layer scope). Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a7b0ae80a9 |
v0.42.60.0 chore(release): eleven verified community fixes — changelog + version bump (#2888)
* v0.42.60.0 chore(release): eleven verified community fixes — changelog + version bump Windows full-sync mass-delete fix, gateway tool-loop resume consolidation (fix-wave A), two source-isolation closes, search-cache exclude-policy keying, and six more verified community fixes. Files the take-writes fail-open source fallback and the #2112 doctor hunk as follow-up TODOs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: update reference docs for v0.42.60.0 - KEY_FILES.md: unpin stale KNOBS_HASH_VERSION number in the autocut entry (mode.ts is the single source of truth); document the full-sync reconcile path-separator normalization + mass-delete safety valve (planReconcileDeletes, GBRAIN_ALLOW_MASS_RECONCILE); describe the TTY-gated admin bootstrap token banner (--print-admin-token, env-sourced always hidden) - docs/mcp/DEPLOY.md + docs/tutorials/company-brain.md: bootstrap token is now hidden on non-TTY starts; document GBRAIN_ADMIN_BOOTSTRAP_TOKEN and --print-admin-token for headless deploys Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(docs): regenerate llms bundle after KEY_FILES/deploy-doc sync Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
68ed7bafa4 |
fix(facts): quarantine ambiguous entity matches (#2723) (#2737)
Bare names resolve only when prefix expansion finds exactly one canonical candidate; ambiguous collisions and low-specificity multi-token fuzzy matches fall through to the guarded holding path instead of confident wrong attribution. Fixes #2723. |
||
|
|
a25209bbb2 |
v0.42.58.0 fix(ai): provider-agnostic gateway — env clobber, base-URL /v1, embedding dims (#1249 #1250 #1292 #2271 #2209) (#2627)
* fix(ai): drop empty-string env values before merge so they can't clobber config keys (#1249) Claude Code injects ANTHROPIC_API_KEY='' to neuter subprocess LLM calls; an unconditional process.env spread let that empty string override a valid config.json key, breaking every gateway op with NO_ANTHROPIC_API_KEY. Filter '' / undefined before the merge; '0' and 'false' are preserved. * fix(ai): normalize native provider base URLs + replace embedding guard with a dims-presence check (#1250, #1292) #1250: createAnthropic/createOpenAI were called with no baseURL, so an env-injected bare host (e.g. ANTHROPIC_BASE_URL without /v1) 404'd. Add a shared resolveNativeBaseUrl and pass a normalized baseURL at all anthropic + openai native sites (google deferred until its suffix is verified). #1292/D6: the user_provided_model_unset guard was structurally unreachable as a no-model check (parseModelId throws on a bare provider) and only ever false-positived for litellm:<model>, silently disabling vector search. Replace it with a real dims-presence check for user-provided/zero-default recipes and delete the dead branch in both consumers. Also stop configureGateway from fabricating a default embedding_dimensions, so 'no dims set' stays honest. * fix(ai): trust user-declared embedding dims for local recipes + litellm /v1 hint (#2271, #2209) #2271: a new trust_custom_dims flag adds a passthrough tier so ollama / llama-server / litellm accept a user-supplied --embedding-dimensions instead of being hard-rejected. Fail-closed for fixed-dim providers (openai/voyage/ zeroentropy) and excludes openrouter (declares dims_options). Register modern ollama embed model names. #2209: litellm setup_hint now states the /v1 path convention and the docs pointer is corrected to docs/integrations/embedding-providers.md. * docs+test(ai): KEY_FILES current-state for provider-agnostic gateway + embed-preflight dims-unset test (#1249, #1250, #1292) * fix(ai): point user_provided_dims_unset remediation at 'gbrain init' (config set rejects it) + coverage Pre-landing adversarial review (P1): the new dims-unset guard told users to run 'gbrain config set embedding_dimensions <N>', which config.ts hard-rejects (it's a schema-sizing field). Both consumer messages now point at 'gbrain init --embedding-dimensions'. Adds: pgvector-cap-still-fires regression for the trust_custom_dims passthrough, and a configureGateway backfill-invariant test. * chore: bump version and changelog (v0.42.57.0) Provider-agnostic plumbing wave: #1249 empty-env clobber, #1250 native baseURL normalization, #1292 embedding dims-presence guard, #2271 trust_custom_dims passthrough, #2209 litellm /v1 hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: sync embedding-providers guide for provider-agnostic gateway wave (v0.42.57.0) Post-ship doc drift fix for the v0.42.57.0 AI-gateway wave: - LiteLLM section now names the /v1 base-URL convention (#2209). - Ollama section lists the newly-registered modern embedders qwen3-embed-8b + snowflake-arctic-embed-l-v2, and notes dims-trust for local recipes (#2271). - llama-server section notes gbrain trusts the user-declared dimension (#2271). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: post-ship doc sweep for v0.42.57.0 provider-agnostic gateway wave - KEY_FILES.md types.ts entry: document EmbeddingTouchpoint.trust_custom_dims (#2271 passthrough tier, runs after dims_options + Matryoshka allowlists) - ENGINES.md: embedding design-choice note now names the provider-agnostic gateway delegation instead of the stale OpenAI-only parenthetical - embedding-providers.md: drop an exact-duplicate doctor-8c paragraph - llms-full.txt regenerated (ENGINES.md is inlined in the bundle) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: apply codex doc-review findings for v0.42.57.0 (base-URL env note, litellm multimodal) - embedding-providers.md OpenAI section: document OPENAI_BASE_URL / ANTHROPIC_BASE_URL bare-host /v1 normalization (#1250 user-facing surface) - TL;DR table: litellm multimodal is backend-permitting (recipe declares supports_multimodal: true, routed via the openai-compat multimodal path), not "no" Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: pin engine-find-trajectory schema to 1536 + stop gateway-state leaks across shard files CI shard 5 failed 7 findTrajectory tests with 'expected 1280 dimensions, not 1536': engine-find-trajectory hardcodes 1536-d vectors but sizes its schema from AMBIENT gateway state in beforeAll — which runs before the legacy-embedding-preload's per-test 1536 restore. A preceding file that ends with a dimensionless configureGateway (facts-extract-silent-no-op) or a bare resetGateway poisons the next fresh initSchema down to 1280-d columns. The new test files in this PR reshuffled shard bin-packing and exposed the trap. - engine-find-trajectory: pin OpenAI/1536 explicitly before initSchema (the pattern bunfig's preload documents) — deterministic regardless of neighbors - facts-extract-silent-no-op, diagnose-embedding-dims, embed-preflight: restore the legacy 1536 pin in afterAll instead of ending reset/dimensionless Reproduced: synthetic dimensionless-gateway file + old victim = the exact 7 CI failures; with the pin = 0. Verified in-process pair runs both orders. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
058f448b9a |
v0.42.57.0 fix(pglite): incident — never steal a live data-dir lock + corrupted-store recovery hint (#2348) (#2400)
* fix(pglite): never steal the data-dir lock from a live holder (#2348) A busy `gbrain dream`/`embed` holder whose 30s heartbeat lapsed (the JS event loop is blocked during long synchronous WASM imports/CHECKPOINTs) used to get its lock reaped past the steal-grace window. PGLite/WASM is strictly single-writer, so a second OS process then opened the same data dir and corrupted the catalog + pgvector extension state (58P01 / internal_load_library / "type vector does not exist"), recoverable only by wipe+restore. Reap ONLY a dead PID; a live holder is never stolen — a wedged-but-alive or PID-reused holder makes the acquire time out with a message naming the PID. Removes the GBRAIN_PGLITE_LOCK_STEAL_GRACE_SECONDS knob (no longer meaningful). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(pglite): point a corrupted store at reinit-pglite recovery (#2348) classifyPgliteInitError gains a `corrupt` verdict for the 58P01 / internal_load_library / "vector does not exist" / "content_chunks does not exist" signature (beating the generic wasm-runtime match), so an already-damaged store gets actionable recovery (gbrain reinit-pglite / restore a backup) instead of the wrong macOS-WASM hint. Updates KEY_FILES.md to current lock behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v0.42.55.0 fix(pglite): incident — never steal a live lock + corrupted-store recovery hint (#2348) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v0.42.56.0 chore: re-bump past #2399 version collision + refresh ownerToken comment #2399 (security wave) claimed 0.42.55.0; take the next slot. Also updates the LockHandle.ownerToken JSDoc to current #2348 behavior (live holders are never reaped, so reap-then-reacquire is dead-holder-only; token guard stays as defense-in-depth). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
646179047a |
v0.42.56.0 feat(chronicle): Life Chronicle — temporal timeline + thought diary + bi-temporal per-entity ontology (#2390) (#2533)
* feat(chronicle): register event + diary page types (#2390) Life Chronicle Phase A.1. Adds `event` (timeline atom) and `diary` (first-person interiority) as temporal-primitive page types under life/events/ and life/diary/, extractable:false — registered in ALL_PAGE_TYPES, both base schema packs, and the inferType prefix table, with parity fixtures. Also lands the chronicle read result types (ChronicleTimelineRow/ChronicleTimelineOpts/LastSeenResult) consumed by Phase A.2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): event_page_id timeline projection + day/since/last-seen reads (#2390) Life Chronicle Phase A.2. Adds a nullable event_page_id FK to timeline_entries (migration v120; mirrored in schema.sql, pglite-schema, schema-embedded) so a type:event page projects ONE date-index row keyed to its depth page; a partial UNIQUE(event_page_id, date) makes re-extraction with a changed summary an update, not a duplicate. Dual-engine getTimelineForDate / getSince / getLastSeen filter the depth page on deleted_at, hide soft-deleted event projections at READ time (not just doctor), order by event effective_date for intra-day sequence, and honor source scope (sourceIds[] > sourceId). Ops surface as `gbrain day <date> [--week]`, `gbrain since <date> [--kind]`, `gbrain last-seen <entity>`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): auto-emit extractor — backstop + chronicle_extract job (#2390) Life Chronicle Phase A.3. A put_page backstop (gated on status==='imported', the auto-link/timeline trust gate, and the default-OFF auto_chronicle flag; diary + event pages never eligible) enqueues a chronicle_extract minion job. The job runs the extractor off the write path: deterministic when/who, an injectable LLM judge (default = chat gateway; no-op when no gateway), an all-or-nothing parse barrier (a malformed proposal writes NOTHING), then content-addressed event pages + a timeline projection via the new dual-engine upsertEventProjection (idempotent — re-run yields one event + one projection). New: src/core/chronicle/{eligibility,config,extract-events,backstop}.ts, engine.upsertEventProjection (both engines), the jobs.ts handler + a 10-min timeout. 14 unit tests (eligibility, idempotency, parse barrier, backstop gating + enqueue) green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): quick-capture for diary + manual events (#2390) Life Chronicle Phase A.5. `gbrain capture` now routes the default slug by type (diary → life/diary/, event → life/events/, else inbox/) and accepts --who/--what/--where/--kind/--depth to assemble the `event:` frontmatter block for `--type event`. User-declared event keys win per-key over the flags. Goes through the existing put_page → write-through → embed path. 6 new unit tests; existing capture tests stay green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): bi-temporal per-entity ontology on the facts table (#2390) Life Chronicle Phase B.10 — the feature's differentiator. Rather than a parallel store, the open-world per-entity ontology EXTENDS the existing `facts` table (eng-review G1): migration v121 adds dimension/value/value_hash /dim_status columns + a deterministic partial-UNIQUE dedup key + an asof read index. facts already supplies bi-temporal validity, supersession (superseded_by), visibility (remote redaction), confidence, provenance, and embedding — all inherited. Dual-engine methods: mergeOntologyFact (deterministic value_hash dedup → idempotent retry; same value corroborates; a different value forward-closes the prior row's valid_until + superseded_by; a BACKDATED conflicting value is recorded WITHOUT rewriting, surfaced by findOntologyConflicts), getOntology with `--asof` valid-time travel (expired_at + status + validity-window in the predicate so quarantined/superseded never leak), discoverOntologyDimensions, findOntologyConflicts. Novel/LLM-proposed dimensions quarantine; a seed lexicon canonicalizes name drift (job_role → role). 9 unit tests cover the full lifecycle; typecheck pins both engines to the interface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): ontology ops — get/add/dimensions/contradictions (#2390) Life Chronicle Phase B.11. Exposes the bi-temporal ontology over CLI + MCP (contract-first, auto-generated): `gbrain ontology <entity> [--asof]`, `gbrain ontology-add <entity> <dim> <value>`, `gbrain ontology-dimensions` (meta-ontology rollup), `gbrain ontology-contradictions`. All reads route through sourceScopeOpts. Privacy: ontology_get redacts diary-sourced observations (source under life/diary/) for untrusted (remote) callers. 3 op tests (incl. the remote-redaction path); 47 op-registry/tool-def/description tests stay green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): agent-context loader — volunteer_chronicle (#2390) Life Chronicle Phase B.12. `loadChronicleContext` hands an agent the recent timeline (last N days) + the validity-resolved current ontology for the entities in play, in one zero-LLM payload, so it orients before acting — the exact gap behind fumbled chronology. Pure composition over getSince + getOntology (no new SQL). Exposed as the `volunteer_chronicle` read op (`gbrain orient [--days] [--entities a,b]`); diary-sourced ontology is redacted for remote callers. 2 loader tests + op-registry green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): backfill op — sweep existing meetings into events (#2390) Life Chronicle Phase A.8. `chronicle_backfill` (local-only admin op; `gbrain chronicle-backfill [--since] [--limit] [--dry-run]`) lists existing meeting/conversation/calendar pages (source-scoped via listPages), filters through the chronicle eligibility predicate, and enqueues one chronicle_extract job per eligible page so existing brains populate the timeline. --dry-run counts only; per-page enqueue failures are surfaced in `errors`, never swallowed. 2 op tests (dry-run count + enqueue). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): delight — on-this-day + narrative rendering (#2390) Life Chronicle Phase A.6 (delight). Dual-engine getOnThisDay (events from the same month-day in prior years; `gbrain on-this-day [--date]`) reusing the chronicle JOIN shape (deleted-event hiding + source scope). A pure renderTimelineNarrative turns timeline rows into prose; `gbrain day --narrative` returns it alongside the events. 5 tests. (Coverage gap-detection ships with the advisor collectors next.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): proactive advisor collector (#2390) Life Chronicle Phase A.7. A brain-state advisor collector surfaces two proactive signals in `gbrain advisor`: unresolved ontology conflicts (warn, → `gbrain ontology-contradictions`) and recent meetings with no timeline coverage (info, → `gbrain chronicle-backfill`). Advisory-only (no dispatch_id); runs over MCP too (not workspace-dependent); tolerant of pre-migration brains. 3 tests + advisor-op-gate green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): doctor chronicle_projection_health probe (#2390) Life Chronicle Phase B.13. An always-run doctor check (keyed off the event_page_id schema, NOT a migration verify-hook) counts timeline projections whose event page is soft-deleted — hidden at read time, surfaced here as a cleanup backlog (`gbrain integrity auto`). Tolerant of pre-migration brains. 1 detection test. (auto_chronicle / chronicle.tz flags already work via getConfig defaults; their docs land with document-release at ship.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): E1 temporal recall — chronicle-type boost on temporal queries (#2390) Life Chronicle Phase A.4 (E1, ambient temporality). Rather than a separate RRF arm (which needs chunk hydration + risks the fusion path), E1 is a bounded post-fusion boost: applyChronicleTypeBoost lifts `event`/`diary` results on temporal queries, wired INSIDE runPostFusionStages' `recency !== 'off'` branch so it fires ONLY on temporal intent — non-temporal search is bit-for-bit unchanged (proven by 110 passing search-path tests). Bounded ([1.0,1.25]) + floor-gated like the other metadata stages; attribution via `chronicle_boost`. 3 unit tests. (Empirical precision/negative measurement lands with the eval.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chronicle): feature eval — gbrain eval chronicle (PRIMARY proof) (#2390) Life Chronicle Phase A.9, the North-Star proof. A deterministic, CI-safe eval (brings its own in-memory PGLite; no LLM, no gateway) builds a synthetic month corpus with a known gold chronology + a planted ontology supersession + a planted conflict, then scores the chronicle layer on six gold tasks: day reconstruction (intra-day order), last-seen exact date, ontology supersession, --asof valid-time travel, contradiction surfacing, and source isolation. `gbrain eval chronicle [--json]` exits 0 iff all pass — currently 6/6. The OFF baseline (raw meeting pages) structurally can't order intra-day events or time-travel ontology; the ON path does. (The live-LLM OFF-vs-ON agent arm + LongMemEval temporal slice are a follow-up; this deterministic bar gates CI.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(chronicle): pre-landing review — conflict validity, parse-barrier date, remote conflict redaction (#2390) Three bugs caught by the codex pre-landing review on the diff: 1. findOntologyConflicts ignored valid_until, so a normal forward supersession (founder→advisor from two sources) falsely reported as a live conflict. Now restricted to currently-open rows (valid_until IS NULL) in both engines. 2. The extractor parse barrier accepted any when-string >= 4 chars; a non-date value slipped past, wrote the event page, then threw on the projection's ::date cast (partial write). isValidProposal now requires a real parseable date. 3. The ontology_conflicts op had no remote diary redaction (ontology_get did); remote callers now get diary-sourced values filtered, and conflicts that lose their disagreement after redaction are dropped. Three regression tests added; 29 chronicle tests + eval 6/6 green; typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v0.42.56.0 feat(chronicle): Life Chronicle — temporal timeline + diary + bi-temporal ontology (#2390) Bump VERSION + package.json to 0.42.56.0 and add the CHANGELOG entry for the Life Chronicle feature (#2390, closes duplicate #2388). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(chronicle): register #2390 surfaces with the five CI guard suites (#2390) CI caught five guard tests that pin registration invariants my diff tripped: - schema-bootstrap-coverage: the four v122 facts ontology columns join COLUMN_EXEMPTIONS (facts is migration-created; the partial indexes live inside v122; every reader filters dimension IS NOT NULL — same precedent as facts.claim_metric et al). - no-valid-until-write (R8): both engines' mergeOntologyFact forward- supersession is a deliberate, documented valid_until write authority (engine-layer, dimension IS NOT NULL only; the contradiction probe still never mutates). - doctor-categories: chronicle_projection_health registered under BRAIN_CHECK_NAMES (same class as child_table_orphans). - checkTypeProliferation: the test is now threshold-relative (computes declared from the active pack, seeds declared+6) so base-pack growth can't silently move the fixed threshold again. - schema-cli: gbrain-base page-type count 25 → 27 (event + diary), with assertions on both new types. All 41 guard tests green; typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: file Life Chronicle follow-up TODOs (v0.42.56.0, #2390) Eight deferred items from the CEO/eng review decisions (auto-emit default-flip fast-follow, live eval arm + LongMemEval slice, passive diary consent, interval-splitting, federation, place-as-entity, meta-ontology dashboard, materialized daily pages), each with decision provenance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(architecture): KEY_FILES entry for the Life Chronicle module (#2390) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |