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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "second-brain",
"source": "./",
"description": "Self-evolving AI second brain. Auto-learns from sessions, discovers tools, maintains a local knowledge base, and self-critiques code quality.",
"version": "0.22.4"
"version": "0.22.5"
}
]
}
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "second-brain",
"description": "Self-evolving AI second brain. Automatically learns from sessions, discovers tools, maintains a local knowledge base, and self-critiques code quality — getting smarter with every interaction.",
"version": "0.22.4",
"version": "0.22.5",
"author": {
"name": "second-brain"
},
Expand Down
1 change: 1 addition & 0 deletions skills/upgrade/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Each migration is identified by its target version. Run only migrations whose ta
| To version | Migration | Idempotent check |
|---|---|---|
| **vector-deps health** (re-runs every upgrade) | Smoke-import `@huggingface/transformers` from `$CLAUDE_PLUGIN_ROOT/mcp`. On failure, run `bash $CLAUDE_PLUGIN_ROOT/bin/install-vector-deps.sh`. **Why**: mcp bundles mark `@huggingface/transformers` as esbuild `--external` because its native binaries (`onnxruntime-node`, `sharp`) can't be statically packed. A plugin cache refresh ships `dist/` but does not touch `node_modules/`, so vector search silently degrades to text-only on every fresh install or cache wipe. Without this gate the user sees no error — just empty embeddings and degraded recall. Idempotent: the script is a no-op when the package and import smoke-check both succeed. | `cd "$CLAUDE_PLUGIN_ROOT/mcp" && node --input-type=module -e 'await import("@huggingface/transformers"); console.log("ok")' >/dev/null 2>&1` — if exit 0, skip. Otherwise run `bash "$CLAUDE_PLUGIN_ROOT/bin/install-vector-deps.sh"`; report the ~70MB network requirement before installing. |
| **0.22.5** | Persona graph-capability awareness (read-only boundary preserved). The persona-as-collaborator protocol (`skills/using-second-brain/SKILL.md`) knew the **read** tools (`knowledge_search`/`episodic_search`/`knowledge_neighbors`) and the wingman graph-check, but never referenced `knowledge_relate`, so the persona-driven loop didn't close the loop on a relationship it confirmed mid-session. The wingman section now does — but as a **surface-only** step: when the work confirms (or retires) a typed relationship, the persona surfaces it once as a *suggested* `knowledge_relate` (confirmed/retired edges only, never speculative). It does **not** call the tool — edge curation stays owned by the three sanctioned writers (capture-time **extractor**, the user's manual `knowledge_relate`, the `knowledge-maintainer`), and the extractor records the relationship from the session transcript at Stop, so the graph still accrues **with no user interaction**. Deliberately keeps the persona a graph READER, not a 4th writer (the boundary `agents/dream-runner.md` enumerates). New `tests/test-persona-capability-awareness.sh` guards both halves: awareness of the capability AND the read-only `allowed-tools` boundary. Prompt/test-only — no state migration. | No precondition — bumping the marker is sufficient. |
| **0.22.4** | Post-0.22.3 completeness-audit cleanup (no functional regression in 0.22.2/0.22.3 — all four features audited `complete`; these are the surfaced gaps). (a) **`graph-migrate.sh` junk-edge root-cause fix**: the one-shot importer scraped `[[..]]` with a raw grep that also caught bash `[[ test ]]` expressions inside fenced code blocks, kept `[[target\|alias]]` display noise, and emitted edges with **no endpoint guard** — so the live graph accreted 6 `migration:v1` junk edges (shell fragments / aliases as `to`) that re-project as broken links. It now mirrors the sibling write path `merge-edges.sh`: skips fenced code blocks, reduces `[[target\|alias]]` to its target, and emits **only** edges whose target resolves to a real wiki page (prebuilt slug index → `resolves()` guard; case-preserving, so mixed-case slugs survive). (b) **Dream inline/background parity**: `skills/dream/SKILL.md` 2c RELATE gains the read-only `conflicts.jsonl` open-conflict echo that `agents/dream-runner.md` already had, so both dream paths report identically (guarded by new `tests/test-dream-conflict-echo.sh`). (c) Corrected stale `0.23.0` references in the write-time spec + two plans to the `0.22.2` version that actually shipped. Script/prompt/doc/test-only — no state migration. | No precondition — bumping the marker is sufficient. **Optional live cleanup** (the importer fix is forward-only; it does not retroactively remove the 6 existing junk edges): invalidate them append-only (never deleted, reversible) — back up `~/knowledge/graph/edges.jsonl`, then for each `source:"migration:v1"` assert whose `to` is a non-slug shell fragment/alias, append an `op:"invalidate"` line with `valid_to=<today>` and reason, then `knowledge_reindex`. |
| **0.22.3** | Dream-dogfood fixes (caught on the first production dream, 2026-06-02). (a) **`wiki-forget-score.sh` mawk-safe**: an empty/sparse `access-counts.json` made `acount` return empty → `awk "BEGIN{a=$acc;…}"` threw `syntax error at or near ;` on mawk (Pi default), degrading the FORGET access signal; now values pass via `-v` + numeric coercion (`x=x+0`) and `acount` defaults empty→0 (same bug class as the 0.21.4 lint-awk fix). (b) **Dream SUMMARIZE theme pages are slugged `theme-<id>`** (was `<id>` = the smallest member slug, which is usually the cluster's *anchor page* → `duplicate_slug` error). (c) **Dream ENRICH no longer hand-edits `related:`** (it is projected from the edge log → overwritten at reindex); it surfaces relationship gaps as `knowledge_relate` suggestions instead. Script/prompt-only — no state migration. | No precondition — bumping the marker is sufficient. Any `wiki/themes/<id>.md` from a pre-0.22.3 dream can be renamed to `wiki/themes/theme-<id>.md` to avoid a collision (or left — theme pages regenerate next dream). |
| **0.22.2** | Graphiti/GraphRAG adoption — three additive, flag-gated features (all back-compat; design in `docs/specs/2026-06-01-*`, decision in wiki `decisions/graphiti-graphrag-evaluation-2026-06-01`). (a) **Write-time contradiction detector** in `merge-edges.sh` — pure-bash R1 reintroduce / R2 opposing / R3 multi_parent (opt-in) flags structural edge collisions to a new append-only `~/knowledge/graph/conflicts.jsonl` sidecar; never blocks the edge append, fail-open, kill switch `SB_CONFLICT_DETECT=off`. Surfaced by a high-priority `session-load.sh` banner (`sb_conflicts_open_count`); drained by the **live** `knowledge-maintainer` Phase 3 (the dream stays read-only re: `graph/`). (b) **Dream SUMMARIZE phase** (7th-phase cycle; `SB_DREAM_SUMMARIZE=off` to skip) — deterministic label-propagation clustering (new `graph-cluster.ts` + bundled `graph-cluster-cli` + `scripts/graph-cluster.sh` shim) writes FORGET-protected `wiki/themes/` pages, staged and reviewed at `dream_accept`. (c) **Retrieval-grounded reconciliation** in `knowledge-maintainer` Phase 2/3 (`SB_RECONCILE=off` to disable) — `knowledge_search` top-k → ADD/UPDATE/NOOP/SUPERSEDE with a deterministic enumerated SUPERSEDE edge loop. Additive on disk: `conflicts.jsonl` and `wiki/themes/` appear lazily; with no `~/knowledge/graph/` and flags at defaults, behaviour is unchanged. No destructive migration. | No precondition — bumping the marker is sufficient. The `graph-cluster-cli` bundle ships in `dist/` (no native deps; the vector-deps health check is unaffected). To verify the detector is wired: `grep -q SB_CONFLICT_DETECT "$CLAUDE_PLUGIN_ROOT/scripts/merge-edges.sh"`. |
Expand Down
2 changes: 2 additions & 0 deletions skills/using-second-brain/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ This **refines** "silence is the default" (point 4) — it does not replace it.

Example: user says "let's refactor the router daemon" → `knowledge_neighbors router-daemon` → "Heads up: graph says router-daemon **affects** vps-collector + supplychain-monitor and **supersedes** pi-ip-ufw-sync (retired) — want me to check those dependents first?" → then refactor.

**Surface what the work confirms (close the loop — but you don't write the graph).** The wingman *reads* the graph; you are **not** a graph writer. Edge curation is owned by three paths: the capture-time **extractor** (it records relationships from this session's transcript at Stop — so the graph accrues with **no user interaction**), the user's manual `knowledge_relate`, and the `knowledge-maintainer`. So when the work **confirms** a typed relationship — the user says "X depends on / affects / is part of Y", or explicitly retires/replaces something — *surface it ONCE* as a suggested `knowledge_relate` (`type: requires|affects|relates|part_of|supersedes`; `invalidate: true` + `valid_to` for a retired edge), brief, per silence-default — then move on. The extractor picks it up from the transcript; you do not call the tool yourself. **Only confirmed/retired relationships — never speculative ones.** Edge *contradictions* are likewise the maintainer's to drain (surfaced by the SessionStart conflict banner), not yours.

**Native-memory cross-check.** Claude Code's built-in auto-memory (`MEMORY.md`) is already in your context at session start — you don't fetch it. If a fact there **contradicts** the second-brain graph/wiki on something load-bearing to the current action, flag the conflict ONCE; don't silently pick a side. The second-brain (graph + wiki) is the structured source of truth; native auto-memory is a flat scratchpad. (Only relevant while both memory systems run; inert once native is disabled — see `/second-brain:status`.)

## Engagement gate
Expand Down
50 changes: 50 additions & 0 deletions tests/test-persona-capability-awareness.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# The persona-as-collaborator protocol (skills/using-second-brain/SKILL.md) must stay
# AWARE of the second-brain toolkit so it uses each capability without the user manually
# invoking a tool ("persona aware what + how to use", no user interaction) — WHILE keeping
# the deliberate write boundary: the persona is a graph READER, not a writer.
#
# Edge curation is owned by three paths only (capture-time extractor, the user's manual
# knowledge_relate, the knowledge-maintainer). The always-on persona must NOT become a
# fourth graph writer (history-review regression class, 0.22.5). Instead the wingman READS
# (knowledge_neighbors) and SURFACES a suggested knowledge_relate when the work confirms a
# relationship — the extractor then records it from the transcript, so the graph still
# accrues with no user interaction.
#
# This guards both halves: awareness of the capability AND the read-only boundary.
set -u
ROOT="$(cd "$(dirname "$0")"/.. && pwd)"
SK="$ROOT/skills/using-second-brain/SKILL.md"
P=0; F=0; ok(){ P=$((P+1)); echo " PASS $1"; }; bad(){ F=$((F+1)); echo " FAIL $1"; }
[ -f "$SK" ] || { echo "FAIL: skills/using-second-brain/SKILL.md missing"; exit 1; }

ALLOWED=$(grep -m1 '^allowed-tools:' "$SK")

# Read-side tools the persona drives autonomously — must be granted.
for t in knowledge_search episodic_search knowledge_neighbors; do
printf '%s' "$ALLOWED" | grep -q "$t" && ok "read tool granted: $t" || bad "read tool missing from allowed-tools: $t"
done

# BOUNDARY: the persona stays READ-ONLY. knowledge_relate must NOT be in allowed-tools —
# granting it makes the always-on persona a 4th, ungoverned graph writer.
printf '%s' "$ALLOWED" | grep -q 'knowledge_relate' \
&& bad "knowledge_relate in allowed-tools — persona must stay read-only (4th-writer regression)" \
|| ok "allowed-tools is read-only (no knowledge_relate write grant)"

# AWARENESS: the protocol still references knowledge_relate so the persona can SURFACE it.
grep -q 'knowledge_relate' "$SK" \
&& ok "protocol aware of knowledge_relate (as a suggestion)" \
|| bad "protocol never mentions knowledge_relate — persona unaware of the capability"

# FRAMING: it must be a SURFACE/SUGGEST, and the real writers (extractor + maintainer) named,
# so the persona knows it is not the one writing.
grep -qiE 'surface|suggest' "$SK" && grep -qi 'extractor' "$SK" && grep -qi 'maintainer' "$SK" \
&& ok "framed as surface-only; extractor + maintainer named as the writers" \
|| bad "knowledge_relate not framed as surface-only with the real writers named"

# ANTI-SPAM: the load-bearing discipline clause must be present (not just the heading word).
grep -qiE 'never[^.]*speculative|only confirmed/retired' "$SK" \
&& ok "scoped to confirmed/retired only (never speculative)" \
|| bad "missing the 'never speculative' discipline clause (over-assertion risk)"

echo "PASS:$P FAIL:$F"; [ "$F" -eq 0 ]