mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
cd33efdd68d6150879313cb1ff75bd0fb1a2cc4f
373
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cd33efdd68 |
Merge PR #2630: feat(autopilot): wire the nightly conversation-parser probe into the loop
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
062915fe7d |
Merge PR #2629: fix(autopilot,eval): make the nightly quality probe enable path work end-to-end
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> # Conflicts: # test/engine-find-trajectory.test.ts |
||
|
|
62592c939e |
Merge PR #2600: fix(ai): add chat touchpoint to litellm proxy recipe
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> # Conflicts: # src/core/ai/recipes/litellm-proxy.ts |
||
|
|
7c04f46f12 |
Merge PR #2632: fix(config): register Life Chronicle keys so the documented enable command works
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cde2f0ec79 |
Merge PR #2628: perf(contextual-retrieval): bound per-chunk synopsis concurrency
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
92b6f00833 |
Merge PR #2625: fix(security): prevent leaking admin bootstrap token to non-TTY logs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0d76183528 |
Merge PR #2621: fix(trajectory): stop negative metrics from inverting regression signals
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f4593769aa |
Merge PR #2617: fix(ai): promote DeepSeek reasoning_content when content is empty
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9c04c42f89 |
Merge PR #2616: fix(migrations): let force-retry escape completed ledger entries
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
819e807ed3 |
Merge PR #2615: feat(extract_atoms): honor pack manifest extractable flag in page discovery
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cd4e3d2c47 |
Merge PR #2611: fix(minions): default timeout for contextual reindex
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9695c7383a |
Merge PR #2596: fix(search): port the CJK keyword fallback to the Postgres engine (PGLite parity)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5008b287e4 |
v0.42.59.0 chore(release): five verified community fixes — changelog + version bump (#2797)
Rolls up the five fixes merged as #2735 #2736 #2737 #2738 #2739 (issues #2724 #2677 #2723 #2726, plus the think slice of #2200). Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
010847c020 |
fix(think): enforce source scope across gather (#2200) (#2739)
Carry the caller's scalar or federated source scope through every think gather stream: hybrid page retrieval, takes keyword/vector retrieval, and graph traversal. Adds source predicates to the takes retrieval methods in both engines. Part of #2200 (the think slice; #2200 stays open as the tracking issue for the remaining by-slug read ops). |
||
|
|
8e84c5b4a1 |
fix(facts): parse escaped pipes in facts fence round-trips (#2726) (#2738)
parseRowCells now splits on unescaped pipes only and decodes escaped pipes while preserving ordinary backslashes and empty cells, so facts whose text contains literal | survive the fence->DB reconcile instead of being silently deleted. Fixes #2726. |
||
|
|
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. |
||
|
|
42ab0956a4 |
fix(migrate): preserve sources and scope resume targets (#2677) (#2736)
migrate --to now copies the complete source catalog before pages (fixes the pages_source_id_fkey failure on multi-source brains), and resume manifests carry an opaque target identity so a checkpoint from one target is discarded for a different target. Fixes #2677. |
||
|
|
2fca124468 |
fix(schema): unblock pre-v121 schema replay (#2724) (#2735)
Adds the missing timeline_entries.event_page_id forward-reference bootstrap probe + bare-column repair to both engines so pre-v121 brains can replay the current schema and reach migration v121. Fixes #2724. |
||
|
|
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> |
||
|
|
f29f24dfb1 |
fix(config): register takes.bootstrap_enabled too — same unregistered-key class
Hit while enabling the takes bootstrap on a live brain: the onboard remediation's documented enable key fails 'Unknown config key' exactly like auto_chronicle did. Registered + pinned by the same regression test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
f2e7a87910 |
test(find-trajectory): pin embedding dims — kill the shard-order 1280/1536 flake
engine-find-trajectory.test.ts hardcodes Float32Array(1536) vectors but let initSchema size its vector columns from process-global gateway state (getEmbeddingDimensions(), default 1280). Whether the file passed depended on which test files ran before it in the shard: any predecessor leaving the gateway configured without dims (or a bare CI env) yields vector(1280) and every insert dies with 'expected 1280 dimensions, not 1536'. Adding test files to the repo reshuffles the weight-packed shards, so unrelated PRs trip it (seen on #2629/#2630 CI, test (5)). Same fix + rationale as cosine-rescore-column.test.ts, which documents this exact class: configureGateway(1536) in beforeAll, resetGateway in afterAll. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
7639493e8c |
feat(autopilot): wire the nightly conversation-parser probe into the loop
The v0.41.16.0 probe module (runConversationParserNightlyProbe) shipped as a callable phase with its scheduler wire-up deferred; the follow-up never landed. Result: zero call sites — the enable flag did nothing, the D10 tokenmax default-on never fired, and doctor's conversation_parser_probe_health was a hardcoded 'Skipped' stub that ignored config and mode. This adds the deferred wiring as autopilot step 4.6, mirroring the nightly quality probe (4.5): - Flag reads dual-plane (DB row from gbrain config set wins; file plane fallback); search.mode=tokenmax keeps the probe default-on per D10. - Fixtures resolve from the gbrain package root (the committed test/fixtures/conversation-formats/), NOT the brain repoPath. Compiled binaries carry no source tree: skip quietly with a once-per-process stderr note instead of writing failure rows that would flip doctor to WARN on every binary install. - New parser-probe audit trail (audit-parser-probe.ts, shared audit-writer primitive, parser-probe-YYYY-Www.jsonl). rate_limited outcomes are NOT logged: the loop ticks every few minutes, so auditing each skip would flood the file. The 24h gate (parserProbeRanWithin) reads real outcomes. - Doctor stub replaced with computeConversationParserProbeHealthCheck: enable hint when off+silent, WARN on any non-pass run in 7 days. - try/catch posture identical to 4.5 — a probe failure never crashes the loop and never bumps consecutiveErrors. Tests: audit round-trip + rate-limit gate, doctor check branches, and source-shape pins for the wiring protections. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
22a9c985d2 |
test(find-trajectory): pin embedding dims — kill the shard-order 1280/1536 flake
engine-find-trajectory.test.ts hardcodes Float32Array(1536) vectors but let initSchema size its vector columns from process-global gateway state (getEmbeddingDimensions(), default 1280). Whether the file passed depended on which test files ran before it in the shard: any predecessor leaving the gateway configured without dims (or a bare CI env) yields vector(1280) and every insert dies with 'expected 1280 dimensions, not 1536'. Adding test files to the repo reshuffles the weight-packed shards, so unrelated PRs trip it (seen on #2629/#2630 CI, test (5)). Same fix + rationale as cosine-rescore-column.test.ts, which documents this exact class: configureGateway(1536) in beforeAll, resetGateway in afterAll. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
e6d392ad08 |
fix(eval,probe): nightly probe reaches real verdicts — SDK model strip, live default slots, gold-answer QA judging
Three more layers of the same never-exercised enable path, each found by
running the probe end-to-end after the previous fix:
1. eval-longmemeval passed resolveModel's RECIPE ids (anthropic:claude-…)
straight to the raw Anthropic SDK — the API 404s on the prefixed string,
so every answer/extractor call failed and batches were all-upstream_error.
(think avoids this by routing through the gateway; this eval's raw-SDK
client is deliberate — hermetic — so strip via splitProviderModelId.)
2. DEFAULT_SLOTS pinned openai:gpt-4o after the OpenAI recipe dropped it, so
judge slot A errored 'not listed for OpenAI chat' on every install and
the panel could not reach its 2-model quorum without a Google key —
verdicts were permanently inconclusive. Bumped to a listed model and
added a slots↔recipes consistency test so defaults can't drift again.
3. The judge panel scored terse LongMemEval facts ('in widget-co') on a
rich-response rubric (DEPTH/SOURCING ≥7) with no gold answer and no
haystack — guaranteed FAIL even when healthy. eval-longmemeval now emits
the dataset's gold answer in each row; batch mode folds it into the
judge task when present; the probe adapter passes QA-shaped dimensions
(CORRECTNESS + DIRECTNESS; deliberately no grounding dimension — judges
who never see the haystack read correct extra detail as invented).
Verified end-to-end on a real brain: 9/10 fixture questions pass; the one
failure is a genuine multi-session aggregation miss (2 of 3 evidence
sessions retrieved), i.e. the probe now surfaces real signal instead of
plumbing artifacts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk
|
||
|
|
bce5f20c25 |
fix(autopilot): quality probe finds its fixture + rate-limit skips stop flooding the audit
Two more blockers on the same enable path, found while verifying the config-plane fix end-to-end: 1. Fixture root: the wiring passed resolveRepoRoot = repoPath (sync.repo_path, the user's BRAIN repo) but the committed fixture lives in the gbrain package. On every real install the probe error'd with fixture-not-found. The DI test harness passes process.cwd() — the gbrain repo in CI — which papered over it. Now resolves the package root from the module location, with repoPath kept as fallback. 2. rate_limited flood: the loop invokes the probe every cycle (~5-10 min); each within-24h skip wrote a rate_limited audit row (~hundreds/day), and doctor counts any non-pass outcome as bad — so enabling the probe flipped nightly_quality_probe_health to a permanent WARN by design. Skips are now non-events: no audit row; the real runs gate the 24h window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
3265dc2c93 |
fix(autopilot,doctor): nightly quality probe honors gbrain config set (dual-plane flag read)
The doctor check prints a paste-ready enable hint — gbrain config set autopilot.nightly_quality_probe.enabled true — which writes the DB config plane. But the autopilot gate and the doctor check both read only the file plane (~/.gbrain/config.json), so following the printed hint was a silent no-op: the probe never ran and doctor kept reporting disabled. Fix: resolveProbeEnabled / resolveProbeMaxUsd in nightly-quality-probe.ts apply the dual-plane rule already used by mcp.publish_skills (serve-http.ts): the DB row wins when present, the file plane is the fallback. Both call sites (autopilot loop gate + doctor health check) now share the helpers, so they can no longer diverge (doctor's old Boolean() read also disagreed with autopilot's === true on string values). Covers the max_usd cap with the same rule. Regression tests pin the helper semantics and the new wiring shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
f589739469 |
fix(config): register Life Chronicle keys so the documented enable command works
The v0.42.56.0 release notes say `gbrain config set auto_chronicle true`, but the key was never added to KNOWN_CONFIG_KEYS — the documented command fails with 'Unknown config key' and the operator has to discover --force by reading source. Registers 'auto_chronicle' plus the 'chronicle.' prefix (chronicle.tz and future knobs). Same registration class as the v0.42.42.0 spend-controls fix. Regression test pins both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQgByq4aqQq2PP8UHCdnfk |
||
|
|
cc90471a96 |
perf(contextual-retrieval): bound per-chunk synopsis concurrency
Replace the strictly sequential per-chunk synopsis loop with a bounded sliding worker pool (existing runSlidingPool helper). Results land in chunk order via index-addressed writes; code chunks still bypass the wrapper; embedding remains one page-level batch after all synopses. New knob GBRAIN_CONTEXTUAL_CHUNK_CONCURRENCY, default 4, clamped to [1,16]; 1 reproduces the prior sequential behavior exactly. Each chunk task still acquires/releases the global synopsis rate-lease, which remains the cross-worker governor; the lease id now travels from acquire to release instead of shared mutable state, and lease waits are abort-responsive. At 20-45s per synopsis call, a 120-chunk transcript page previously needed 60-90+ min wall time and routinely outlived job timeouts. |
||
|
|
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> |
||
|
|
41d72585d0 |
fix(security): #2624 banner shows 'from env' before non-TTY hidden guard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a21abd9d49 |
fix(security): #2624 don't print admin bootstrap token on non-TTY (log-leak)
serve --http printed the generated admin token in the startup banner unconditionally. In containerized deploys stderr ships to centralized log storage, turning the token into a standing secret in logs. Fail-safe default: the generated token now prints only when stderr is an interactive TTY. Non-TTY starts hide it (--print-admin-token forces it; $GBRAIN_ADMIN_BOOTSTRAP_TOKEN + --suppress-bootstrap-token already existed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9102ce75e1 | fix(trajectory): stop negative metrics from inverting regression signals | ||
|
|
89af5e3c09 |
fix(ai): promote DeepSeek reasoning_content when content is empty
DeepSeek-style reasoning models (deepseek-reasoner, GLM et al behind the openai-compatible recipe) can return the substantive answer in message.reasoning_content with content empty. The AI SDK adapter reads only content, so gateway.chat() yielded text:'' and callers classified the response as empty (observed: ~54% of per-chunk synopsis calls on one such model collapsed to title-only fallback, 1,030 pages). Add a recipe-scoped compat fetch (mirroring the voyage/zeroEntropy inbound-rewrite precedent) that promotes reasoning_content into content only when content is empty/whitespace; non-streaming JSON responses only. New recipe-level compat.fetch hook preserves the base_urls override chain. |
||
|
|
63f6665b73 |
fix(migrations): let force-retry escape completed ledger entries
statusForVersion short-circuited on any 'complete' entry before checking the trailing 'retry' marker, so --force-retry appended an inert row and a version marked complete with zero work done could never be re-run without hand-editing completed.jsonl. Check retry-latest first: an explicit --force-retry now yields 'pending' even past an earlier 'complete', while a stray 'partial' after 'complete' still cannot regress the version. |
||
|
|
78f3ebea33 |
feat(extract_atoms): honor pack manifest extractable flag in page discovery
Atom-extraction page discovery hardcoded EXTRACTABLE_PAGE_TYPES and ignored the active pack's `extractable: true` flags, so a type declared extractable in the manifest (e.g. `note`) never actually extracted. Closes the D2 TODO the code already flagged (extract-atoms.ts: 'future pack-aware refactor ... pull from the active pack manifest'). Resolve the allowlist as: legacy hardcoded floor UNION the pack's extractable types, MINUS synthesis outputs (atom, concept — extracting from these would loop, since concepts are synthesized from atoms). Mirrors facts/eligibility.ts, which excludes concept the same way. Back-compat: gbrain-base brains keep every legacy target via the union; fail-soft falls back to the legacy floor if the pack can't load. - pure unionExtractableTypes() policy, unit-tested - discoverExtractablePages + countExtractAtomsBacklog resolve from the pack - page-discovery fixture updated (note now extracts; concept stays excluded) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
62bee97d20 | fix(minions): default timeout for contextual reindex | ||
|
|
08bdae892e | fix(ai): add chat touchpoint to litellm proxy recipe | ||
|
|
0621687276 |
fix(search): port the CJK keyword fallback to the Postgres engine (PGLite parity)
websearch_to_tsquery('english') can't tokenize CJK, so the keyword arm
of hybrid search degrades badly for Korean/Chinese/Japanese queries on
the Postgres engine — particle-suffixed Korean often matches nothing.
PGLite has had the hasCJK() -> ILIKE + occurrence-count-ranking fallback
since v0.32.7; the Postgres engine never got it.
Port _searchKeywordCJK to postgres-engine.ts at both FTS sites
(searchKeyword dedup path + searchKeywordChunks), keeping the host
engine's conventions (sql.begin + SET LOCAL statement_timeout, named
limit params, its extra-filter set including types/exclude_slugs).
scoreExpr, ILIKE ESCAPE discipline, empty-query guard, and the
dedup/chunk-grain split are copied from PGLite exactly.
Measured on a private production Korean corpus (74k chunks):
particle-suffixed queries returned 7 rows via english FTS vs 152 via
the fallback; a common two-word query 21 vs 847 — a 20-40x recall gap
this closes.
Tests: 6 source-guardrail tests in test/postgres-engine.test.ts +
3 Postgres-vs-PGLite parity e2e tests with synthetic Korean fixtures.
bun run verify 31/31.
Co-Authored-By: Claude Fable 5 <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> |
||
|
|
dde1132a29 |
v0.42.55.0 fix(security): dotfile/skills/slug confinement + DCR consent default + schema-lint migration (#418 #419 #245 #1353 #1647 #171 #1385) (#2399)
* fix(security): confine routing dotfiles, skills dir, slugs, and transcription exec Shared src/core/path-confine.ts consolidates the realpath-containment idiom (moved from sources-ops.ts) and adds isTrustedDotfile + isWriteTargetContained. - .gbrain-source (source-resolver) and .gbrain-mount (brain-resolver) walk-up dotfiles are now lstat trust-gated: a symlink, foreign-owned, or world-writable file is refused on multi-user hosts (#418), fail-closed on stat error. - resolveWorkspaceSkillsDir + every skills-dir tier (env, cwd_walk_up, repo_root, cwd_skills, install_path) route through realpath containment so a symlinked workspace/skills can't escape the declared workspace (#419). - resolveSourceId/resolveBrainId realpath both sides of the registered local_path / mount prefix match so a symlinked cwd can't misattribute source/brain. - validateSlug rejects NUL/control, bidi/RTL overrides, backslashes, and URL-encoded path separators at the shared putPage/updateSlug chokepoint; write-through confirms the file path stays within the source tree. - transcribeLargeFile uses execFileSync arg-arrays + fs.rmSync (no shell), so a path with shell metacharacters is never parsed by a shell (#245). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(security): default dynamic-registration clients to authorization_code Self-registered DCR clients (the unauthenticated network registration path) previously defaulted to the client_credentials grant, which bypasses the /authorize consent screen. They now default to authorization_code; an explicit client_credentials request is rejected with invalid_client_metadata unless the operator opts in with the new --enable-dcr-insecure flag. A loud stderr WARNING prints at startup whenever DCR is enabled (#1353). Manual CLI/admin client registration is unchanged (operator-trusted). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(security): schema-lint hardening migration (search_path + view security_invoker) Migration v120 brings existing brains to the same posture as fresh installs: - ALTER VIEW page_links SET (security_invoker = on) on Postgres so the view honors the caller's RLS instead of the owner's (the view-through-RLS bypass). - ALTER FUNCTION ... SET search_path on the gbrain-owned trigger/event functions (both engines, IF EXISTS so engine-only functions are skipped; body untouched, so the load-bearing auto_enable_rls event trigger is unchanged). Closes #171. - Broaden the BYPASSRLS preflight in the historical RLS migration gates to honor superuser and inherited-role BYPASSRLS, so a superuser-connected fresh install no longer aborts (#1385). Fresh-install function definitions in schema.sql / pglite-schema.ts are born-correct (regenerated schema-embedded.ts). scripts/check-search-path.sh is a new CI guard (wired into verify) that fails if a trigger function in the schema base files is added without SET search_path. Postgres-only assertions live in the bootstrap E2E; the PGLite path is covered by test/migration-v120.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v0.42.55.0 fix(security): dotfile/skills/slug confinement, DCR consent default, schema-lint migration Bump VERSION + package.json to 0.42.55.0 and add the CHANGELOG entry for the security-hardening wave (#418 #419 #245 #1353 #1647 #171 #1385). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(security): note the DCR consent default in SECURITY.md (#1353) The "disable client_credentials, only allow authorization_code" guidance is now the built-in DCR default; document the new --enable-dcr-insecure escape hatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(todos): add takes_search + code_def to the federated by-slug P1 (#2200) The v0.42.55.0 eng-review codex pass flagged takes_search (holder-allowlist only) and code_def (brain-wide raw SQL over content_chunks) as remaining same-class surfaces. Noted on the existing #2200 P1 follow-up, with the caveat that the #2399 close-list deliberately keeps #1371/#2200 open until this lands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(security): correct plpgsql alias collision in #1385 BYPASSRLS gate (real-PG) The broadened BYPASSRLS preflight aliased `pg_roles r`, but several RLS DO-blocks already declare `r record` for their backfill FOR loop, so plpgsql resolved `r.oid`/`r.rolbypassrls` to the unassigned record variable → "record \"r\" is not assigned yet" on real Postgres (PGLite tolerated it; the DATABASE_URL-gated e2e jobs are the backstop). Renamed the subquery alias to `pr` at all 10 migrate.ts sites; also broadened the schema.sql base RLS gate the same way (with the `pr` alias) for #1385 consistency on superuser fresh installs, and regenerated schema-embedded.ts. Also fixes a PRE-EXISTING engine-parity bug (confirmed failing on clean origin/master): the relationalFanout shape compared `canonical_chunk_id`, a serial id that diverges between a fresh PGLite engine and a shared Postgres DB (setupDB TRUNCATEs without RESTART IDENTITY). Compare its presence, not the exact value. Validated on real Postgres (pgvector/pg16): migration v120 applies, the v35 RLS backfill runs, and engine-parity + postgres-bootstrap + jsonb-parity are green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
814258dda6 |
v0.42.53.0 fix(sync,db): #2339 op_checkpoints jsonb double-encode + bug-class sweep + CI guard (#2375)
* fix(sync): op_checkpoints pin write double-encodes jsonb — every sync aborts (#2339) recordCompleted bound JSON.stringify(array) to a $3::jsonb param via postgres.js .unsafe(), double-encoding it into a jsonb string scalar that violates the v119 op_checkpoints_completed_keys_array CHECK — aborting every multi-source sync on real Postgres at the first checkpoint write. PGLite parses the string silently, which is why unit tests stayed green and it shipped. Cast through $3::text::jsonb so the text->jsonb cast parses a genuine array. Adds a DATABASE_URL-gated parity test + a dedicated Postgres CI job so the guard can never silently skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(db): sweep positional jsonb double-encode sites + AST CI guard (#2324) Every executeRaw/.unsafe site that bound JSON.stringify(x) to a bare positional jsonb cast double-encodes on real Postgres (same class as #2339). Sweep them all to the text::jsonb form across query-cache, sources-ops, llm-base, calibration-profile, impact-capture, subagent, receipt-write, traversal-cache, symbol-resolver, and the agent/sources commands. Adds scripts/check-jsonb-params.mjs (AST-lite scanner for the positional form the legacy template grep misses, incl. generic-typed calls), wired into check-jsonb-pattern.sh, with a self-test. PGLite's native db.query is not scanned — it parses text to jsonb natively, so the bug can't occur there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(search,eval): alias-hop injected results carry page_id (contradiction-probe crash) applyAliasHop injected synthetic SearchResults without page_id (the `as SearchResult` cast hid the missing field), so listActiveTakesForPages bound undefined/NaN into ANY($1::int[]) and crashed the whole contradiction probe on real Postgres. Stamp page_id=page.id at the injection site and add a finite-id filter in generateIntraPagePairs as a defensive backstop (mirrors hybrid.ts:63). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(engines): positional jsonb binding rule (text::jsonb vs the double-encode trap) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v0.42.53.0 fix(sync,db): #2339 op_checkpoints jsonb double-encode + bug-class sweep + CI guard Bumps VERSION + package.json to 0.42.53.0, adds the CHANGELOG entry, and regenerates llms-full.txt. Ships the #2339 sync-abort hotfix, the repo-wide positional jsonb double-encode sweep, the alias-hop contradiction-probe crash fix, and the new positional-form CI guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: post-ship sync — jsonb invariant now covers the positional form + new guard CLAUDE.md JSONB invariant + KEY_FILES (sql-query, check-jsonb-pattern, op-checkpoint) now describe the #2339 positional double-encode class, the $N::text::jsonb fix, and the new check-jsonb-params.mjs guard. Regenerates llms-full.txt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
bb2e88c42a |
v0.42.52.0 fix(reliability): autopilot dead-job storm + supervisor wedge + sync/status/minion reliability (#2194 #2227 #1994 #1737 #1738 #1950 #1984) (#2287)
* test(supervisor): pin LOCK_HELD fence-exit is never counted as a crash (#2227) A duplicate supervisor loses the queue-scoped DB singleton lock (#1849) and exits LOCK_HELD before spawning a worker or emitting 'started'. summarizeCrashes counts only worker_exited, so the fence path is structurally uncountable. Pin it so a future refactor that logs worker_exited on the fence path fails here instead of silently re-introducing the crash-budget breaker-trip loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(autopilot): per-source cycle binds FS phases to source.local_path, not global repo (#2194 #2227) A per-source autopilot-cycle inherited the global sync.repo_path as brainDir while stamping DB freshness for source_id — mixed scope. FS phases (sync/lint/extract) ran against the wrong tree, so the failure-cooldown and freshness gates would attribute work to the wrong source. Resolve the source's local_path in the handler (reuse the archive-recheck SELECT) and bind brainDir to it; a pure-DB source gets null (FS phases skip) instead of falling through to the global checkout. Legacy no-source dispatch keeps the global repoPath. Prerequisite for the cooldown/split commits (codex outside-voice #8). Resolves TODOS:634. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(supervisor): detect a live supervisor via the DB lock under split $HOME (#2227) jobs supervisor status + doctor read the HOME-derived pidfile, so a supervisor started under a different $HOME (keeper=/root vs ops=/data) read as 'not running' while healthy — the false signal that drives an operator to spawn a duplicate. Both surfaces now fall back to the queue-scoped DB singleton lock (#1849), the HOME-independent authority, when the pidfile shows nothing. New isLockHolderLive keys on lock freshness (ttl + heartbeat steal-grace), never process.kill, so PID reuse can't false-positive (pid-liveness-alone-pid-reuse). Status surfaces the holder host/pid + recorded concurrency/max-rss from the latest started event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(supervisor): degraded retry instead of permanent give-up on crash storm (#1994 #2227) max_crashes_exceeded gave up forever, so a transient DB-pooler blip that tripped the soft budget wedged the queue until a human restart (#2227's breaker-trips tail). Crossing the soft budget now enters degraded mode: keep respawning with capped exponential backoff (60s cap — a paced retry, not a hot loop) and emit a loud crash_budget_degraded health_warn. The existing stable-run reset clears the count once a respawn survives >5min, so a recovered DB self-heals. Permanent give-up fires only at a much-higher hard ceiling (maxCrashes × 10), tunable/disablable via GBRAIN_SUPERVISOR_HARD_STOP_CRASHES (0 = never). Resolves TODOS:92. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(autopilot): clamp fan-out to worker concurrency + doctor warning (#2194) Fan-out resolved to 4 (Postgres) regardless of worker --concurrency, so surplus cycles queued behind the worker and raced the stalled-sweeper. Two fixes for the same mismatch: - resolveEffectiveFanoutMax clamps to max(1, concurrency-1) (reserve a slot), gated on a LIVE DB-lock holder so a stale started-audit row can't shrink throughput (codex #9/D5); no live holder → unknown → unclamped base. Escape hatch autopilot.fanout_clamp_to_concurrency. - doctor's autopilot_fanout_concurrency check warns when fan-out exceeds effective slots — the misconfig was silent before. Advisory (started-event concurrency), wired into both doctor surfaces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(autopilot): per-source failure cooldown — break the dead-job storm (#2194) Only SUCCESS gated dispatch, so a source whose cycle kept failing/timing-out re-fanned-out every 5-min tick forever (200+ dead jobs/24h). Now a failed source backs off with bounded exponential cooldown (10→120min). Read at DISPATCH from minion_jobs dead/failed rows (timeouts/RSS-kills dead-letter via SQL and never run handler code, so a write-only hook would miss them) AND re-checked at CLAIM time in the handler (codex #5: already-queued/retrying jobs). A success clears it (codex #7); null-source rows excluded (codex #6); engine-parity via executeRaw. Disable with autopilot.failure_cooldown_min=0. Fail-open if config/history reads error. Surfaced via fanout_cooldown_skipped + the fanout summary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(autopilot): split the cycle — per-source phases + one global-maintenance job (#2194 #2227) N per-source cycles each ran the brain-wide global phases (embed-all/orphans/ purge/…) concurrently, thrashing the same rows and taking the worker 4→10GB in <60s → RSS-kill → orphaned stalls. Split them: per-source jobs now run only source-scoped (+ mixed) phases and stamp last_source_cycle_at; a new autopilot-global-maintenance job runs the global phases ONCE per window (idempotency_key + maxWaiting:1 = structural single-flight) and stamps autopilot.last_global_at. This is the codex-endorsed design that replaced the rejected skip-and-stamp-fresh approach (codex #1/#2): no freshness poisoning, no starvation — global work always runs as its own job, never marked done when it wasn't. PHASE_SCOPE is now a runtime partition (GLOBAL ∪ NON_GLOBAL == ALL). last_full_cycle_at still written for doctor/legacy (no longer a global gate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(doctor): guard nullable engine in supervisor DB-lock fallback (#2227) Follow-up to the supervisor-visibility commit: doctor's engine binding is BrainEngine | null, so the inspectLock fallback must guard on a non-null engine (tsc TS2345). No behavior change — a null engine simply skips the DB-lock probe and falls back to the pidfile reading, as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(doctor): categorize autopilot_fanout_concurrency check as ops (#2194) Follow-up to the fan-out/concurrency commit: the doctor-categories drift guard requires every check name in doctor.ts to belong to exactly one category set. Add the new autopilot_fanout_concurrency check to OPS_CHECK_NAMES (infrastructure liveness, alongside wedged_queue/supervisor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: update KEY_FILES for the autopilot cycle split + supervisor degraded-retry (#2194 #2227) Post-ship document-release: refresh the KEY_FILES current-state entries that drifted — cycle.ts (GLOBAL/NON_GLOBAL phase split + last_source_cycle_at / autopilot.last_global_at), jobs.ts (per-source local_path brainDir, claim-time cooldown, autopilot-global-maintenance handler), supervisor.ts + child-worker (degraded retry instead of permanent give-up; hard ceiling), db-lock.ts (isLockHolderLive), handler-timeouts (new handler). Regenerated llms bundle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(minions): handleTimeouts counts the timed-out run as a spent attempt (#1737) The per-job timeout_at dead-letter (handleTimeouts) set status='dead' without incrementing attempts_made, unlike the wall-clock and stall dead-letter siblings. It is the FIRST killer to fire for the long-lane handlers (subagent / embed-backfill / autopilot-cycle) because timeout_ms is stamped at submit, so a timed-out long job reported `attempts: 0/N (started: N)`. Mirror the siblings with attempts_made + 1 (terminal, no retry). Safe against double-count: the worker sweep runs handleStalled -> handleTimeouts -> handleWallClockTimeouts sequentially and awaited, each guarded on status='active', so the first to dead-letter excludes the row from the rest. Regression assertions added (test/minions.test.ts + e2e/minions-resilience.test.ts) so the increment can't be silently dropped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent): recognize trailing switches in `agent run`, keep prompts freeform (#1738) parseRunFlags() broke flag parsing at the first positional token, so any flag after the prompt (`gbrain agent run "do X" --detach`) was swallowed into the prompt string and silently ignored. Now the no-value switches --detach/--follow/ --no-follow are hoisted when they trail the prompt, while everything else stays verbatim: an unknown --word is treated as prompt text (no "unknown flag" throw), a --switch mid-prompt is preserved, and `--` suppresses hoisting entirely for a literal escape. Value-flags now reject a missing or flag-shaped value (and --max-turns/--timeout-ms a non-number) instead of capturing undefined/NaN. Contract change: a prompt that starts with or trails an unguarded --word no longer errors; a literal trailing --detach needs `--`. Help text updated; tests revised + extended (test/agent-cli.test.ts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sync): honest live-sync status + progress-aware stall-abort (#1950) Finishes the #2255 honest-freshness story for two gaps it left. (a) `gbrain sources status` printed "idle" while a sync proc held the per-source lock (the reported bug). New shared liveSyncStatus() helper in db-lock.ts reads the SAME live-lock signal `gbrain doctor` uses; runStatus now shows "running" (BACKFILL column + a sync_running field in --json) and suppresses the misleading "never synced" warning while a sync is live. One helper, so the surfaces can't drift (doctor/status retrofit tracked as a follow-up). (b) A sync wedged-but-alive kept refreshing its lock heartbeat (it fires on its own timer) and hadn't hit the wall-clock deadline, so only a manual pkill freed it. New in-band stall watchdog keys off FORWARD IMPORT PROGRESS (progress.tick), not the heartbeat: if no file completes for GBRAIN_SYNC_STALL_ABORT_SECONDS (default 900s), it aborts via a controller composed into opts.signal, so the drain returns partial() (last_commit unchanged, next run resumes from the checkpoint) and withRefreshingLock releases the lock. Limits, documented in code: a single file slower than the window trips it; a fully starved event loop won't fire the timer (the wall-clock hard deadline is that backstop). Tests: liveSyncStatus (live/expired/none/per-source) in db-lock-inspect; the resolveStallAbortSeconds env matrix in sync-hard-deadline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(status): version field + per-section --deadline-ms budget (#1984) `gbrain status` had no version in its JSON envelope and could hang on a slow connection with no way to get a partial answer. Two additions: - version: the StatusReport JSON now carries the local gbrain CLI version so a poller can pin behavior to a build. Thin-client also surfaces remote_version (the brain server's version), and the get_status_snapshot MCP op reports its version for that parity. - --deadline-ms=N / --fast: a shared wall-clock budget. Each section is raced against the REMAINING budget via Promise.race (NOT process-watchdog, which SIGKILLs and can't return partial output), so one slow/hung section can't strand the snapshot — it's marked stale and the rest still return. The envelope gains partial:true + stale_sections[]; exit code stays 0 (a snapshot was produced). Invalid --deadline-ms → exit 2. Tests: parseDeadlineFlag + withSectionDeadline (hermetic), the usage-error exit, version presence in the PGLite envelope, and the op's version key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sync): report stall_timeout distinctly + document in-flight limit (#1950) Pre-landing review (codex + adversarial): the stall watchdog aborted opts.signal but the per-iteration abort checks returned partial('timeout'), collapsing a wedge-reap into a user --timeout/SIGINT so JSON consumers couldn't tell them apart. Add a 'stall_timeout' reason (set via a stallAborted flag) on the three import-loop abort sites; deletes/renames-phase and checkpoint sites stay 'timeout'. Sharpen the watchdog comment: the abort is observed BETWEEN files, so a hang inside a single importFile is not interrupted until it returns (TODO: thread a cancellation signal through importFile). * fix(agent): `--` escape suppresses trailing-switch hoisting anywhere (#1738) Pre-landing review: the leading-flag loop breaks at the first positional, so the `escaped` flag only fired for a leading `--`. A `--` placed after a positional left trailing-switch hoisting active, so `agent run note -- body --detach` silently detached and dropped the `--` as junk. Suppress hoisting whenever a literal `--` appears in the prompt. Regression test added. * fix(status): deadline-ms usage-error + scoped stale_sections + cancel losing remote call (#1984) Pre-landing review (codex): (1) bare `--deadline-ms` with no value silently fell through to no-budget/--fast instead of a usage error; (2) thin-client timeout reported both sync+cycle stale even under `--section sync`, naming a section the caller excluded (local path was already correct); (3) the section race abandoned the remote promise locally but didn't cancel the in-flight MCP call — pass the budget as timeoutMs so the losing side actually cancels. Regression test added. * v0.42.52.0 fix(reliability): autopilot dead-job storm + supervisor wedge + sync/status/minion reliability (#2194 #2227 #1994 #1737 #1738 #1950 #1984) Bundles the already-reviewed autopilot/supervisor stabilization (#2194 #2227 #1994: cycle split, per-source failure cooldown, fan-out clamp, degraded supervisor retry, DB-lock live-supervisor detection) with four operational fixes: minion timeout attempt-accounting (#1737), agent-run trailing-flag parsing (#1738), honest live-sync sources status + progress-aware stall watchdog (#1950), and status version + --deadline-ms partial result (#1984). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: document GBRAIN_SYNC_STALL_ABORT_SECONDS env knob (#1950) Post-ship doc sync (/document-release): add the sync stall watchdog env var to the CLAUDE.md sync-tuning table (Five → Six knobs) + regenerate the llms bundle. * test: quarantine #2249 fanout tests as *.serial (R1 env-isolation) (#2194) The cherry-picked autopilot-fanout-clamp + doctor-autopilot-fanout-concurrency tests mutate process.env.GBRAIN_AUDIT_DIR in beforeEach/afterEach, which the check:test-isolation R1 lint flags (parallel shards load multiple files per process). Rename to *.serial.test.ts (sanctioned quarantine — they run under --max-concurrency=1) instead of restructuring the reviewed test bodies. No logic change; both files stay green (9 tests). Fixes the failing verify CI check. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9bf96db807 |
v0.42.51.0 fix(sync): contention-free clock + checkpoint integrity + honest sync freshness (#2255)
* fix(sync): contention-free page-generation clock — sequence swap The page-generation clock backed the query-cache Layer-1 bookmark via a FOR EACH STATEMENT trigger running `UPDATE page_generation_clock SET value=value+1 WHERE id=1`. That took a transaction-length RowExclusiveLock on one tuple, so every concurrent page writer serialized on the prior writer's COMMIT — sync ran at ~0.8 cores regardless of worker count. Swap to a SEQUENCE bumped by nextval() (a microsecond LWLock, never a row lock). The clock's only contract is monotonic advancement on any page INSERT/UPDATE/DELETE; last_value is non-transactional, so rolled-back or concurrent-uncommitted writers only OVER-invalidate the cache (lose a hit), never serve stale. - migration v118: CREATE SEQUENCE + load-bearing 2-arg setval (is_called= true, floor 1, seeded >= old clock and MAX(generation)) + repoint the trigger function body + DELETE query_cache so no old-clock bookmark survives the swap. v107 left immutable. - query-cache-gate.ts: 3 readers -> SELECT last_value FROM page_generation_clock_seq. - schema.sql + pglite-schema.ts (+ regenerated schema-embedded.ts) ship the sequence on fresh install; table + trigger names retained. - tests: clockValue reads last_value; mechanism proof (trigger fn uses nextval not the row UPDATE); rollback-advances-clock safety pin; real PGLite sequence round-trip (is_called gotcha); shape test requires _seq. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sync): op_checkpoints array-shape guard — CHECK + repair + defensive loader completed_keys is JSONB and the checkpoint loader runs jsonb_array_elements_text over it. A non-array (scalar) value makes that throw "cannot extract elements from a scalar", which takes down the whole UNION load — including the valid op_checkpoint_paths child rows — and loses all checkpoint progress for that key. No current writer produces a scalar, but an older binary / external script / future bug could. Make the corruption class structurally impossible and self-healing: - migration v119: LOCK TABLE (so an out-of-band scalar can't land between repair and constrain; no-op on single-connection PGLite), repair any pre-existing scalar to '[]' (op_checkpoint_paths child rows are the append-only source of truth, so the reset loses nothing), then add the named CHECK (jsonb_typeof(completed_keys) = 'array') via a pg_constraint IF NOT EXISTS guard. A DB-enforced always-on guard — the correct pattern vs a migration verify-hook, which never runs on already-stamped brains. - schema.sql + pglite-schema.ts (+ regenerated schema-embedded.ts) ship the same NAMED inline CHECK so fresh installs match migrated brains and v119 skips the duplicate. - op-checkpoint.ts loader: gate the legacy arm on jsonb_typeof = 'array' so a scalar parent is skipped (children still load) instead of throwing the whole union, and log a specific corruption warning when one is seen. - tests: CHECK rejects a scalar (exactly one constraint, no blob+migration dupe); loader survives a scalar parent and returns the children; v119 repair converts a scalar to '[]'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(doctor): report actively-running sync via live lock, not stale freshness A slow source that makes partial progress every cycle but never fully completes used to read as permanently "stale" / "never synced" because last_sync_at only advances on a full successful sync. The naive fix (treat recent checkpoint banking as "in progress") is unsafe: a blocked sync banks the good files then writes no anchor, so banking can't tell in-progress from wedged. Use the only honest signal: a LIVE, non-expired per-source sync lock (inspectLock + syncLockId against gbrain_cycle_locks). Every non-skipLock sync holds it and refreshes it; a blocked/failed sync's process has exited (no lock row) and a wedged holder stops refreshing (TTL lapses), so either correctly falls through to the stale path and is NEVER masked. An actively-syncing source (including a never-synced source doing its first sync) counts as synced_recently, preserving the pinned 3-bucket invariant. The lock lookup reuses doctor's existing dynamic db-lock import and swallows any throw (stub engine, pre-lock-table brain) to false, so it can only ADD an in-progress verdict, never suppress a real stale one. Tests (real PGLiteEngine + real lock rows): stale+no-lock -> fail; stale+live-lock -> ok; never-synced+live-lock -> ok; never-synced+no-lock -> fail; expired-TTL lock -> fail (wedged not masked); blocked source with banked checkpoint rows but no lock -> still fail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sync): honest --force-break-lock diagnostic when no lock is held --force-break-lock used to emit the same terse "Lock ... is not held (nothing to break)" line and exit 0 even when a sync was genuinely wedged, sending the operator down a dead end — the wedge was not a held lock. Keep rc=0 (breaking a non-existent lock is idempotently successful; flipping the exit code would break automation), but under --force say plainly that nothing was broken and point at the real next step (gbrain sync / gbrain doctor) plus a `wedge_hint` field in --json output. The non-force path is byte-for-byte unchanged. runBreakLock is exported for the test. Tests: force+no-lock -> wedge_hint JSON + human hint, rc 0; non-force+no-lock -> unchanged terse line, no hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(doctor): surface the in-progress sync holder in the freshness message Plan-completion follow-up to the BUG 4 live-lock signal: when a source is actively syncing, name the holder (pid + host) in the check message instead of silently folding it into synced_recently. The note is appended only when something is in progress, so steady-state messages stay byte-for-byte unchanged (the pinned exact-message + 3-bucket-invariant tests still pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sync): pre-landing review fixes — monotonic clock seed, scoped CHECK guard Adversarial (codex) review of the implementation diff caught three: - P1 (correctness): the fresh-schema setval was not monotonic. initSchema replays the schema blob, and the unconditional setval(MAX(generation)) could move page_generation_clock_seq.last_value BACKWARD on an already-upgraded brain, letting a stored query_cache bookmark serve stale rows. Seed via GREATEST over the sequence's OWN last_value (+ old table value + MAX(generation)) in all 3 fresh schemas and migration v118, so a replay is idempotent — mirrors the old table's ON CONFLICT DO NOTHING. Pinned by a new monotonic regression test. - P2: v119's CHECK-exists guard keyed on conname only (not globally unique). Scope it to conrelid = 'op_checkpoints'::regclass. - P3: in-progress note ran into the prior sentence in fail/warn doctor messages; separate it with '. '. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: make Anthropic/ZE no-key tests hermetic against a dev config key These "no key" tests cleared only ANTHROPIC_API_KEY / ZEROENTROPY_API_KEY from the env, but hasAnthropicKey() and checkZeEmbeddingHealth() also read the key from ~/.gbrain/config.json. On a dev machine whose real config holds a key, the no-key assertions flipped and the tests failed locally (they passed only in key-less CI). Add a shared with-env emptyHome() helper and point GBRAIN_HOME at an empty dir in every no-key path so loadConfig finds nothing — matching the already-hermetic anthropic-key / gateway-probe tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.44.1.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(key-files): sync doctor + op-checkpoint entries to v0.44.1.0 truth checkSyncFreshness now reports an actively-running sync via the live per-source lock (names holder pid+host, counts as synced_recently) instead of flagging it stale; loadOpCheckpoint gates the legacy union arm on jsonb_typeof = 'array' so a scalar parent can't take down the whole load, and migration v119's CHECK constraint makes the corruption class structurally impossible. Reference docs describe current behavior only — both entries updated in place, no release-clause appends. Guard + llms freshness test green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: re-version to v0.42.51.0 (natural next-off-master) Maintainer override of the queue allocator's leap to 0.44.1.0 (it jumped past in-flight sibling PR claims at 0.42.50/0.43.0/0.44.0). Take the natural next slot in the 0.42.x line above the immediate sibling claim (0.42.50.0); a merge re-bump resolves any collision if a cathedral PR lands first. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(e2e): bound + retry the OpenClaw install so a transient npm hang can't burn the Tier 2 budget The Tier 2 (LLM Skills) job failed at 30m16s — the `npm install -g openclaw@2026.4.9` step hung on a transient npm/registry stall (orphan `npm install openclaw` was still running at cancel time) and consumed the entire 30m job budget that v0.42.50.0 (#2254) introduced. The install normally finishes in under a minute (Tier 2 is ~4m end to end on master), so this is flaky-install infra, not a test failure. Wrap the install in `timeout 120` + a 3-attempt retry loop with an 8-minute step backstop: a hung attempt is killed in 2 min and retried instead of eating the whole job. Same bound-the-hang philosophy as #2254's job timeouts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
70d5f36db6 |
v0.42.50.0 ci: reliability hardening — cancel-superseded + per-job timeouts + actionlint + hermetic E2E env (#2254)
* ci: cancel superseded runs + per-job timeouts on test.yml & e2e.yml Ports the GH-Actions hygiene gbrain already uses in heavy-tests.yml to the two hot-path workflows. concurrency cancels a superseded run (keyed on PR number for pull_request events — fork-safe — with github.ref fallback for push/scheduled); frees runners and stops a stale-SHA run reporting a flaky failure on an obsolete commit. Per-job timeout-minutes (test matrix 15, verify 12, serial 15, slow-* 12, e2e tier1 20 / tier2 30, trivial jobs 5-10) convert a wedged job from a 6-hour zombie (GitHub's default) into a fast legible fail. fail-fast:false already set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: add actionlint workflow (rhysd/actionlint v1.7.11, SHA-pinned) Lints workflow YAML on .github/workflows/** changes so a malformed workflow / bad action ref / missing-permission bug is caught before it ships a broken pipeline. gbrain edits these workflows often; cheap preventive guard. Mirrors GStack's actionlint job, SHA-pinned to gbrain's convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(e2e): scrub operator/agent env before E2E (hermetic runner) A dev or Conductor shell exports CONDUCTOR_*/MCP_*/OPENCLAW_*/GBRAIN_* overrides that silently change test behavior, making hermetic E2E non-hermetic and its failures unreproducible across machines. run-e2e.sh drops those prefixes before bun starts (denylist — PATH/HOME/TMPDIR/DATABASE_URL survive; GBRAIN_HOME kept for the existing HOME isolation). Adapts GStack's buildHermeticEnv to gbrain's shell runner. Verified: a 78-test e2e file passes with DATABASE_URL surviving + a planted GBRAIN_BRAIN_ID scrubbed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v0.42.50.0 ci: reliability hardening — cancel-superseded + per-job timeouts + actionlint + hermetic E2E env Ports GStack's CI-reliability hygiene to gbrain's hot-path workflows: concurrency cancel-in-progress (PR-number keyed), per-job timeout-minutes (no more 6-hour zombie jobs), an actionlint workflow, and an operator-env scrub in run-e2e.sh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7968f84077 |
v0.42.49.0 feat(pace): native DB-contention pacing for embed/sync backfills (#2240)
* feat(pace): composable DB-contention pacer primitive createDbPacer/createNoopPacer (concurrency permit + in-band EWMA + jittered cooperative sleep, abort-throws, fail-open) + named pace-mode bundles (env>config>bundle, default off) + shared embed-backfill lock key. * feat(embed): wire DB-pacing into embed paths + single-flight + bounded keyset re-entry embedStaleForSource + CLI embedAllStale/embedAll lower worker count to the resolved cap and observe()/pace() their DB ops; embed job + embed-backfill handler resolve env>config>bundle; CLI --pace flags; --background carries overrides into the job payload; single-flight via shared per-source lock; budget-timer re-arm around paced sleeps; EmbedResult.pacing telemetry. * feat(sync): shared DB-pacer permit across parallel worker engines One pacer spans the per-worker PostgresEngines (the multi-pool permit case); observe() import writes, pace() between files, dispose on all exit paths. * docs(pace): CLAUDE.md Pace Mode section + regenerated llms bundle * fix(pace): pre-landing review fixes (Codex P1/P2) - never unref() the cooperative-sleep timer (could exit mid-sleep) - pace() excludes the wall-clock budget + re-arm after each sleep - pacing only lowers concurrency, never raises above an operator cap - serialized job pace resolves at config tier so GBRAIN_PACE_* still wins - --pace-max-concurrency consumes its value token * chore: bump version and changelog (v0.42.48.0) Native DB-contention pacing for embed/sync backfills. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version to v0.42.49.0 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7ea92d602c |
v0.42.48.0 feat(durability): auto-harden brain repos for git durability on PAT+URL (#2241)
* feat(git): divergence-safe pull, push-probe, default-branch detection for brain durability Add GIT_ENV_AUTH + divergenceSafePull (skip-on-dirty, conflict-abort-clean, never-mid-rebase), detectDefaultBranch, pushProbe, and an env-gated GBRAIN_GIT_ALLOW_FILE_TRANSPORT escape hatch. Export GIT_ENV. pullRepo's --ff-only contract is unchanged. * feat(durability): brain-repo hardening core (hook, helper, cron, PAT, AGENTS rules) hardenBrainRepo/unhardenBrainRepo: local untracked post-commit hook + committed brain-commit-push.sh (one shared push-retry template), repo-scoped credential with existing-helper reuse, push-probe verify, active-resolver-file rules with taxonomy from _brain-filing-rules.json, minimal DB-free pull cron. PAT redaction via redactSecretsInText. * feat(sources): harden/pull/unharden commands + auto-harden on add --url sources harden/pull/unharden subcommands; --pat-file/--no-harden on add; auto-harden managed clones on add; unharden-before-remove. cli.ts pre-connect early-exit for DB-free 'sources pull --path' (the cron entry, never opens PGLite). * test(durability): unit + integration coverage for brain-repo durability git helpers, core harden/unharden, hook+helper E2E (real background push), cron generators. 41 tests across 4 files. * chore: bump version and changelog (v0.42.48.0) Brain-repo git durability: auto-harden a brain's working tree (local auto-push hook, committed commit-push helper, always-on agent rules, DB-free pull cron, repo-scoped credential, push-probe verify) the moment gbrain gets a PAT + URL. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sources): route harden exit code through setCliExitVerdict A raw process.exitCode write is zeroed by the owned-verdict flush-exit (#2084 PGLite-Emscripten pollution defense); cli-exit-verdict-pin guard caught it. Use setCliExitVerdict(3) so 'sources harden' actually reports needs-attention to cron/automation. * docs: document brain-repo durability (KEY_FILES + multi-source guide) KEY_FILES: extend git-remote.ts entry (divergenceSafePull, pushProbe, detectDefaultBranch, GIT_ENV_AUTH, GBRAIN_GIT_ALLOW_FILE_TRANSPORT) + add brain-repo-durability.ts/sources-harden.ts entry. multi-source-brains.md: add a Durability (auto-harden) how-to covering sources harden/pull/unharden, --pat-file, the guarantees, and the security posture. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9d88680a51 |
v0.42.47.0 feat(skillpack,advisor): brain-resident skillpacks + proactive gbrain advisor (#2180) (#2231)
* feat(skillpack): brain_resident manifest fields + init-brain-pack scaffolder + tools version-skew lint Add optional brain_resident/schema_pack to the v1 manifest (additive, forward-compatible). New runInitBrainPack scaffolds a brain-resident pack (brain_resident:true, exact gbrain_min_version, 5-section machine-parseable README) beside brain content; applyWritePlan factored out of init-scaffold. brain-pack-lint validates each skill's declared tools: against the serving op set (E6 version-skew). Wires gbrain skillpack init-brain-pack. * feat(skillpack): Topology A brain-pack discovery on sources add + bounded nag After 'gbrain sources add', if the source ships a brain_resident pack, print an agent-readable advisory (ask the user before scaffolding). nag-state.ts tracks declines per (source-repo brain_id, source, pack) with escalate-then-suppress; declines count only on CLI-interactive displays, never cron/MCP. Fail-open: a malformed/absent pack never breaks sources add. * feat(advisor,skillpack): list_brain_skillpack MCP tool + gbrain advisor Topology B: dedicated source-scoped list_brain_skillpack op + get_skill source_id disambiguation (brain-resident-locate.ts); git scaffold-spec never a server FS path; source-aware schema match. LEARN_INSTRUCTION + serve-http banner. gbrain advisor: read-only ranked actions from brain state (8 resilient collectors, shared renderer, JSONL history, --json severity exit codes, local-only argv --apply dispatcher). Exposed over MCP behind mcp.publish_advisor (default off, read-only on remote; workspace collectors no-op remotely). Generalizes post-install-advisory to a single current-state recommended set (install→scaffold). * feat(skills): bundle gbrain-advisor skill + weekly cron recipe + ranking eval skills/gbrain-advisor teaches a harness to run gbrain advisor on a cadence and ping the user (read-only; ask before fixing). Registered in manifest.json, RESOLVER.md, openclaw.plugin.json. E4 ranking-precision eval on seeded-defect fixtures (100%). * chore: bump version and changelog (v0.42.47.0) Brain-resident skillpacks + gbrain advisor (#2180). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: sync CLAUDE.md + KEY_FILES for brain-resident skillpacks + advisor (#2180) Skill count 29->30, Skills section gains the brain-resident skillpacks + advisor capability, KEY_FILES gets current-state entries for the new modules. Regenerate llms bundles. * test,fix: align stale assertions with generalized advisory + advisor resolver triggers (#2180) - post-install-advisory.test.ts: install→scaffold wording (the install verb was removed); restore two-column in book-mirror copy; drop the removed skillpack-list line. - RESOLVER.md: gbrain-advisor trigger now fuzzy-matches a declared frontmatter trigger (resolver round-trip D5/C). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate llms bundle for updated advisor resolver row (#2180) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c023a6041d |
v0.42.46.0 fix(engine): federated read scope reaches by-slug reads (#2200) (#2239)
* fix(engine): federated sourceIds[] scope on by-slug secondary reads (#2200) getTags/getLinks/getBacklinks/getTimeline (both engines) + TimelineOpts now accept a federated `sourceIds[]` read grant, precedence over scalar sourceId, filtering `source_id = ANY($::text[])` — mirroring getPage from v0.42.37.0. - getTags: `page_id = (subquery)` -> `IN (subquery)` + DISTINCT so a slug present in >1 granted source unions tags instead of throwing on a multi-row subquery. - getLinks/getBacklinks: federated branch scopes ALL THREE page endpoints (from, to, AND the authoring origin) so a cross-source link can't disclose a foreign slug. Scalar/unscoped branches unchanged (trusted internal callers keep the cross-source view). - getTimeline: Postgres 8-branch cartesian tree collapsed to one fragment-composed query; PGLite adds the sourceIds branch to its dynamic WHERE. * fix(ops): route by-slug reads through the federated source scope (#2200) get_page resolves tags against the concrete page's source; get_tags/get_links/ get_backlinks/get_timeline route through sourceScopeOpts(ctx) (replacing the copy-pasted scalar `ctx.sourceId ? {sourceId} : {}`). New linkReadScopeOpts promotes an UNTRUSTED remote scalar scope to sourceIds[] so legacy/pre-federated tokens also get all-endpoint link scoping; trusted local CLI keeps cross-source. * test: federated read scope on by-slug reads + engine parity (#2200) Per-op federated reads, isolation (out-of-grant -> empty), cross-source decoy guard, far-endpoint + origin leak guards (F1), same-slug union (D3A), empty-array contract, scalar-remote promotion (D1), getTimeline date-window after the Postgres fragment refactor (D5A), and engine-parity arms for all four methods. * chore: bump version and changelog (v0.42.46.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(key-files): document federated by-slug read scope + linkReadScopeOpts (#2200) The #2200 fix routed get_page tags + get_tags/get_links/get_backlinks/ get_timeline through the federated source scope and added sourceIds[] to the engine read methods + TimelineOpts. Bring KEY_FILES.md to current state: the operations.ts entry's sourceScopeOpts read-op list now includes the by-slug reads and documents the linkReadScopeOpts helper (three-endpoint link scoping + untrusted-remote scalar promotion); the engine.ts entry notes the by-slug read methods + TimelineOpts carry the same sourceIds[] federated axis. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
5c49225e4b |
v0.42.45.0 feat(sync): delta-aware cost estimator — stop wedging the daily cron (#2139) (#2224)
* feat(core): shared computeSyncDelta + spend-posture module (#2139) sync-delta.ts: ONE implementation of "what changed since last_commit", consumed by both the sync executor and the inline cost estimator so the gate's dollar figure can't drift from what the sync imports. spend-posture.ts: spend.posture config + parseUsdLimit/formatUsdLimit off-switch parsing (off/unlimited/none → Infinity; undefined at the budget boundary so ledger rows never serialize null). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(sync): delta-aware cost estimator + non-TTY auto-defer + per-source failure acks (#2139) The inline-embed cost gate was a ~400x phantom: it priced the entire tree whenever the working tree was dirty (always, on an active brain), then blocked the daily cron with exit 2. Now: - performSyncInner + the estimator both route through computeSyncDelta, so the estimate mirrors execution (fetch-first delta; dirty-but-caught-up tree → $0). - shouldBlockSync is posture-aware; non-TTY above floor AUTO-DEFERS embeds to capped backfill jobs (exit 0) instead of wedging — single shared runInlineCostGate on both --all and single-source paths. - --full prices delta + stale backlog (full sync sweeps it inline). - off/unlimited on the cost knobs; tokenmax bypasses the backfill cap (still ledgered) but never the cooldown. - --skip-failed/--retry-failed scoped per source; the D15 parallel refusal is lifted (the #1939 ledger is per-source + lock-serialized). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(config): register spend-control keys + validate spend.posture (#2139) Adds spend.posture + the five previously --force-only spend knobs to KNOWN_CONFIG_KEYS so `config set` accepts them directly (removes the archaeology the issue complained about), and rejects invalid spend.posture values at set time with a paste-ready hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(reindex,enrich,onboard): spend.posture across the remaining cost gates (#2139) reindex-code: tokenmax makes the cost gate informational; --max-cost accepts off/unlimited. enrich + onboard --auto: tokenmax lifts the refuse-without-cap guardrail and runs UNCAPPED (spend still ledgered by BudgetTracker). Explicit --max-usd always wins over posture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cost-gate, delta estimator, spend-posture, off-switch coverage (#2139) New sync-delta + sync-cost-estimate unit suites; rewritten cost-gate serial tests (auto-defer instead of exit 2, posture, off-switch, format split, single-source); parseUsdLimit/posture-aware shouldBlockSync; backfill cap-off + tokenmax-bypass + cooldown-still-refuses; config known-key acceptance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(spend-controls): single spend-control surface + ref-map + follow-up TODOs (#2139) New docs/operations/spend-controls.md (every gate, key, default, off switch, posture interaction); CLAUDE.md reference-map row; two P3 follow-up TODOs (measured chunk-count gating, per-source defer granularity). llms bundles regenerated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(spend): SSRF-harden estimator fetch + complete off/uncapped across reindex/enrich/onboard (#2139) Ship-stage codex pre-landing review caught four P1s in the secondary cost gates: - The delta estimator's fetch-first ran `git fetch` through the plain git() helper, bypassing the GIT_SSRF_FLAGS + GIT_TERMINAL_PROMPT=0 hardening that real sync uses. Added `fetchRemote()` to git-remote.ts (same flags as pullRepo) and route the estimator through it — a cost preview / dry-run can no longer hit a remote through a less-protected path. - `reindex --max-cost off`, `enrich --max-usd off`, `onboard --auto --max-usd off` were parsed but didn't actually proceed/uncap. Now: explicit off (and spend.posture=tokenmax) proceed past the confirmation/missing-cap refusal AND run uncapped. enrich threads an Infinity sentinel mapped to "no BudgetTracker ceiling" (never raw Infinity → no null in audit rows); reindex/onboard use their native undefined=uncapped path. Spend still ledgered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.42.45.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(KEY_FILES): update sync/embedding/git-remote/reindex entries to post-#2139 truth document-release pass: the cost-gate entries described the pre-#2139 behavior (full-tree-ceiling estimator, --skip-failed-rejects-under-parallel, exit-2 confirmation gate). Updated to current truth — delta-aware estimator via the shared computeSyncDelta, per-source failure acks under parallel, non-TTY auto-defer (no exit 2), posture-aware shouldBlockSync. Added entries for the two new core modules (sync-delta.ts, spend-posture.ts) + fetchRemote on git-remote.ts + reindex --max-cost off. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
090bb53203 |
v0.42.44.0 docs(tutorial): point AlphaClaw deploy link at the official site (#2165) (#2171)
* docs(tutorial): point AlphaClaw deploy link at the official site (#2165) Step 4 of the personal-brain tutorial linked to the wrong top-level domain for AlphaClaw. Corrected so the deploy step works as written. * chore: bump version and changelog (v0.42.44.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |