Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to GBrain will be documented in this file.

## [0.42.63.3] - 2026-07-22

**Eva Brain install success now means the brain is callable, not that every optional content-maintenance cycle has run.**

### Fixed

- **Installer health now proves callability without hiding runtime failures.** The updater permits explicit content-maintenance gaps (`cycle_freshness` and `links_extraction_lag`), and permits `sync_freshness` only for sources whose search succeeds in the same health run. Unknown, schema, migration, index, database, and malformed doctor results still fail the install. The health probe runs through the guaranteed Bun runtime and remains compatible with successful minimal doctor JSON from older installs.
- **Non-actionable taxonomy drift no longer fails doctor.** `type_proliferation` stays blocking only when the active schema pack has a built-in successor; otherwise the report identifies custom taxonomy drift without suggesting a no-op onboard command.
- **Removed stale source-freshness calls.** The Eva updater no longer invokes `sources cycle-freshness` or `sources sync-freshness`, which current upstream GBrain does not expose.

## [0.42.63.2] - 2026-07-22

**Eva Brain installs now finish cleanly against upstream GBrain 0.42.63.1.**
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.42.63.2
0.42.63.3
6 changes: 6 additions & 0 deletions docs/downstream-carry-patches.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ Eva Brain uses upstream GBrain as its core. Core changes in this file are tempor
- Reason: only a truly missing `pages` table means an empty brain. Other database failures must surface instead of being reported as zero pages.
- Removal condition: upstream distinguishes missing-table errors from unexpected query/database failures.

## Actionable type-proliferation diagnostics

- Files: `src/core/onboard/checks.ts`, `test/onboard-pack-upgrade-checks.test.ts`
- Reason: a current schema pack with no built-in successor cannot remediate custom type drift through `gbrain onboard`. The diagnostic must report the observed and declared type counts without failing the brain or recommending a no-op command.
- Removal condition: upstream makes type-proliferation severity depend on an available pack successor or another concrete, previewable remediation.

## Install-time migration policy

