Skip to content
Open
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
9 changes: 7 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,10 @@ Features across OpenClaw plugin (`skill/plugin/`), MCP server (`mcp/`), NanoClaw
| **Knowledge Graph (Core Hoist Tier 1)** | | | | | | | All via `@totalreclaw/core` 1.5.0 WASM/PyO3 with local fallbacks |
| Store-time dedup (best-match) | Yes (core) | Yes (core) | Yes (via MCP) | Yes (core) | Yes (via MCP) | Yes (core) | `find_best_near_duplicate` — returns highest-similarity match, not first |
| Bulk clustering | Yes (core) | Yes (core) | Yes (via MCP) | -- | Yes (via MCP) | -- | `cluster_facts` — greedy single-pass for consolidation tool |
| Pin status semantics | Yes (core) | -- | -- | -- | -- | Yes (core) | `is_pinned_claim`, `respect_pin_in_resolution` |
| Contradiction detection orchestration | Yes (core) | -- | -- | Yes (core) | -- | Yes (core) | `resolve_with_candidates` — full pipeline: detect → pin check → resolve → tie-zone |
| Pin status semantics | Yes (core) | -- | -- | Yes (core) | -- | Yes (core) | `is_pinned_claim`, `respect_pin_in_resolution`. Hermes (PRD-04 DEP-5): pin guard on LLM UPDATE/DELETE + pin state fed to the contradiction resolver. |
| Contradiction detection orchestration | Yes (core) | -- | -- | Yes (core) | -- | Yes (core) | `resolve_with_candidates` — full pipeline: detect → pin check → resolve → tie-zone. Hermes passes entity refs + pin state since PRD-04 DEP-5 and applies `skip_new`/`existing_pinned` only (see Known Gaps). |
| Pin guard on LLM UPDATE/DELETE | -- | n/a | -- | Yes | n/a | -- | PRD-04 F1 / DEP-5 — `python/src/totalreclaw/agent/pin_guard.py`: an auto-extraction UPDATE/DELETE whose target is pinned (core `is_pinned_claim` on the decrypted blob) is dropped; unreadable pin state fails closed (DELETE dropped, UPDATE stored as ADD, nothing tombstoned). MCP has no LLM UPDATE/DELETE path. OpenClaw plugin (parked): see Known Gaps. |
| Recall pin boost (core `pin_boost`, 1.5×) | -- | Pending | Pending | Yes (core) | Pending | -- | PRD-04 F1 / DEP-5 — core `RerankerConfig.pin_boost` + `Candidate.pinned`; `DEFAULT_PIN_BOOST` = 1.5; off by default (bit-identical ranking) and ignored without a query embedding. Hermes: every recall through `search_facts` (auto-recall is lexical-only until PRD-04 DEP-8, so it gets the boost with DEP-8). MCP: DEP-5 Part B, after the core release. A pin raises rank; it does not add a fact to every recall. |
| Decision log types | Yes (core) | -- | -- | -- | -- | Yes (core) | `DecisionLogEntry`, `find_loser_claim_in_decision_log` — enables pin-on-tombstone recovery |
| Shadow mode filtering | Yes (core) | -- | -- | -- | -- | Yes (core) | `filter_shadow_mode` — observer-only validation mode |
| Importance rubric (1-10 anchored) | Yes | Yes (via prompt) | Yes | Yes | Yes (via prompt) | Yes | Phase 2.2.6 — explicit band definitions in extraction prompt |
Expand Down Expand Up @@ -326,6 +328,9 @@ Managed Service tier model (single-chain Gnosis — ops-1 shipped, #283 closed 2
| Provisioned signing key is not revocable | MEDIUM | The bundle ships `signing.kind: "owner-eoa"` — the Smart Account owner key. It carries full account authority and cannot be revoked, because canonical `SimpleAccount` has no module dispatch and no owner setter. A compromised host still requires vault-identity rotation. The `session-key` variant (scoped to DataEdge `execute`/`executeBatch`, `valueMax = 0`, on-chain revocable) is specified in the schema but requires the signing-delegation phase. |
| Phrase remains on env-var and external-provider installs | LOW | Auto-migration covers `credentials.json` states only. `TOTALRECLAW_RECOVERY_PHRASE` and operator-authored external-provider payloads are read-only from the daemon's perspective and must be replaced by the operator. See `docs/guides/headless-deployment.md`. |
| Phrase not recoverable from a migrated host | LOW | After migration the host holds only derived material; the phrase cannot be reconstructed from it. Users must have their own copy. A one-cycle `credentials.json.bak` is the recovery path. |
| Pin guard on LLM UPDATE/DELETE — Hermes only | MEDIUM | PRD-04 DEP-5 guards Hermes auto-extraction. The parked OpenClaw plugin's LLM UPDATE/DELETE branches (`LLM dedup: UPDATE` / `LLM dedup: DELETE` in `skill/plugin/index.ts`) still tombstone `existingFactId` with no pin check, so a plugin auto-extraction can tombstone a fact pinned from any client. Port on un-park: check core `isPinnedClaim` on the target's decrypted blob before queueing the tombstone, mirroring `python/src/totalreclaw/agent/pin_guard.py`. |
| Recall pin boost — partial client coverage | LOW | Wired: Hermes (explicit recall and contradiction candidates; auto-recall once PRD-04 DEP-8 adds a query embedding). Pending: MCP managed recall (DEP-5 Part B, needs the core release). Not wired: OpenClaw plugin (parked), ZeroClaw (`rust/totalreclaw-memory` passes `pinned: false`), MCP self-hosted HTTP recall (`mcp/src/tools/recall.ts`, ranks via `@totalreclaw/client`). |
| Hermes contradiction resolver applies pin skips only | LOW | Since PRD-04 DEP-5 Hermes hands core's resolver entity refs + pin state, so detection runs; only `skip_new`/`existing_pinned` is applied. `skip_new`/`existing_wins`, `supersede_existing` and `tie_leave_both` are logged, not applied — PRD-04 DEP-12 owns applying them (bench-gated). |

---

Expand Down
3 changes: 3 additions & 0 deletions docs/specs/totalreclaw/memory-taxonomy-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ Every v1.1-compliant client MUST:
3. **Write on pin**: pinning a fact produces a NEW claim with `pin_status == "pinned"` that supersedes the original via the standard tombstone + new-fact pattern (same pattern as `retype` and `set_scope`). The new claim is wrapped in the v1 outer protobuf (`version = 4`) with `schema_version = "1.0"` inner JSON.
4. **Write on unpin**: unpinning produces a NEW claim with `pin_status == "unpinned"` (or omits the field — both are equivalent) and supersedes the pinned one.
5. **Round-trip**: readers at any compliance level MUST preserve the `pin_status` field on round-trip (read → write) so a fact pinned by one client does not silently lose its pinned status when surfaced by another.
6. **Guard LLM-guided rewrites** (PRD-04 F1 / DEP-5): an automatic UPDATE or DELETE produced by an extraction LLM MUST NOT tombstone a pinned claim. Before applying it, the client reads the target's pin state (`is_pinned_json` on the decrypted blob). Target pinned → drop the whole action; for UPDATE the replacement is not stored either, the same outcome as `SkipNew { reason: ExistingPinned }`. Pin state unreadable → fail closed on the destructive half: DELETE is dropped and UPDATE is stored as a plain ADD with no tombstone. Explicit user tools (`totalreclaw_forget`, `totalreclaw_unpin`) are user-initiated and unaffected. Reference: `python/src/totalreclaw/agent/pin_guard.py`.
7. **Feed pin state to the resolver**: a client that projects claims into the short-key `Claim` shape for `resolve_with_candidates` MUST carry `st: "p"` for pinned claims and the claim's entity refs (`e`). The resolver only compares claims that share an entity, and reads pin state from `st` on this shape.
8. **Recall boost (recommended)**: recall MAY rank pinned claims higher by setting `pinned: true` on the reranker candidate and `pin_boost` (core `DEFAULT_PIN_BOOST` = 1.5) in `RerankerConfig`. A pin raises rank; it does not add a claim to every recall. See `retrieval-v2.md` §Pin boost.

### Cross-client compatibility

Expand Down
20 changes: 20 additions & 0 deletions docs/specs/totalreclaw/retrieval-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,26 @@ assistant-source facts surface when the user query targets them.

---

## Pin boost (PRD-04 F1 / DEP-5)

**What:** `RerankerConfig.pin_boost: Option<f64>` (JSON `pin_boost`) multiplies the final score of candidates whose `Candidate.pinned` is `true` (JSON `pinned`, default `false`):

final_score = fused × source_weight × (pinned ? pin_boost : 1.0)

The multiplication happens after Tier 1 source weighting and before top-k truncation. `None` (the default), a non-finite value, a value ≤ 1.0, or an **empty query embedding** is a no-op. Callers that do not opt in therefore rank bit-for-bit as before, and no parity vector changed.

**Enabled value:** `DEFAULT_PIN_BOOST = 1.5` (core `reranker.rs`; equal to `PinConfig::default().hard_boost`). Pedro signed off 1.5× on 2026-04-28 (internal F1 pin-UX defaults note) as strong enough to lift pinned facts about 50% above unpinned ones without making every pin rank first. On-wire pins are binary (`pin_status == "pinned"`) and carry no pin timestamp, so the non-decaying hard-pin value applies. Soft-pin decay (`claims::pin_boost`) needs a pin timestamp on the wire first.

**Magnitude:** RRF-fused scores span only about 2× between rank 1 and rank 60, so 1.5× moves a candidate from combined rank r to roughly (60 + r) / 1.5 − 60. A relevant pinned fact just outside the top k moves into it. A pinned fact that matches neither signal stays out (core tests `test_pin_boost_lifts_pinned_candidate_just_outside_top_k_into_top_k`, `test_pin_boost_does_not_surface_an_irrelevant_pinned_fact`).

**Why no boost without a query embedding:** with no embedding, every candidate ties on cosine, and every candidate without a lexical match ties on one BM25 rank. Any multiplier would then lift every pinned fact above genuine matches. Hermes auto-recall is lexical-only until PRD-04 DEP-8 adds a query embedding, so its boost turns on with DEP-8. Explicit recall, contradiction-candidate recall and MCP recall all embed the query.

**Pin state source:** clients set `pinned` from core `is_pinned_claim` / `isPinnedClaim` on the decrypted blob (v1.1 `pin_status` or v0 `st == "p"`).

**Status:** Hermes: every recall through `search_facts`. MCP managed recall: DEP-5 Part B. Not wired: OpenClaw plugin (parked), ZeroClaw, MCP self-hosted HTTP recall.

---

## Tier 2 — Scope pre-filter (OPTIONAL, post-v1)

**Rationale:** If user query carries a scope hint ("at work…", "for my health…"), pre-filtering candidates by scope improves precision. Otherwise, scope is ignored at retrieval time (as today).
Expand Down
10 changes: 10 additions & 0 deletions python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- **[PRD-04 F1 / DEP-5] Pinned facts are protected from auto-extraction.** An auto-extraction UPDATE or DELETE whose target is pinned is now dropped (`agent/pin_guard.py`). When the pin state cannot be read, DELETE is dropped and UPDATE is stored as a plain ADD, so nothing is tombstoned. The contradiction resolver now receives entity refs and pin state, so it actually runs, and a new fact that contradicts a pinned fact is not stored (core `SkipNew { reason: existing_pinned }`). Other resolver outcomes stay log-only until DEP-12. Log lines carry the category only, never fact text or ids.
- **SKILL.md** no longer claims pinned facts surface in every recall. They rank higher when they match the query.

### Added

- **Recall pin boost.** Recall ranks pinned facts 1.5× higher through core `rerank_with_config(..., pin_boost=)` (`operations.PIN_BOOST_DEFAULT`). The option is feature-detected: a `totalreclaw-core` wheel without it keeps the old ranking. Core applies no boost when the query has no embedding (Hermes auto-recall until DEP-8).
- `TotalReclaw.get_fact_pin_status(fact_id)`.

## [2.5.2] — 2026-09-26

### Fixed
Expand Down
Loading
Loading