- File: `package.json`
Expand Down
2 changes: 1 addition & 1 deletion openclaw.plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "gbrain",
"name": "gbrain",
"version": "0.42.63.2",
"version": "0.42.63.3",
"description": "Eva Brain/GBrain skills and personal knowledge brain with provider-neutral hybrid search",
"family": "bundle-plugin",
"configSchema": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
"bun": ">=1.3.10"
},
"license": "MIT",
"version": "0.42.63.2",
"version": "0.42.63.3",
"overrides": {
"@hono/node-server": "2.0.10",
"body-parser": "^2.3.0",
Expand Down
2 changes: 1 addition & 1 deletion plugins/gbrain-codex/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gbrain-codex",
"version": "0.42.63.2",
"version": "0.42.63.3",
"description": "Full-surface Codex Desktop adapter for a local Eva Brain/GBrain install.",
"author": {
"name": "Electric Sheep",
Expand Down
2 changes: 1 addition & 1 deletion plugins/gbrain-codex/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eva-brain/gbrain-codex",
"version": "0.42.63.2",
"version": "0.42.63.3",
"description": "Codex Desktop local plugin adapter for Eva Brain/GBrain.",
"type": "module",
"private": true,
Expand Down
2 changes: 1 addition & 1 deletion plugins/openclaw-gbrain/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eva-brain/openclaw-gbrain",
"version": "0.42.63.2",
"version": "0.42.63.3",
"description": "OpenClaw native plugin for Eva Brain/GBrain search, query, and OAuth-backed media extraction.",
"type": "module",
"main": "index.js",
Expand Down
60 changes: 58 additions & 2 deletions scripts/eva-brain-health.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const WORKSPACE_DOCS_QUERY = process.env.EVA_BRAIN_WORKSPACE_DOCS_QUERY || 'runb
const DEFAULT_TIMEOUT_MS = 30_000;
const CANONICAL_WORKSPACE_DOCS_PATH = '/root/.openclaw/workspace/docs';
const CANONICAL_WORKSPACE_RUNBOOKS_PATH = '/root/.openclaw/workspace/docs/runbooks';
const ADVISORY_DOCTOR_FAILURES = new Set([
Comment thread
100yenadmin marked this conversation as resolved.
'cycle_freshness',
'links_extraction_lag',
]);

const requireOpenClaw = process.argv.includes('--require-openclaw') || process.env.EVA_BRAIN_REQUIRE_OPENCLAW === 'true';
const allowMissingSupportKb = process.argv.includes('--allow-missing-support-kb') || process.env.EVA_BRAIN_ALLOW_MISSING_SUPPORT_KB === 'true';
Expand Down Expand Up @@ -154,6 +158,17 @@ function summarizeSearch(result) {
};
}

function staleSourceIds(message) {
return [...String(message).matchAll(/Source '([^']+)'/gu)].map(match => match[1]);
Comment thread
100yenadmin marked this conversation as resolved.
}

function isAdvisoryDoctorFailure(failure, verifiedSearchSources) {
if (ADVISORY_DOCTOR_FAILURES.has(failure.name)) return true;
if (failure.name !== 'sync_freshness') return false;
const sourceIds = staleSourceIds(failure.message);
return sourceIds.length > 0 && sourceIds.every(sourceId => verifiedSearchSources.has(sourceId));
}

function walkMarkdownFiles(dir, limit = 12) {
const files = [];
const stack = [dir];
Expand Down Expand Up @@ -295,11 +310,47 @@ function main() {
const pluginInspect = run(openclaw, ['plugins', 'inspect', 'gbrain', '--runtime', '--json'], {
timeoutMs: 15_000,
});
const doctorReport = parseJson(doctor.stdout, null);
const doctorFailures = Array.isArray(doctorReport?.checks)
? doctorReport.checks
.filter(check => check?.status === 'fail')
.map(check => ({
name: String(check.name ?? 'unknown'),
category: String(check.category ?? 'unknown'),
message: String(check.message ?? ''),
}))
: [];
const doctorHasBasicReport = Boolean(
doctorReport &&
typeof doctorReport === 'object' &&
Number.isFinite(doctorReport.health_score),
);
const doctorDiagnosticAvailable = Boolean(
Comment thread
100yenadmin marked this conversation as resolved.
(doctor.ok ? doctorHasBasicReport : Array.isArray(doctorReport?.checks)) &&
!doctor.timedOut &&
doctor.signal == null,
);
const verifiedSearchSources = new Set();
if (supportKb?.pages > 0 && supportKbSearchSummary.ok && supportKbSearchSummary.resultCount > 0) {
verifiedSearchSources.add(SUPPORT_KB_SOURCE);
}
if (workspaceDocs?.pages > 0 && workspaceDocsSearchSummary.ok && workspaceDocsSearchSummary.resultCount > 0) {
verifiedSearchSources.add(WORKSPACE_DOCS_SOURCE);
}
const doctorBlockingFailures = doctorFailures.filter(
failure => !isAdvisoryDoctorFailure(failure, verifiedSearchSources),
);
const doctorExitAcceptable = Boolean(
Comment thread
100yenadmin marked this conversation as resolved.
doctor.ok ||
(doctorFailures.length > 0 && doctorBlockingFailures.length === 0),
);

const report = {
ok: Boolean(
version.ok &&
doctor.ok &&
doctorDiagnosticAvailable &&
doctorExitAcceptable &&
doctorBlockingFailures.length === 0 &&
sourcesResult.ok &&
(!requireSupportKb ||
(supportKb &&
Expand All @@ -318,7 +369,12 @@ function main() {
version: version.stdout.trim(),
versionOk: version.ok,
doctorOk: doctor.ok,
doctor: parseJson(doctor.stdout, null),
doctorGate: 'advisory-content-only',
doctorDiagnosticAvailable,
doctorExitAcceptable,
doctorFailures,
doctorBlockingFailures,
doctor: doctorReport,
},
pages: {
total: totalPages,
Expand Down
65 changes: 19 additions & 46 deletions scripts/update-local-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,23 @@ doctor() {
return
fi
stop_stale_serve_if_requested
run env GBRAIN_SKILLS_DIR="$INSTALL_DIR/skills" "$HOME/.bun/bin/gbrain" doctor --json
local output
local cmd=(env GBRAIN_SKILLS_DIR="$INSTALL_DIR/skills" "$HOME/.bun/bin/gbrain" doctor --json)
printf '+'
printf ' %q' "${cmd[@]}"
printf '\n'
if [ "$DRY_RUN" = "true" ]; then
return
fi
if output="$("${cmd[@]}" 2>&1)"; then
printf '%s\n' "$output"
return
fi
printf '%s\n' "$output"
if [ "$RUN_HEALTH" = "false" ]; then
die "Doctor failed and --skip-health disabled the structured failure classifier. Rerun without --skip-health or repair the reported doctor failure."
fi
log "Doctor reported brain/content readiness gaps; continuing to the source-aware runtime health gate."
Comment thread
100yenadmin marked this conversation as resolved.
}

health_report() {
Expand All @@ -364,10 +380,6 @@ health_report() {
if [ "$WITH_WORKSPACE_DOCS" = "true" ] || { [ "$WITH_WORKSPACE_DOCS" = "auto" ] && [ -d "$WORKSPACE_DOCS_DIR" ]; }; then
require_workspace_docs="true"
fi
if [ "$RUN_HEALTH" = "auto" ] && [ "$WITH_SUPPORT_KB" != "true" ] && [ "$require_workspace_docs" != "true" ]; then
log "Skipping source-aware health report because no source package was requested or detected"
return
fi
local health_args=()
Comment thread
100yenadmin marked this conversation as resolved.
if [ "$WITH_OPENCLAW" = "true" ]; then
health_args+=(--require-openclaw)
Expand All @@ -381,10 +393,10 @@ health_report() {
health_args+=(--require-workspace-docs)
fi
if [ "$DRY_RUN" = "true" ]; then
run node scripts/eva-brain-health.mjs "${health_args[@]}"
run bun scripts/eva-brain-health.mjs "${health_args[@]}"
return
fi
run env GBRAIN_BIN="$HOME/.bun/bin/gbrain" node scripts/eva-brain-health.mjs "${health_args[@]}"
run env GBRAIN_BIN="$HOME/.bun/bin/gbrain" bun scripts/eva-brain-health.mjs "${health_args[@]}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor --skip-doctor in the health gate

When an operator passes --skip-doctor without also passing --skip-health, the explicit doctor step is skipped but this default health command still launches scripts/eva-brain-health.mjs, which unconditionally runs gbrain doctor --json. After the removed auto-skip above, this now affects ordinary updates with no source package requested, so --skip-doctor no longer does what the usage promises and can still fail or lock on the very doctor check the operator tried to bypass; either pass a skip flag/env into the health script or skip this gate when doctor is disabled.

Useful? React with 👍 / 👎.

}

provider_test() {
Expand Down Expand Up @@ -556,43 +568,6 @@ install_support_kb() {
run node "$kb_dir/scripts/status.mjs"
run "$HOME/.bun/bin/gbrain" sync --repo "$kb_dir" --source openclaw-support-kb --no-embed
embed_support_kb_if_provider_auth_available
disable_source_cycle_freshness_if_supported openclaw-support-kb
}

disable_source_cycle_freshness_if_supported() {
disable_source_freshness_if_supported "$1" cycle-freshness
}

disable_source_sync_freshness_if_supported() {
disable_source_freshness_if_supported "$1" sync-freshness
}

disable_source_freshness_if_supported() {
local source_id="$1"
local freshness_command="$2"
local output
local cmd=("$HOME/.bun/bin/gbrain" sources "$freshness_command" "$source_id" off)
printf '+'
printf ' %q' "${cmd[@]}"
printf '\n'
if [ "$DRY_RUN" = "true" ]; then
log "Dry-run: skipping optional $freshness_command disable execution"
return
fi
if output="$("${cmd[@]}" 2>&1)"; then
printf '%s\n' "$output"
return
fi
local normalized_output
normalized_output="$(printf '%s' "$output" | tr -d '\r' | sed -e 's/[[:space:]]*$//')"
local first_line
first_line="$(printf '%s\n' "$normalized_output" | sed -n '1p')"
if [ "$first_line" = "Unknown sources subcommand: $freshness_command" ]; then
log "Skipping $freshness_command disable; installed gbrain does not expose 'sources $freshness_command'."
return
fi
printf '%s\n' "$output" >&2
return 1
}

install_workspace_docs() {
Expand Down Expand Up @@ -621,8 +596,6 @@ install_workspace_docs() {
fi
run "$HOME/.bun/bin/gbrain" import "$WORKSPACE_DOCS_DIR" --source-id "$WORKSPACE_DOCS_SOURCE" --no-embed
embed_source_if_provider_auth_available "$WORKSPACE_DOCS_SOURCE" "Workspace docs"
disable_source_cycle_freshness_if_supported "$WORKSPACE_DOCS_SOURCE"
disable_source_sync_freshness_if_supported "$WORKSPACE_DOCS_SOURCE"
}

main() {
Expand Down
2 changes: 1 addition & 1 deletion skills/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gbrain",
"version": "0.42.63.2",
"version": "0.42.63.3",
"conformance_version": "1.0.0",
"description": "Personal knowledge brain with hybrid RAG search \u2014 GStack mod for agent platforms",
"skills": [
Expand Down
47 changes: 45 additions & 2 deletions src/core/onboard/checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface OnboardCheckResult {
name: string;
status: 'ok' | 'warn' | 'fail';
message: string;
details?: Record<string, unknown>;
};
remediations: RemediationStep[];
}
Expand Down Expand Up @@ -461,15 +462,30 @@ export async function checkTypeProliferation(
engine: BrainEngine,
): Promise<OnboardCheckResult> {
let declared = 15; // fallback to gbrain-base-v2 default if pack unavailable
let activeName: string | null = null;
let activeIdentity: string | null = null;
let builtInRemediationAvailable = false;
let successorLookupSucceeded = false;
let successorIdentity: string | null = null;
try {
const { loadActivePack } = await import('../schema-pack/load-active.ts');
const { loadActivePack, findPackSuccessors } = await import('../schema-pack/load-active.ts');
let dbConfig: string | undefined;
try {
dbConfig = (await engine.getConfig('schema_pack')) ?? undefined;
} catch { /* tolerate pre-config brains */ }
const active = await loadActivePack({ cfg: null, remote: false, dbConfig })
.catch(() => null);
if (active) declared = active.manifest.page_types.length;
if (active) {
declared = active.manifest.page_types.length;
activeName = active.manifest.name;
activeIdentity = active.identity;
const successors = await findPackSuccessors(active.manifest.name, active.manifest.version);
successorLookupSucceeded = true;
if (successors.length > 0) {
builtInRemediationAvailable = true;
successorIdentity = successors[0]!.identity;
}
}
} catch {
// Use fallback.
}
Expand All @@ -479,15 +495,40 @@ export async function checkTypeProliferation(
);
const warn = declared + 5;
const fail = declared * 2;
const details = {
distinct_type_count: n,
declared_type_count: declared,
active_pack_name: activeName,
active_pack_identity: activeIdentity,
built_in_remediation_available: builtInRemediationAvailable,
successor_lookup_succeeded: successorLookupSucceeded,
successor_identity: successorIdentity,
};
if (activeIdentity && successorLookupSucceeded && !builtInRemediationAvailable && n > warn) {
Comment thread
100yenadmin marked this conversation as resolved.
return {
check: {
name: 'type_proliferation',
status: 'ok',
message:
`${n} distinct page types (pack declares ${declared}); ` +
`active pack ${activeIdentity ?? activeName ?? 'unknown'} has no built-in type-unification successor. ` +
`Treat as custom taxonomy drift unless you define a custom pack with mapping_rules.`,
details,
},
remediations: [],
};
}
if (n > fail) {
return {
check: {
name: 'type_proliferation',
status: 'fail',
message:
`${n} distinct page types (pack declares ${declared}). ` +
(successorIdentity ? `Built-in successor available: ${successorIdentity}. ` : '') +
`Run \`gbrain onboard --check --explain\` to preview a pack upgrade ` +
`or define a custom pack with mapping_rules.`,
details,
},
remediations: [], // pack_upgrade_available check emits the actionable step
};
Expand All @@ -498,6 +539,7 @@ export async function checkTypeProliferation(
name: 'type_proliferation',
status: 'warn',
message: `${n} distinct page types vs ${declared} declared in pack — consider unification.`,
details,
},
remediations: [],
};
Expand All @@ -507,6 +549,7 @@ export async function checkTypeProliferation(
name: 'type_proliferation',
status: 'ok',
message: `${n} distinct typed values (pack declares ${declared})`,
details,
},
remediations: [],
};
Expand Down
Loading
Loading