Skill: ciklopentan/super-memori (version 4.0.23)

--- name: super-memori description: > Local-first hybrid memory skill for OpenClaw agents. Use when the agent needs to find, recall, search, or reuse past knowledge across episodic, semantic, procedural, and learning memory; when the user asks things like "what did we do about X", "remember", "find in memory", "что мы делали", or "найди в памяти"; when exact match and meaning-based recall both matter; or when designing, operating, or improving long-term agent memory on a local Ubuntu host. Includes manual-review learning improvement surfaces and memory-health guidance for degraded-mode detection, backup awareness before major operations, and risk-aware memory changes. Optimized for weak models by exposing a small command surface and clear degraded-mode rules. --- # Super Memori — v4.0.23 Project Skill **Release line:** `v4.0.23` unpublished current candidate line for the current-generation local-only memory runtime, advanced from the previously-published `v4.0.22` baseline with an enforceable per-session startup self-check hook for OpenClaw session start. **Release-line truth boundary:** the release line identifies the packaged skill artifact, not the live freshness state of whatever host runs it later. Current host freshness, semantic readiness, degraded state, and authority limits must be read from live command outputs such as `./health-check.sh --json` and `./query-memory.sh ... --json` at use time, not inferred from the release label alone. **Runtime prerequisites:** Semantic / hybrid retrieval is now implemented in the runtime, but it only becomes active on hosts where local semantic prerequisites are actually present: `sentence-transformers`, `numpy`, a locally available embedding model, local Qdrant, and vectors built from canonical files. Without them, the skill remains operational in lexical/degraded mode and surfaces that state explicitly. **Status:** v4 local-only memory runtime with real lexical, semantic, hybrid, temporal-relational, audit, and change-memory machinery in code, plus maintenance-only learning improvement / pattern-mining surfaces. Host state still matters: a host can run this release in lexical-only/degraded mode if semantic prerequisites or vector build state are missing. ## Current Release & Host State ### Artifact invariants (v4.0.23) - Lexical retrieval: active. - Change-memory: implemented and live. - Change-audit integration: implemented and live. - Minimal hot-change-buffer: implemented, internal, recovery-only, non-canonical, non-durable. - Semantic / hybrid / temporal-relational runtime: implemented in code, host-dependent activation. - Destructive auto-actions: disabled by default. - Overall release state: unpublished `4.0.23` built on top of the previously published baseline `4.0.22`. ### Host-state guidance - Do not infer live host state from this document. Read `./health-check.sh --json` and `./query-memory.sh ... --json` at use time. - Known host-scoped caveat: `system_hygiene` may surface as stale or partial-visibility on some hosts; treat that as host-scoped degraded evidence, not a clean-health signal and not an artifact-wide invariant. - Blocking issues for the active `4.0.23` line: none currently known after the enforceable per-session startup-hook hardening pass on this host. - Publish-readiness qualification: a host reporting `WARN` is still within the qualified `v4.0.23` baseline and does not by itself indicate artifact instability, setup failure, or a publication blocker; it only activates the degraded-mode routing contract defined below. ## Host profiles - `current-degraded-host` — safe-first, degraded-aware, no destructive auto-actions by default. - `equipped-stable-host` — stronger semantic / audit / index verification allowed, but still truth-tracked and rollback-aware. ## OpenClaw quickstart for weak models If you need the shortest safe operating path under OpenClaw, use this order and do not improvise extra steps: 1. `cd ~/.openclaw/workspace/skills/super_memori` 2. `./health-check.sh --json` 3. Read the JSON `status` field first. If status is `FAIL` → stop (emit `D-FAIL`). 4. If status is `WARN` → continue only in degraded mode, applying the matching `D-WARN-*` catalog row from the JSON-qualified state rather than from exit code alone. 5. Treat the bash exit code from `health-check.sh` only as a hard-stop gate: exit `3`, `4`, or `5` = immediate stop/escalate; otherwise continue to JSON parsing. 6. For first query: `./query-memory.sh "<your query>" --mode auto --json` 7. Trust the exact field set defined in Operating Rule 2 below. Do not infer stronger capability than the payload states. 8. Use `./memorize.sh` only for reusable lessons. 9. Use `./index-memory.sh` only when the contract below tells you to refresh or repair freshness. This quickstart does not guarantee semantic-ready operation on every host. It gives the strongest safe first path on OpenClaw while keeping degraded truth explicit. ## Weak-Model Decision Matrix Use this matrix when a weak model needs one-pass routing instead of hopping between quickstart prose, degraded-state catalog rows, and multiple exit-code tables. | Situation | Read first | Exact next action | Exact user-visible outcome | |---|---|---|---| | `health-check.sh --json` says `FAIL`, or bash exit is `3`/`4`/`5` | `status`, then exit code | Stop immediately. Do not query, memorize, or index. | Emit `D-FAIL` or the explicit argument/internal-error stop string. | | `health-check.sh --json` says `WARN` | `status`, `warnings[]`, freshness fields | Continue only in degraded mode. Select the matching `D-WARN-*` row from the catalog before any query/write/index step. | Emit the matching degraded string. Add `D-WARN-WRITE-ROLLBACK` for writes/maintenance. | | `health-check.sh --json` says `OK` | `status` | Normal path: run `./query-memory.sh --mode auto --json` for reads, or enter the pre-action gate for writes/maintenance. | No degraded warning required unless the later command payload says otherwise. | | `query-memory.sh` exit `0` and `degraded=false` | exit code, `degraded`, `mode_used`, result fields | Use the returned results normally. | Normal success. | | `query-memory.sh` exit `0` and `degraded=true` | exit code, `degraded`, `warnings[]`, freshness/authority fields | Use the returned results, but route through the matching `D-WARN-*` row first. | Degraded success, never normal success. | | `query-memory.sh` exit `1` and `degraded=false` | exit code, `degraded` | Report a clean miss. Do not escalate. | `No memory entries found for this query.` | | `query-memory.sh` exit `1` and `degraded=true` | exit code, `degraded`, `warnings[]` | Report a degraded miss, then apply the matching `D-WARN-*` row. | `No memory entries found for this query (degraded retrieval; results may be incomplete).` plus the matching degraded warning. | | `query-memory.sh` exit `2` | exit code, `degraded`, `warnings[]`, authority fields | Present returned results only as degraded/partial, then apply the matching `D-WARN-*` row. | Degraded partial-result response, never clean success. | | `query-memory.sh` exit `3`/`4`/`5` | exit code | Stop and escalate. | Emit the explicit unavailable / argument / internal-error stop string. | | Need `memorize.sh` or `index-memory.sh` | latest health state + rollback state | Run the pre-action gate first. If rollback is unclear, stop. | For WARN writes/maintenance, append `D-WARN-WRITE-ROLLBACK`. | | Request does not map to the four public commands | command mapping | Stop and ask for an in-scope command or human maintenance. | Emit the deterministic fallback line. | Priority rules: 1. `health-check.sh --json` `status` is the primary gate; bash exit code is only a hard-stop override. 2. For degraded routing, `D-WARN-COMBINED` beats `D-WARN-INDEX-STALE-ONLY`, which beats `D-WARN-GENERIC`. 3. `D-LOW-AUTHORITY` can apply on top of `OK` or `WARN` whenever the returned authority fields require it. 4. Never describe a degraded lexical fallback as semantic or hybrid retrieval. ## Execution ### OpenClaw host setup (weak-model executable) Use this only when preparing `super-memori` on an OpenClaw host for first use or when rebuilding local prerequisites. Execute the steps in order. ### 1. Enter the skill directory ```bash cd ~/.openclaw/workspace/skills/super_memori ``` ### 2. Confirm Python is available ```bash python3 --version ``` Expected: Python is installed and callable as `python3`. ### 3. Optional: install semantic prerequisites for fuller local capability ```bash pip3 install --user sentence-transformers numpy qdrant-client ``` Note: this installs only the Python client library; it does not install or start the Qdrant database service. Vector search will remain inactive until a reachable local Qdrant service is running and accessible. If this step fails, the skill can still run in degraded lexical-only mode and will report that state honestly. ### 4. Build or refresh local indexes ```bash ./index-memory.sh --full --json ``` If semantic prerequisites are still unavailable, this may complete in degraded mode. Report that honestly; do not pretend semantic readiness. ### 5. Verify host health ```bash ./health-check.sh --json ``` Expected result: - `OK` = normal operation - `WARN` = degraded but usable operation - `FAIL` = stop and escalate to maintenance ### 6. Run a first real query ```bash ./query-memory.sh "test query" --mode auto --json --limit 3 ``` Confirm that the output includes at least `mode_used`, `degraded`, `warnings`, and `results`. ### 7. Return to the normal operating contract After setup, follow only the four public commands and the `FOR ALL MODELS — REQUIRED OPERATING CONTRACT` below. ## Change-Memory Authority & Scope - Change-memory records are operational truth about agent-made changes. Structured records distinguish `applied`, `failed`, `reverted`, and `unverified` states, and support current-known-state + recall-bundle retrieval. `reverted` or `unverified` records must not be presented as active current state. - A minimal internal hot-change-buffer may hold very recent recovery-only state for recent agent-made changes. It is internal-only (no public command), RAM-resident, circular, non-canonical, non-durable, and aggressively noise-filtered. - Neither durable change-memory nor the hot-change-buffer replaces direct live filesystem / service / package inspection when exact current machine state is required. - Destructive auto-actions remain disabled by default. ## Change-Memory Noise Policy - Do not log harmless reads as change-memory. - Log only state-changing actions, failed writes, risky cleanup, package/service/config/runtime changes, and rollback events. ## Truth precedence ### For current machine state 1. Canonical files and direct live inspection 2. Lexical index 3. Semantic / vector index when healthy 4. Learning memory 5. Inferred recall last ### For agent-made change history 1. Change-memory records 2. Change-audit integration 3. Canonical files that directly confirm the current result 4. Inferred recall last ### Decision rule for live inspection Live filesystem / service / package inspection is **mandatory** before writes, config changes, service restarts, or any action that depends on exact current machine state, and whenever `./health-check.sh` returns `WARN` or `FAIL`. Memory-tool outputs are sufficient on their own only for read-only queries when `status=OK` and the returned payload has `degraded=false`. Never let inferred, stale, degraded, or retrieval-only surfaces override canonical truth. ## RUNTIME CAPABILITY MATRIX | Capability | Implementation status | Host requirement | |---|---|---| | Lexical search (SQLite FTS5) | **Implemented** | Always available | | Learning-memory retrieval | **Implemented** | Always available | | Semantic embeddings | **Implemented** | `sentence-transformers` + `numpy` + local model files | | Vector search (Qdrant local) | **Implemented** | Local Qdrant reachable + vectors built | | Hybrid fusion (RRF) | **Implemented** | Semantic stack ready | | Temporal / relation-aware rerank | **Implemented** | Semantic or temporal rerank path selected | | Integrity audit | **Implemented** | Local lexical DB available | | Pattern mining (block-level) | **Implemented** | `.learnings` populated | | Change-memory records | **Implemented** | Always available | | Change-audit integration | **Implemented** | Always available | | Optional semantic-ready host state | **Optional host capability** | Semantic deps + local model + vectors built | ## IMPLEMENTED VS OPTIONAL VS HOST-STATE TRUTH - **Implemented now in code:** lexical search, semantic search, hybrid fusion, temporal-relational rerank, integrity audit, relation-aware write metadata, block-level pattern mining, change-memory capture, change-audit integration, and a minimal internal recovery-only non-canonical non-durable hot-change-buffer. - **Optional host state:** semantic embeddings, vector search, and hybrid selection only activate when local semantic dependencies/model/vector state are actually ready. - **Not implemented / not claimed:** cloud backends, remote embeddings endpoints, remote vector DB, auto-promotion into durable memory, internet-dependent memory runtime, destructive auto-actions by default. ## Design Boundaries / Intentional Exclusions This skill is intentionally strong-by-constraint rather than feature-maximal. - It keeps a four-command public surface because weak OpenClaw models are more reliable when command choice, degraded routing, and rollback expectations fit in one short loop. - It rejects background auto-indexing and always-on memory daemons as public operating requirements because they hide freshness state, blur operator intent, and make degraded/rollback truth harder to surface cleanly on local hosts. - It rejects LLM-driven auto-consolidation or automatic promotion into durable memory because reusable lessons must remain operator-auditable and reversible. - It rejects cloud sync, hosted embeddings, and remote vector backends because this artifact is optimized for local-first OpenClaw deployment, local privacy, and host-truthful failure handling. - It prefers explicit host-state checks over ambient automation: current freshness, semantic readiness, and authority must come from live payloads, not from assumptions that a background subsystem stayed healthy. ## FOR ALL MODELS — REQUIRED OPERATING CONTRACT ### Public commands - `./query-memory.sh` - `./memorize.sh` - `./index-memory.sh` - `./health-check.sh` ### Maintenance-only entrypoints These are maintenance-only surfaces. Do not add them to the normal weak-model command loop unless maintenance is explicitly requested. - `./audit-memory.sh` — human/maintenance integrity audit surface. - `./repair-memory.sh` — human/maintenance repair planner/executor surface for controlled memory repair work. - `./list-promotion-candidates.sh` — human/maintenance review surface for candidate promotion workflow. - `./validate-release.sh` — human/maintenance release-surface verification gate; not a normal runtime memory command. - `./startup-self-check.sh` — startup-only health gate and safe self-heal surface for OpenClaw bootstrap/BOOT.md flows. - [REDACTED_SECRET] — enforceable per-session startup hook surface for OpenClaw `agent:bootstrap` runs. ### Weak-model operating rules 1. Default to `./query-memory.sh --mode auto`. The script will choose the strongest available local path and will report what actually ran via `mode_used`. 2. Trust the returned `mode_used`, `degraded`, `warnings[]`, `semantic_ready`, `semantic_fresh`, `index_fresh`, `authoritative_result_present`, `low_authority_only`, and each result's `match_authority` fields when they are present. Do not infer stronger capability than the payload states. 3. `--mode semantic` and `--mode hybrid` are now real implemented runtime modes. They are no longer compatibility stubs. Weak models still should prefer `auto` unless the task clearly requires a forced semantic/hybrid retrieval query. 4. If `--mode semantic` or `--mode hybrid` is requested on a host where the runtime reports `semantic_ready=false` or otherwise returns `degraded=true`, do not pretend semantic execution succeeded. Trust the returned `mode_used` and warnings: the request may honestly degrade to the strongest available local lexical path on this host. In that case, report the degraded lexical outcome as such and do not describe the result as semantic or hybrid retrieval. Precedence: if the payload also reports `index_fresh=false` (or `index_stale=true`) together with semantic unavailability, `D-WARN-COMBINED` applies and lexical fallback authority is revoked; otherwise apply `D-WARN-GENERIC` and present the degraded lexical result honestly. 5. For the lowest-friction safe path, weak models should think in this order: `health-check -> query(auto) -> read returned fields -> only then decide whether memorize or index is needed`. 6. `--mode learning` remains a learning-memory-oriented retrieval lane, but it now sits on top of the stronger v4 retrieval stack and still reports its true `mode_used` honestly. 7. Use `memorize.sh` only for reusable lessons that should influence future behavior. Positive test: memorize only if the lesson encodes a reusable decision rule, parameter set, or failure pattern that will predictably recur across ≥2 sessions or contexts. Do not log expected misses, one-off noise, duplicate lessons, or `checked, nothing relevant`. 8. Relation targets in `memorize.sh` are canonical, not freeform. Use only `learn:<signature>`, `chunk:<chunk_id>`, or `path:<canonical_path>` relation targets. 9. Do not execute internal helper scripts or reason about backend selection manually during normal use. The runtime owns retrieval choice; you consume the structured output. ### Canonical action routing Use this compact routing map first when choosing what to do next; the detailed gates and exit-code tables below remain authoritative for script-specific interpretation. | Trigger | First gate | Normal path | Maintenance / write path | Primary authority | |---|---|---|---|---| | Read/query request | `./health-check.sh` | If `OK` or degradable `WARN`, run `./query-memory.sh --mode auto --json` and trust returned fields | n/a | Health & Safety Gate + `query-memory.sh` payload | | No-results query outcome | `query-memory.sh` exit + payload | If exit `1` and `degraded=false`, report clean miss; if degraded, apply matching `D-WARN-*` row | n/a | `query-memory.sh` exit table + payload | | Durable lesson write | Pre-action gate | n/a | Run `./health-check.sh`, apply Health & Safety Gate, verify rollback, then run `./memorize.sh` only for reusable lessons | Pre-action gate + `memorize.sh` exit table | | Index / repair freshness work | Pre-action gate | n/a | Run `./health-check.sh`, apply Health & Safety Gate, verify rollback, then run `./index-memory.sh` | Pre-action gate + `index-memory.sh` exit table | | Any `FAIL` health state | `./health-check.sh` | Stop | Stop | `D-FAIL` | | Ambiguous / out-of-scope request | command mapping check | Stop and ask for one of the public commands or escalate to maintenance | Stop and ask for one of the public commands or escalate to maintenance | Deterministic fallback | ### Degraded-State Response Catalog (single source of truth) All mandatory user-visible degraded strings live here. Some rows are exact output strings; behavioral-rule rows are marked as such. Other sections reference this catalog by ID; do not restate inline elsewhere. | ID | Trigger (payload signals) | Mandatory exact output | |----|---------------------------|------------------------| | `D-WARN-GENERIC` | `status=WARN` and no more specific trigger below | `⚠️ MEMORY DEGRADED: <reason>. Results are partial.` | | `D-WARN-WRITE-ROLLBACK` | Any WARN continuation that performs writes or maintenance | Append: `⚠️ Continuing in degraded mode. Rollback path: <git/backup>.` | | `D-WARN-INDEX-STALE-ONLY` | WARN caused by stale lexical index, semantic still OK | `⚠️ Memory index may be stale. Results may miss recent changes. Consider running ./index-memory.sh.` | | `D-WARN-COMBINED` | `index_fresh=false` (or `index_stale=true`) **and** `semantic_ready=false` in the same WARN state | `⚠️ MEMORY DEGRADED: index stale, semantic unavailable. Results from query-memory fallback only. Missing recent changes and meaning-based matches. Run ./index-memory.sh and restore semantic prerequisites to resolve.` | | `D-LOW-AUTHORITY` | [REDACTED_SECRET] and `low_authority_only=true` | *(behavioral rule, not an exact emit string)* Treat returned matches as heuristic/fallback assistance only; do not present as confirmed memory truth. | | `D-FAIL` | `status=FAIL` | `❌ MEMORY UNAVAILABLE: health check FAIL.` Do not query, memorize, or edit memory files. | JSON-capable outputs: mandatory degraded notices must live inside structured warning fields rather than outside the payload. ### Health & Safety Gate For one-pass weak-model routing, prefer the **Weak-Model Decision Matrix** above first; this section remains the detailed degraded-state authority. See **Canonical action routing** above for the shortest precedence map; this section remains the detailed degraded-state authority. - **OK** → proceed normally. - **WARN** → emit the matching catalog row (`D-WARN-COMBINED` takes precedence over `D-WARN-INDEX-STALE-ONLY`, which takes precedence over `D-WARN-GENERIC`). For writes/maintenance also append `D-WARN-WRITE-ROLLBACK`. For read-only degraded queries, state the degradation and fallback scope without requiring a rollback path. - In the `D-WARN-COMBINED` state, lexical authority is revoked: do not present results as lexical truth or as `the best available answer`; do not invent manual retrieval steps outside the four public commands. - Under `D-WARN-INDEX-STALE-ONLY`, indexed results remain usable but freshness-limited; do not present them as fully current. Under semantic-only unavailability (lexical freshness OK), lexical/index-backed results remain authoritative for exact/path/time-style matches, but do not describe them as semantic or meaning-based retrieval. - `D-LOW-AUTHORITY` applies regardless of warn/ok surface whenever those fields are set. - Do not treat every `warnings[]` note as a degraded retrieval result. Informational notes may appear even when the current request was satisfied exactly as designed; rely on the script's `degraded` field and exit code, not on the mere presence of warning text. - Queue/backlog WARN states do not by themselves disable read/query authority, but they do require reporting that recent learnings or pending index work may not yet be reflected. Negative results under that WARN should be treated as potentially incomplete, not definitive absence. - **FAIL** → emit `D-FAIL`. ### Interpreting `query-memory.sh` exit codes After running `./query-memory.sh`, you MUST check the exit code and act accordingly: | Exit Code | Meaning | Permitted Action | |-----------|---------|------------------| | `0` | Results found. Inspect `degraded` to determine stack health. | If `degraded=false`: use results normally. If `degraded=true`: results were returned but the stack is degraded — apply the matching `D-WARN-*` catalog row via the Health & Safety Gate while still using the returned payload. | | `1` | No results found. Check `degraded` and `warnings[]` to tell whether this was a clean miss or a degraded no-results outcome. | If `degraded=false`, state: `No memory entries found for this query.` If `degraded=true`, state: `No memory entries found for this query (degraded retrieval; results may be incomplete).` Then apply the matching `D-WARN-*` row from the Degraded-State Response Catalog per the Health & Safety Gate precedence. Use returned `warnings[]` to surface any freshness-relevant notes. Do **not** treat exit code `1` as an automatic stack failure. | | `2` | Degraded but usable results returned. | Apply the matching `D-WARN-*` catalog row via the Health & Safety Gate; inspect `degraded`, `warnings[]`, `authoritative_result_present`, and `low_authority_only`. Present any returned results explicitly as degraded/partial results, not as a normal or fully authoritative success. Do **not** treat this as a clean success. | | `3` | Retrieval stack unavailable. | **STOP.** State: `❌ MEMORY UNAVAILABLE: Cannot search memory at all.` Do **not** continue as if this were a clean no-results case. Escalate to human maintenance. | | `4` | Bad arguments provided to script. | **STOP.** State the argument error and re-evaluate the command. | | `5` | Internal script error. | **STOP.** State: `❌ MEMORY INTERNAL ERROR.` Escalate to human maintenance. | **CRITICAL:** Do not confuse exit code `1` (clean miss) with exit code `3` (broken retrieval stack). The former is normal operation; the latter requires immediate escalation. ### Interpreting `memorize.sh` exit codes After running `./memorize.sh`, you MUST check the exit code and act accordingly: | Exit Code | Meaning | Permitted Action | |-----------|---------|------------------| | `0` | Learning was written successfully, or an exact duplicate was safely skipped. | State success honestly. Do **not** retry a duplicate write as if persistence failed. | | `4` | Bad arguments provided to script. | **STOP.** State the argument error and re-evaluate the command. | | `5` | Internal script error. | **STOP.** State: `❌ MEMORY INTERNAL ERROR.` Escalate to human maintenance. | If you need machine-readable confirmation, prefer `./memorize.sh --json ...` and inspect its `status` field (`written` vs `duplicate`) instead of inventing extra exit-code meanings. ### Interpreting `health-check.sh` exit codes After running `./health-check.sh`, you MUST check the exit code and act accordingly: **Precedence rule:** for normal degraded-mode routing, always execute `./health-check.sh --json` and decide `OK` vs `WARN` vs `FAIL` from the parsed JSON `status` field plus payload details. Treat the bash exit code only as a hard-stop gate: exit `3`, `4`, or `5` means immediate stop/escalate; other non-fatal paths still require JSON parsing before selecting a `D-WARN-*` row. | Exit Code | Meaning | Permitted Action | |-----------|---------|------------------| | `0` | Overall status is `OK`. | Proceed normally. | | `2` | Overall status is `WARN`. | Apply the matching `D-WARN-*` catalog row via the Health & Safety Gate. | | `3` | Overall status is `FAIL`. | Emit `D-FAIL` and STOP. Do not continue with memory edits or retrieval-dependent work. | | `4` | Bad arguments provided to script. | **STOP.** State the argument error and re-evaluate the command. | | `5` | Internal script error. | **STOP.** State: `❌ MEMORY INTERNAL ERROR.` Escalate to human maintenance. | If you need machine-readable status decisions, prefer `./health-check.sh --json` and inspect structured `status`, `warnings`, and `checks[]` fields rather than guessing from free text. ### Interpreting `index-memory.sh` exit codes After running `./index-memory.sh`, you MUST check the exit code and act accordingly: | Exit Code | Meaning | Permitted Action | |-----------|---------|------------------| | `0` | Requested indexing/maintenance action completed without warnings. | Continue normally. | | `2` | Action completed with warnings or expected degraded conditions. | Report the warning state explicitly. Treat indexing as usable-but-degraded until the warning cause is resolved. On lexical-only hosts, `--rebuild-vectors` returning `2` because semantic dependencies are unavailable is an expected degraded outcome, not an unexpected hard failure. | | `3` | Index maintenance failed at the storage/runtime layer. | **STOP.** State: `❌ MEMORY UNAVAILABLE: index maintenance failed.` Escalate to human maintenance. | | `4` | Bad arguments provided to script. | **STOP.** State the argument error and re-evaluate the command. | | `5` | Internal script error. | **STOP.** State: `❌ MEMORY INTERNAL ERROR.` Escalate to human maintenance. | If you need machine-readable details, prefer `./index-memory.sh --json` and inspect `actions[]`, `warnings[]`, and any mode-specific fields returned by the script. ### Pre-action gate **Applies when:** the next action is a script/policy/index edit that modifies files, OR a `./memorize.sh` call meant to durably record a high-value lesson. For the short routing form, see **Canonical action routing** above. Execute in order, do not skip: 1. Run `./health-check.sh` directly (not via `bash <script>`). 2. Apply the Health & Safety Gate result: - `OK` → continue to step 3. - `WARN` → emit the matching catalog row (plus `D-WARN-WRITE-ROLLBACK`) and continue to step 3 only if the rollback path is concrete. - `FAIL` → emit `D-FAIL` and STOP. 3. Verify rollback exists: `git status`, a backup directory, or untouched canonical files. If rollback is unclear, abort and escalate to human maintenance. 4. If live inspection contradicts a change-memory record marked `applied`, trust live inspection, abort the write/maintenance action, and treat the host as degraded until reconciled. 5. Do not ingest `references/maintenance.md` during normal operation; open it only for human-led maintenance or when this skill explicitly tells you to. ### Deterministic fallback - If a request does not clearly map to the four public commands or the explicit maintenance path, reply: `Out of scope for super-memori v4 local-only runtime. Please specify which command to run or escalate to human maintenance.` After running a public command, follow the exit-code interpretation rules above exactly. - If you are unsure whether information qualifies for `memorize.sh`, default to not memorizing. ## Maintenance Reference For retrieval pipeline contracts, write/learning contracts, promotion rules, maintenance entrypoints, release gates, and anti-patterns, see [`references/maintenance.md`](references/maintenance.md).

path: ARCHIVE.md
content: # Super Memory Skill (legacy archive note)

Этот файл — замороженная историческая архивная заметка о legacy/v2-этапе навыка. Он не является источником текущей runtime-истины и не должен использоваться для вывода о текущем release/status/runtime contract.
Актуальный контракт см. только в `SKILL.md` и актуальных release/reference surfaces.

Навык семантического поиска по памяти. Пройден 3 раунда Dual Thinking с DeepSeek.

**Дата создания:** 2026-04-05  
**Статус:** Архив — не установлен как активный навык  
**Причина:** Отложен пока память не вырастет  

## Для активации:
1. `pip install sentence-transformers`
2. Переместить в skills/ и добавить в AGENTS.md
3. Первый запуск: `bash index-daily.sh`
4. Cron: `0 2 * * * ~/.openclaw/skills/super_memori/index-daily.sh`

## Dual Thinking итог:
- Round 1: 5 слепых пятен найдено, overengineering остановлен
- Round 2: Архитектура подтверждена, Qdrant выбран
- Round 3: "Ready to ship after 2 fixes" — оба внесены


path: CHANGELOG.md
content: # Changelog

## 4.0.23 - 2026-04-27
- Added a real workspace internal hook `super-memori-session-start` that listens to OpenClaw's runtime `agent:bootstrap` event and runs the Super Memori startup self-check through a host-enforced path instead of relying only on startup instructions.
- Added a per-session ledger keyed by `sessionId` so the startup self-check runs once for each new session and does not re-run on ordinary continuation turns of the same session even if bootstrap context is rebuilt.
- Wired the hook through `hooks.internal.load.extraDirs` and `hooks.internal.entries.super-memori-session-start` in `/home/irtual/.openclaw/openclaw.json`.
- Injected a compact session-start report into bootstrap context for the first handled turn so the agent has machine-generated startup state instead of only an instruction contract.
- Advanced the active artifact line to unpublished `4.0.23` for truthful packaging/release surfaces after landing the enforceable per-session startup hardening line.

## 4.0.22 - 2026-04-27
- Added a startup self-check/self-heal surface at `./startup-self-check.sh` so OpenClaw startup flows can re-check health, perform a safe refresh/rebuild pass when the host is degraded, and confirm a real smoke query before trust.
- Enabled the official OpenClaw bundled `boot-md` hook via `openclaw hooks enable boot-md` and added workspace `BOOT.md` so gateway startup now triggers the Super Memori startup self-check and writes a machine-readable report to `memory/index-state/startup-self-check-last.json`.
- Hardened workspace startup guidance in `AGENTS.md` so new sessions explicitly re-run the same startup self-check before trusting memory freshness; this line does not claim a separately verified per-session OpenClaw hook beyond gateway startup + startup instructions.
- Extended packaging/release/test surfaces so `startup-self-check.sh` is now part of the package contract, release gate, and regression runner.
- Advanced the active artifact line to `4.0.22` instead of mutating the already-published `4.0.21` package in place, then published `4.0.22` to ClawHub after the final truthfulness pass.

## 4.0.21 - 2026-04-25
- Completed the user-requested 6-round alternating Dual Thinking rerun without subagents.
- Accepted a final weak-model clarity patch from the clean DeepSeek round-6 review: simplified the `references/release-status.md` stable-line interpretation so it now states what the active line means instead of embedding a long per-patch lineage sentence that duplicated `CHANGELOG.md`.
- Preserved the same release truth while reducing support-surface cognitive load for weak-model and human readers.
- Advanced release surfaces to `4.0.21` for the actual publish step, then published `4.0.21` to ClawHub after the completed 6-round alternating rerun.
- Runtime capability claims unchanged; this is a release-status clarity hardening patch.

## 4.0.20 - 2026-04-25
- Continued the same user-requested 6-round alternating Dual Thinking rerun from accepted `4.0.19` state without subagents.
- Repaired a weak-model validation regression introduced during the readability cleanup: the skill now exposes an explicit `## Execution` anchor above the executable OpenClaw host setup flow, preserving the validator-recognized execution surface without reviving the old empty phantom heading.
- Advanced release surfaces to unpublished `4.0.20` so the accepted post-`4.0.19` patch line remains publish-honest during the ongoing rerun.
- Runtime capability claims unchanged; this is a weak-model-validation / readability correction.

## 4.0.19 - 2026-04-25
- Continued the same user-requested 6-round alternating Dual Thinking rerun from accepted `4.0.18` state without subagents.
- Accepted a clean-failure release-gate hardening patch from the clean Qwen round-5 review: `validate-release.sh` now explicitly requires `references/release-status.md` before the Python sync block reads it, eliminating the last raw-traceback path for a mandatory release surface and preserving deterministic gate failures.
- Removed an empty `## Instructions` heading from `SKILL.md` so weak-model readers no longer encounter a phantom contract section with no content.
- Advanced release surfaces to unpublished `4.0.19` so the accepted post-`4.0.18` patch line remains publish-honest during the ongoing rerun.
- Runtime capability claims unchanged; this is a clean-failure / readability hardening patch.

## 4.0.18 - 2026-04-25
- Continued the same user-requested 6-round alternating Dual Thinking rerun from accepted `4.0.17` state without subagents.
- Accepted a contract-surface sync patch from the second DeepSeek challenge round: `SKILL.md` now documents all four maintenance-only entrypoints already required by the package contract (`audit-memory.sh`, `repair-memory.sh`, `list-promotion-candidates.sh`, `validate-release.sh`) and explicitly keeps them out of the normal weak-model command loop unless maintenance is requested.
- Closed the remaining contract-to-package mismatch where maintenance surfaces were present and release-gated but only partially described in the main skill contract.
- Advanced release surfaces to unpublished `4.0.18` so the accepted post-`4.0.17` patch line remains publish-honest during the ongoing rerun.
- Runtime capability claims unchanged; this is a contract-surface / weak-model-guardrail hardening patch.

## 4.0.17 - 2026-04-25
- Continued the same user-requested 6-round alternating Dual Thinking rerun from accepted `4.0.16` state without subagents.
- Accepted a current-date truth-hardening patch from the second Qwen challenge round: removed the stale version tag from the `RUNTIME CAPABILITY MATRIX` header in `SKILL.md`, eliminating an internal version-drift seam where weak models could encounter conflicting version labels inside the same live artifact.
- Hardened `validate-release.sh` against recurrence by rejecting stale version-tagged section headers in `SKILL.md` when they do not match `_meta.json`'s active version.
- Advanced release surfaces to unpublished `4.0.17` so the accepted post-`4.0.16` patch line remains publish-honest during the ongoing rerun.
- Runtime capability claims unchanged; this is a document-truth / release-gate hardening patch.

## 4.0.16 - 2026-04-25
- Continued the same user-requested 6-round alternating Dual Thinking rerun from accepted `4.0.15` state without subagents.
- Accepted a second release-gate completeness patch from the first DeepSeek challenge round: `validate-release.sh` now also enforces the maintenance-only package-root entrypoints (`audit-memory.sh`, `repair-memory.sh`, `list-promotion-candidates.sh`, `validate-release.sh`) and the publish-evidence files (`references/verification-evidence.md`, `references/reference-test-log.md`) already declared mandatory by `PACKAGING_CHECKLIST.md`.
- Closed the remaining checklist-vs-gate mismatch of the same class as round 1, so the executable release gate now covers the declared public entrypoints, maintenance entrypoints, support evidence files, package-root metadata files, regression runner, `scripts/`, and `references/`.
- Advanced release surfaces to unpublished `4.0.16` so the accepted post-`4.0.15` patch line remains publish-honest during the ongoing rerun.
- Runtime capability claims unchanged; this is a further release-gate / packaging-honesty hardening patch.

## 4.0.15 - 2026-04-25
- Continued the user-requested 6-round alternating Dual Thinking rerun from the already-published `4.0.14` baseline without subagents.
- Accepted a release-gate completeness patch from the first Qwen round: `validate-release.sh` now enforces the package-root artifacts already declared mandatory by `PACKAGING_CHECKLIST.md` (`SKILL.md`, `_meta.json`, `.clawhubignore`, `CHANGELOG.md`, `PACKAGING_CHECKLIST.md`, and the four public entrypoints), requires `scripts/` and `references/`, and blocks if `tests/regression/run-all.sh` is missing or not executable.
- Narrowed the publish-readiness seam between the declared checklist and the executable release gate by actually running `tests/regression/run-all.sh` inside `validate-release.sh`, so future regression additions cannot silently drift past the release gate.
- Fixed the first validation rollback from real evidence: the gate now requires `tests/regression/run-all.sh` to exist and invokes it via `bash` instead of demanding an executable bit, so the release gate matches the repository's real file mode rather than inventing a stronger packaging requirement.
- Advanced `_meta.json` and release surfaces to unpublished `4.0.15` so the post-`4.0.14` patch line does not falsely read as already published before the current rerun completes.
- Runtime capability claims unchanged; this is a release-gate / packaging-honesty hardening patch on top of published `4.0.14`.

## 4.0.14 - 2026-04-25
- Started a fresh user-requested 6-round alternating Dual Thinking rerun without subagents, using Qwen and DeepSeek as the consultant line.
- Accepted the first material hardening patch from that rerun: `validate-release.sh` now enforces release-surface sync against the canonical `_meta.json` version, blocking publish/readiness drift when `SKILL.md`, `CHANGELOG.md`, `references/release-status.md`, `references/verification-evidence.md`, or `references/reference-test-log.md` fall out of sync.
- Accepted a second narrow hardening patch from the rerun: `_meta.json.publishedAt` validation now permits only `null` (unpublished) or a positive integer (real publish timestamp), closing the old ambiguous-sentinel gap where `0` or other non-positive integers could slip through the release gate.
- Accepted a third hardening patch from the rerun: `validate-release.sh` no longer hardcodes the current machine path for `skill_root`; it now validates the current working tree, restoring honest portability for CI, alternate workspaces, and cross-host publish prep inside the intended workspace layout.
- Accepted a fourth narrow truth patch from the rerun: the release surfaces now explicitly document that `validate-release.sh --strict` is a workspace-coupled gate relying on sibling validator tooling under `../skill-creator-canonical/`, rather than pretending it is an isolated package-root check.
- Reset `_meta.json.publishedAt` to the unpublished sentinel for the new `4.0.14` line so the workspace would not falsely imply that this post-`4.0.13` artifact was already published before the real release.
- Published `4.0.14` to ClawHub ([REDACTED_SECRET]) and synced local metadata/support surfaces to the real publish timestamp `2026-04-25T13:25:24Z` (`publishedAt=1777123524`).
- Runtime capability claims unchanged; this is a publish-honesty / validation-discipline hardening patch on top of `4.0.13`.

## 4.0.13 - 2026-04-25
- Continued the required Dual Thinking rerun from the accepted `4.0.12` state and synchronized the support/package truth surfaces for honest publish readiness.
- Updated the release-support references so they now describe the active `4.0.13` line instead of the older `4.0.10` line.
- Marked the intermediate `4.0.11` changelog block as unpublished work carried forward, so the changelog no longer reads like `4.0.11` shipped independently.
- Published `4.0.13` to ClawHub (`k971cr1q8rss1bxr71cp7hjncd85g7t8`) and synced local metadata to the actual publish timestamp.
- Runtime capability claims unchanged; this is a release-surface / evidence-surface truth-sync patch.

## 4.0.12 - 2026-04-25
- Continued the required Dual Thinking rerun from the accepted `4.0.11` state and landed one more narrow publish-honesty clarification rather than a structural rewrite.
- Added an explicit host-state qualification to `Host-state guidance`: a `WARN` host is still within the qualified `v4.0.12` baseline and does not by itself mean artifact instability, setup failure, or a publication blocker; it simply activates the degraded-mode routing contract.
- Advanced the active line to `4.0.12` because this accepted wording patch materially changed the published-facing contract surface.

## 4.0.11 - 2026-04-25 (unpublished — changes carried forward into `4.0.12` and later `4.0.13`)
- Re-ran Dual Thinking against the stable `4.0.10` baseline and accepted a weak-model/current-date hardening patch before the next consultant handoff.
- Added a one-pass **Weak-Model Decision Matrix** immediately after the OpenClaw quickstart so weak models can resolve `health-check` state, query exit codes, degraded routing, and write/index gating without multi-hop cross-referencing.
- Added a **Design Boundaries / Intentional Exclusions** section that explicitly justifies the four-command public surface and names the modern local-memory patterns this artifact intentionally rejects for safety and operator clarity: background auto-indexing as a public requirement, LLM-driven auto-consolidation into durable memory, and cloud/remote vector dependencies.
- Fixed post-bump truth surfaces for the new unpublished `4.0.11` line: corrected stale in-file version labels, added a direct precedence note telling weak models to read the decision matrix first, and reset `_meta.json.publishedAt` to the unpublished sentinel instead of falsely implying that `4.0.11` was already live.
- Runtime capability claims unchanged; this is a contract-sharpening / current-date-positioning patch release.

## 4.0.10 - 2026-04-25
- Re-ran Dual Thinking from a fresh baseline after tightening `dual-thinking` itself against continuity-summary substitution in fresh/recovery consultant sessions.
- Hardened weak-model degraded-state routing in `SKILL.md`: the OpenClaw quickstart now treats `health-check.sh --json` as the primary degraded-state authority and uses bash exit codes only as hard-stop gates.
- Clarified `query-memory.sh` exit handling so `exit 1` + `degraded=true` must be reported as a degraded miss, and `exit 2` results must be presented explicitly as degraded/partial rather than normal success.
- Tightened the pre-action gate so live inspection wins over any conflicting `applied` change-memory record before writes or maintenance continue.

## 4.0.9 - 2026-04-24
- Published `4.0.8`, then immediately detected a final truth-sync seam: the package/support surfaces still described the line as unpublished and still named `4.0.7` as latest published, which became false the moment the real ClawHub publish completed.
- Advanced to `4.0.9` instead of mutating the already-published `4.0.8` package in place.
- Updated `SKILL.md`, `_meta.json`, and release/support surfaces so the current published line is now described honestly after the real publish.
- Runtime capability claims unchanged; this is a post-publish truth-sync release only.

## 4.0.8 - 2026-04-24
- Re-ran a fresh 6-round alternating Dual Thinking pass requested by the user (Qwen rounds 1/3/5, DeepSeek rounds 2/4/6) starting from the published `4.0.7` baseline and preserving per-orchestrator session continuity.
- Fixed a real packaging-honesty regression before republish: restored the missing package-root `.clawhubignore`, reset `_meta.json.publishedAt` for the new unpublished line, and advanced the artifact line to `4.0.8` instead of mutating the already-published `4.0.7` package.
- Tightened the release path so the next publish is honest-by-construction: strict validation now matches the actual tree again, and package metadata no longer implies that the new line was already published.
- Added a compact **Canonical action routing** table to `SKILL.md` so weak models can resolve the normal query path, no-results handling, durable writes, index/repair work, and hard-stop `FAIL` cases from one summary surface before falling through to the detailed gates and exit-code tables.
- Runtime capability claims unchanged so far in this line; current work is release-surface hardening plus accepted weak-model/operator clarifications from the active 6-round review.

## 4.0.7 - 2026-04-22
- Second 6-round alternating Dual Thinking review (Qwen rounds 1/3/5, DeepSeek rounds 2/4/6) executed directly by the main session without subagents. Qwen required one lawful recovery session mid-series when its persistent chat continuity degraded (`chatUrl: null`, hung follow-up); recovery repasted the latest accepted artifact before continuing.
- Introduced a single **Degraded-State Response Catalog** (`D-WARN-GENERIC`, `D-WARN-WRITE-ROLLBACK`, `D-WARN-INDEX-STALE-ONLY`, `D-WARN-COMBINED`, `D-LOW-AUTHORITY`, `D-FAIL`) as the sole source of mandatory user-visible degraded strings and behavioral rules; the Health & Safety Gate, exit-code tables, and OpenClaw quickstart now cross-reference this catalog by ID instead of restating strings inline, removing the previous drift risk between Health & Safety Gate and the exit-code-1 combined-degraded notice.
- Reworked the file-edit / memorize pre-action gate from dense prose into an explicit numbered procedure with trigger scope, Health & Safety Gate routing per catalog ID, concrete rollback verification step, and mandatory STOP on `FAIL`.
- Flattened Operating Rule 4's nested `Exception:` clause into an explicit precedence chain that defers directly to `D-WARN-COMBINED` vs `D-WARN-GENERIC` instead of re-describing catalog logic.
- Added a positive memorization test to Operating Rule 7: memorize only when the lesson encodes a reusable decision rule, parameter set, or failure pattern predictably recurring across ≥2 sessions or contexts.
- Fixed a real exit-code / degraded-flag contradiction flagged by DeepSeek: exit code `0` now explicitly handles `degraded=true` by routing to the matching `D-WARN-*` catalog row via the Health & Safety Gate rather than claiming "stack healthy" unconditionally.
- Consolidated the top-of-document status cluster (previously `Current truth snapshot` + `Current host limitations` + `Current blocker classification`) into one `Current Release & Host State` block with `Artifact invariants` vs `Host-state guidance` subsections, and dropped the hard-coded stable-host snapshot claim so the document no longer goes stale when a later release uses a different validation host.
- Consolidated the four change-memory paragraphs (`Change-memory authority boundary`, `Change-memory truth`, `Minimal hot-change-buffer`, `Change-memory noise policy`) into two sections (`Change-Memory Authority & Scope` + `Change-Memory Noise Policy`).
- Added an explicit decision rule to Truth Precedence: live filesystem / service / package inspection is mandatory before writes, config changes, service restarts, or any action depending on exact current machine state, and whenever `./health-check.sh` returns `WARN` or `FAIL`; memory-tool outputs alone are sufficient only for read-only queries at `status=OK` with `degraded=false`.
- Updated the OpenClaw quickstart steps 3-4 to emit catalog IDs (`D-FAIL` / `D-WARN-*`) for parity with the rest of the document.
- Runtime capability claims unchanged: this is a documentation / weak-model contract hardening release on top of `4.0.6`.

## 4.0.6 - 2026-04-22
- Completed the required 6-round alternating Dual Thinking review using Qwen (rounds 1, 3, 5 with one lawful recovery session after Qwen continuity degraded) and DeepSeek (rounds 2, 4, 6), then converged honestly without inventing a larger runtime rewrite.
- Rejected an unnecessary runtime-schema expansion for recency-gap reporting after confirming the existing payload already exposes the freshness/authority fields needed for this line.
- Hardened weak-model/operator wording in `SKILL.md`: queue/backlog WARN negative results are now explicitly potentially incomplete; the exit-code-1 combined degraded notice is self-contained; the quickstart now defers to one authoritative trusted-field definition; Operating Rule 2 now explicitly includes per-result `match_authority`; and the pre-edit maintenance rule now points directly to `references/maintenance.md`.
- Trimmed the live `Current blocker classification` section down to active-state truth instead of release-engineering residue.
- Hardened active support surfaces so they no longer carry stale or future-stale release wording: `validate-equipped-host.sh` and `scripts/release-prep.sh` now describe the current active line generically.
- This release does not change runtime capability claims; it is a documentation/support-surface hardening follow-up to `4.0.5`.

## 4.0.5 - 2026-04-22
- Advanced the active artifact line from `4.0.4` to `4.0.5` because the published `4.0.4` package was missing the declared `.clawhubignore` surface even though the packaging checklist required it.
- Restored `.clawhubignore` to the package root and excluded transient/local state deterministically (`.clawhub/`, `__pycache__/`, `*.pyc`, `*.tmp`, `*.pending`, `*.lock`, `.index_state/`, `backups/`, full `reports/`, `reports/test-generated/`, and `tmp_round2_strict.txt`).
- Kept runtime capability claims unchanged: this is a packaging-hygiene republish fix, not a new semantic/runtime feature line.

## 4.0.4 - 2026-04-21
- Advanced the active artifact line from `4.0.3` to `4.0.4` because the final safe latency fix and release validation work happened after `4.0.3`, and publishing over an already-established stable line would be dishonest.
- Fixed the real semantic latency seam by keeping embeddings behind a local semantic daemon instead of cold-loading the model in each short-lived query subprocess, while confirming the daemon stays idle-safe (`/health` reported `idle_cpu_percent_of_total=0.0` during the release check).
- Confirmed warm-path query latency on the current host after the fix: `exact≈0.13s`, `semantic≈0.21s`, `hybrid≈0.22s`, `auto≈0.22s` on the final current-workspace hardening run, with the separate direct timing probe also showing semantic warm calls dropping from cold-start seconds to sub-second daemon-backed execution.
- Re-ran the hardening benchmark on the current workspace (`benchmarks/locomo/results/20260421T093801.239655Z`) and kept accuracy green: `answer_hit_at_1=1.0` / `answer_hit_at_5=1.0` for `auto`, `exact`, `semantic`, and `hybrid`, with `avg_ms` around `212–219 ms` for the semantic/hybrid/auto modes after the safe warm-path fix.
- Fixed one release-test flake in `tests/test_semantic_daemon_surface.py`: the daemon surface gate now asserts stable fast daemon-backed embedding behavior instead of the brittle strict inequality `t2 < t1`, which could fail spuriously when both calls land in the same timing bucket.
- Rebuilt lexical/vector state on the current workspace, cleared transient orphan-vector drift caused by overlapping maintenance/release passes, and finished with clean gates: `./index-memory.sh --full --rebuild-vectors --json` exit `0`, `./validate-release.sh --strict` exit `0`, `audit-memory.sh --json status=ok vector_state=ok`, and benchmark post-index health `OK`.

## 4.0.3 - 2026-04-21
- Advanced the active artifact line from `4.0.2` to `4.0.3` because ClawHub truth inspection showed that `4.0.2` already exists and this workspace now contains additional real changes beyond the earlier release-surface patch.
- Hardened the LoCoMo benchmark harness so staged benchmark conversations are written into canonical `memory/episodic/benchmark-locomo`, verified against the lexical index with a new stage-coverage gate, and documented accordingly.
- Added an expanded hardening benchmark dataset plus stage-coverage regression so benchmark failures now distinguish real retrieval weakness from corpus-staging mistakes.
- Hardened temporal / semantic rerank by passing the live query text into `temporal_relational_rerank()` and applying a small query-anchor bonus plus bootstrap-path penalty, improving top-1 ranking on the new hardening benchmark set without weakening lexical authority rules.
- Re-ran strict local release validation, full local regression tests, smoke benchmark, and expanded benchmark before publishing this new stable patch line.

## 4.0.2 - 2026-04-21
- Archived stale candidate-era maintenance and promotion references so the packaged artifact no longer mixes the active stable `4.0.1` line with pre-stable `4.0.0-candidate.23` guidance.
- Advanced the active artifact line to `4.0.2` instead of mutating the already-published `4.0.1` package in place after this post-publish documentation/release-surface fix.
- Reset local `_meta.json.publishedAt` to the unpublished sentinel before republish so package metadata does not imply that `4.0.2` was already live before the next real ClawHub publish.
- Added inherited-evidence bridge notes so `references/reference-test-log.md` and `references/verification-evidence.md` now identify the active packaged line as `4.0.2` while preserving the real equipped-host validation body captured for stable `4.0.1`.

## 4.0.1 - 2026-04-20
- Completed the equipped-host stable-readiness sequence with a clean `./validate-equipped-host.sh` exit and final stable verdict: `Host is fully equipped and v4.0.0 hybrid mode is operational.`
- Closed the last stable-gate validator seams instead of weakening the gate: tolerated only the exact transient step-1 `stale-vectors` rebuild path, read semantic freshness from the real `health-check.sh --json` schema, and hardened eval/release surfaces so heavy hybrid checks time out honestly instead of hanging the release path.
- Reconfirmed the stable host truth end-to-end: semantic retrieval active, vector state healthy, `health-check.sh --json` status `OK`, `audit-memory.sh --json` status `ok` with `vector_state=ok`, `skill_operational_memory.status=ok`, and `agent_change_memory.status=ok`.
- Intended stable promotion target was `4.0.0`, but ClawHub truth inspection showed that version was already occupied by an older historical stable artifact from 2026-04-09; published this validated stable state as `4.0.1` instead to preserve registry honesty.

## 4.0.0-candidate.27 - 2026-04-20
- Ran one more post-publish truth-sync pass and found a real metadata seam: local `_meta.json` still carried the older `candidate.25` `publishedAt` value even though ClawHub already reported `Latest: 4.0.0-candidate.26` and `Updated: 2026-04-20T14:14:23.919Z`.
- Synchronized `_meta.json` publish metadata to the real latest published state instead of leaving stale publish-truth in the package root.
- Advanced the active artifact line to `4.0.0-candidate.27` rather than silently mutating the already-published `candidate.26` line.
- Reconfirmed current host runtime cleanliness after the sync pass: health `ok`, audit `ok`, `skill_operational_memory.status=ok`, and `agent_change_memory.status=ok`.

## 4.0.0-candidate.26 - 2026-04-20
- Ran an additional hardening pass focused on the last real operational WARN tails rather than release-label polish.
- Fixed routine maintenance self-noise in `index-memory.sh` and `scripts/agent_change_memory.py`: successful low-risk reindex maintenance is now truthfully reconciled into one verified current durable change instead of accumulating endless `applied_but_unverified` duplicates.
- Added a dedicated reconciliation helper plus regression coverage so matched hot-buffer events are settled to verified/applied state and do not remain falsely interrupted or unverified after a successful maintenance pass.
- Refreshed live maintenance state after the patch; current host health now reports `skill_operational_memory.status=ok` and `agent_change_memory.status=ok` with `records=1`, `duplicates=[]`, `unverified=[]`, and `hot_interrupted=[]`.
- Re-ran targeted tests, strict release validation, regression suite, and an external DeepSeek sanity-check on the post-fix artifact; no new publish-blocking seam remained.
- Advanced the active artifact line to `4.0.0-candidate.26` for this validated post-publish hardening step instead of silently folding the fix into the already-published `candidate.25` tag.

## 4.0.0-candidate.25 - 2026-04-20
- Ran a fresh 6-round alternating Dual Thinking pass through ai-orchestrator (DeepSeek) and qwen-orchestrator (Qwen) starting from the already-published `4.0.0-candidate.24` baseline.
- Fixed a false semantic-health WARN in `health-check.sh`: the warning path now falls back from Qdrant `indexed_vectors_count` to `points_count` before claiming that no indexed vectors are present.
- Re-synchronized active support surfaces so current-line documentation no longer states that the current validation host is degraded/unbuilt when live validation shows semantic-ready execution; the earlier degraded claims are now explicitly historical snapshots.
- Advanced the active artifact line to `4.0.0-candidate.25` across `_meta.json`, `SKILL.md`, `references/release-status.md`, `references/verification-evidence.md`, and `references/reference-test-log.md` instead of silently reusing the already-published `candidate.24` tag for this post-publish fix.
- Candidate publication remains honest and host-truth-bound: stable `4.0.0` is still blocked pending the equipped-host readiness sequence, while current host state must still be taken from live health/audit output at use time.

## 4.0.0-candidate.24 - 2026-04-20
- Fixed `canonical_memory_files()` so ordinary daily notes in `memory/YYYY-MM-DD.md` are indexed as episodic memory instead of being silently skipped.
- Excluded non-canonical operational memory under `memory/semantic/skill-memory`, `memory/semantic/system-hygiene`, and `memory/semantic/agent-change-memory` from canonical indexing so maintenance refreshes do not make the canonical freshness checks immediately self-stale.
- Added the missing `.clawhubignore` file and excluded local review/runtime artifacts, bytecode caches, full `reports/`, and temporary validation leftovers from the published package.
- This release is still a candidate line; publish honesty remains host-scoped and live `health-check.sh --json` output remains the source of truth for freshness and semantic readiness.

## 4.0.0-candidate.23 - 2026-04-14
- Fresh 10-round alternating Dual Thinking rerun started from the already-published `4.0.0-candidate.21` baseline and accepted a real package-hygiene fix before continuing the confirmatory rounds.
- Tightened `.clawhubignore` so internal `reports/` review artifacts and the root temporary validator leftover `tmp_round2_strict.txt` no longer ship in the distributable ClawHub package payload.
- Synced the active artifact line to `4.0.0-candidate.23` across `_meta.json`, `SKILL.md`, `references/release-status.md`, `references/verification-evidence.md`, and `references/reference-test-log.md` instead of silently reusing the already-published `candidate.21` tag for this post-publish packaging fix.
- Updated `PACKAGING_CHECKLIST.md` so the exclusion contract explicitly covers full `reports/` review artifacts and root temporary validation leftovers.
- Strict local release validation still passes after the accepted rerun patch.

## 4.0.0-candidate.21 - 2026-04-14
- Fresh 10-round alternating Dual Thinking rerun started from the already-published `4.0.0-candidate.20` baseline and accepted a narrow support-surface truth fix before continuing the confirmatory rounds.
- Tightened `references/verification-evidence.md` so the current-host lexical-freshness line no longer hard-pins a stale `2026-04-12T14:24:01+0800` timestamp as if it were the active evidential anchor for the current line; it now points explicitly to the live `./health-check.sh --json` snapshot for the candidate line while preserving the true degraded state (`lexical_freshness.ok=false`).
- Advanced the active artifact line to `4.0.0-candidate.21` instead of silently reusing the already-published `candidate.20` tag for this post-publish support-surface fix.
- Synchronized current-line identity across `_meta.json`, `SKILL.md`, `references/release-status.md`, `references/verification-evidence.md`, and `references/reference-test-log.md` to `candidate.21` while leaving `.clawhub/origin.json` untouched as real prior-publication metadata for `candidate.20` until the next actual publish completes.
- Strict local release validation still passes after the accepted rerun patch.

## 4.0.0-candidate.20 - 2026-04-14
- Fresh 10-round alternating Dual Thinking rerun started from the already-published `4.0.0-candidate.19` baseline and accepted two additional publish-honesty fixes before republish.
- Fixed a live host-evidence drift in `references/verification-evidence.md`: the current host-observed block no longer falsely says `lexical freshness: working after refresh`; it now reports the real current host state that the lexical stack is operational but freshness is stale (`lexical_freshness.ok=false`, last indexed `2026-04-12T14:24:01+0800`).
- Corrected release-history semantics after that post-publish fix by advancing the active artifact line from `4.0.0-candidate.19` to `4.0.0-candidate.20` instead of silently reusing the already-published candidate tag.
- Synchronized current-line identity across `_meta.json`, `SKILL.md`, `references/release-status.md`, `references/verification-evidence.md`, and `references/reference-test-log.md` to the new `candidate.20` line while leaving `.clawhub/origin.json` untouched as real prior-publication metadata for `candidate.19`.
- Strict local release validation still passes after the accepted rerun patches.

## 4.0.0-candidate.19 - 2026-04-14
- Ran another fresh alternating Dual Thinking rerun with explicit current-date internet research pressure through ai-orchestrator (DeepSeek) and qwen-orchestrator (Qwen).
- Accepted one new runtime contract hardening from rounds 1-2:
  - added per-result `match_authority` plus top-level `authoritative_result_present` / `low_authority_only` signals so weak models can distinguish confirmed exact/hybrid memory from heuristic semantic/fallback matches without suppressing valid lexical truth
- Rejected a naive numeric confidence-floor proposal because this runtime's RRF/fusion scores are not globally calibrated and a hard cutoff would break the existing lexical-authority contract for exact/path matches.
- Added targeted regression coverage for the new authority surface and synced public command/skill contract wording to the new payload fields.
- Later rerun rounds 7-8 found one more real publish seam: active contract/reference surfaces still mixed `candidate.18` and `candidate.19` identity after the version bump. Fixed that release-truth drift by syncing `SKILL.md`, `release-status.md`, `verification-evidence.md`, and `reference-test-log.md` to `candidate.19` while preserving historical bridge notes and leaving `.clawhub/origin.json` untouched as real published `candidate.18` metadata.
- Removed one harmless duplicate `retrieval_stack_unavailable` assignment from `query-memory.sh` during the same sync pass.
- Candidate line remains `4.0.0-candidate.19` pending final convergence proof and publish.

## 4.0.0-candidate.18 - 2026-04-14
- Ran a brand-new full 10-round alternating Dual Thinking rerun from the published `4.0.0-candidate.17` baseline using ai-orchestrator (DeepSeek) and qwen-orchestrator (Qwen), with exact Qwen daemon-restart recovery applied when round 4 and round 6 hit `exit 2` / navigation-timeout continuity failures.
- Accepted two real runtime/test-alignment fixes from this rerun:
  - `audit_memory_integrity()` now classifies the zero-chunk / zero-vector degraded host state as `semantic-unbuilt` instead of `ok`, bringing runtime output back into line with the documented degraded-host contract and the strict semantic-unbuilt test
  - `build_hot_recovery_bundle()` now emits an explicit truth note that the hot buffer is not canonical truth and that direct live inspection plus durable change-memory remain stronger for exact current machine state, bringing runtime output back into line with the hot-buffer authority test and public contract
- After those two fixes, all remaining rounds were confirmatory only: DeepSeek rounds 3/5/7/9 and Qwen rounds 2/4/6/8/10 found no new material seam that justified another patch.
- Fresh strict validation now passes end-to-end on the post-fix baseline, and current health remains publish-compatible `WARN` for the already-documented host-scoped degraded reasons (`semantic-unbuilt`, stale lexical freshness) rather than any forbidden critical failure surface.
- Advanced the candidate line to `4.0.0-candidate.18` for this rerun-backed release.
- No public command expansion, no stable-release claim, no capability overstatement.

## 4.0.0-candidate.17 - 2026-04-14
- Ran another fresh 10-round alternating Dual Thinking rerun using ai-orchestrator (DeepSeek) and qwen-orchestrator (Qwen), including one lawful Qwen recovery chat after a polluted session repeated an already-fixed stale-version finding.
- Accepted four additional narrow, validated fixes during this rerun:
  - updated `ARCHIVE.md` from a stale `candidate.10` pointer to a frozen-history-only note that now defers generically to the current contract in `SKILL.md` and live release/reference surfaces
  - synchronized stale support-surface self-identification so `release-status.md`, `verification-evidence.md`, and `reference-test-log.md` now align to the live `candidate.17` line, while preserving a single explicit bridge note that the underlying evidence was captured at `candidate.12`
  - added an internal frozen-reference restatement inside the maintenance `Execution Notes` subsection so weak models cannot skim into the `<details>` block and misread it as active operating contract
  - removed the redundant secondary freshness-caution append path from the `query-memory.sh` exit-code `1` override when the exact combined degraded-state notice already governs, while still surfacing freshness notes from returned `warnings[]` outside that override
- DeepSeek confirmatory rounds 5, 7, and 9 found no new material seam after those accepted fixes.
- Qwen round 6 and round 10 both converged on confirmatory acceptance with no new material fix required; round 8 also found no new materially justified seam.
- Advanced the candidate line to `4.0.0-candidate.17` after the rerun-specific accepted fixes and fresh validation passed.
- No public command expansion, no stable-release claim, no capability overstatement.

## 4.0.0-candidate.16 - 2026-04-14
- Ran a fresh 10-round alternating Dual Thinking rerun with ai-orchestrator (DeepSeek) and qwen-orchestrator (Qwen), including honest recovery handling for Qwen daemon/session continuity failures and polluted repeat-finding chats.
- Accepted three real micro-fixes from this rerun:
  - synchronized stale lower-section `candidate.12` references so the live artifact no longer contradicts its own `candidate.15`/`candidate.16` release identity inside maintenance/reference sections
  - added explicit frozen-reference scoping for the maintenance `<details>` block and neutralized the highest-risk present-tense maintenance claims so weak models cannot misread them as live runtime truth
  - clarified in OpenClaw host setup that `qdrant-client` does not install or start the Qdrant database service, preventing weak operators from treating `pip install` as full semantic-stack readiness
- Rejected Qwen's later proposal to hard-restrict weak models to `--mode auto` only, because that would under-document real implemented semantic/hybrid capability and create a release-truth seam.
- Confirmatory DeepSeek/Qwen rounds converged with no further accepted material seam; remaining `Host profiles` weakness was judged optional fluff, not release-blocking.
- Advanced the candidate line to `4.0.0-candidate.16` after validation passed on the accepted artifact.
- No public command expansion, no stable-release claim, no capability under-reporting for convenience.

## 4.0.0-candidate.15 - 2026-04-13
- Fixed a real weak-model operator-risk seam in rule #4: forced semantic/hybrid fallback wording now explicitly yields to the combined degraded-state lexical-authority revocation rule when `index_fresh=false` (or `index_stale=true`) and semantic is unavailable together.
- Advanced the candidate line to `4.0.0-candidate.15` after this narrow contract fix.
- No public command expansion, no stable-release claim, no policy-gate overfitting.

## 4.0.0-candidate.13 - 2026-04-13
- Tightened release-truth wording so the packaged release line is explicitly separated from live host freshness, semantic readiness, and degraded authority state at use time.
- Reframed current-host degraded/stale wording as validation-host evidence instead of artifact-wide truth, reducing operator risk that a published version label is misread as a claim about every installed host.
- Tightened `validate-release.sh` so non-FAIL WARN publication is no longer an undifferentiated pass: release gating now explicitly blocks critical WARN surfaces such as unreadable canonical files or unavailable lexical FTS even if overall health is not `FAIL`.
- Synced `_meta.json` publish policy to the stronger host-scoped WARN interpretation and advanced the release line to `4.0.0-candidate.13`.
- No public command expansion, no stable-release claim, no forced freshness-window policy invented without stronger evidence.

## 4.0.0-candidate.12 - 2026-04-12
- Ran a fresh full 3-cycle / 18-round Dual Thinking rerun from the published `4.0.0-candidate.11` baseline using alternating AI Orchestrator and Qwen Orchestrator, including honest recovery handling for polluted Qwen sessions that repeated already-landed findings.
- Accepted three real micro-fixes from the new rerun:
  - in the combined stale-index + semantic-unavailable WARN state, degraded matches are now explicitly non-authoritative and lexical authority is revoked
  - removed the misleading `zero-findings` token from current-host `system_hygiene` wording so weak models cannot misread stale partial visibility as clean health
  - tightened the `query-memory.sh` exit code `1` no-results row so the combined stale-index + semantic-unavailable WARN state must reuse the exact degraded notice from the Health & Safety Gate rather than a weaker generic template
- Remaining Qwen suggestions about extra warning duplication or verbatim symmetry in other rows were reviewed and rejected as optional documentation polish, not material contract seams.
- Confirmatory rounds 10-18 produced clean convergence proof with no further accepted fixes.
- No public command expansion, no stable-release claim, no architecture rewrite.

## 4.0.0-candidate.11 - 2026-04-12
- Added an inline `OpenClaw quickstart for weak models` directly to `SKILL.md` so weak OpenClaw operators have a shortest safe path without cross-reading multiple files.
- Added an inline `OpenClaw host setup (weak-model executable)` section to `SKILL.md` with only real commands (`cd`, `python3 --version`, `pip3 install --user sentence-transformers numpy qdrant-client`, `./index-memory.sh --full --json`, `./health-check.sh --json`, `./query-memory.sh ...`).
- Added an explicit lowest-friction weak-model decision order: `health-check -> query(auto) -> read returned fields -> only then decide whether memorize or index is needed`.
- Retained the previously accepted weak-model honesty hardening: forced `semantic` / `hybrid` retrieval on unequipped hosts must be described through returned `mode_used`, `degraded`, and `semantic_ready` fields rather than overstated as semantic success.
- Retained the clarified hot-change-buffer durability wording (`RAM-resident`, `circular-buffer`, `non-durable`) and the synced WARN publish-qualification terminology.
- Final convergence result for the extended review scope: repeated DeepSeek/Qwen confirmatory rounds found no new material blocker after these weak-model/OpenClaw improvements.
- No runtime logic changes, no public command expansion, no stable-release claim.

## 4.0.0-candidate.10 - 2026-04-12
- Applied the accepted narrow SKILL.md wording cleanup for the hot-buffer truth line.
- Changed `very recent recovery-only recent changes` to `very recent recovery-only agent-made changes`.
- Narrowed the weak-model forced-mode contract so requested `semantic` / `hybrid` retrieval on an unequipped host must be reported via returned `mode_used`, `degraded`, and `semantic_ready` fields rather than being over-described as semantic success.
- Tightened the hot-change-buffer wording from `RAM-first, rotational` to `RAM-resident, circular-buffer, ... non-durable` to remove false durability implications.
- Synced WARN publish qualification terminology to the actual observed health-check shape (`lexical_freshness: ok=false`, `semantic_dependencies: ok=false`) instead of the older `: fail` shorthand.
- No runtime logic changes, no public command changes, no hot-buffer scope expansion.

## 4.0.0-candidate.9 - 2026-04-12
- Added a minimal safe-first Phase 1 internal hot-change-buffer for very recent agent-made changes.
- Buffer is RAM-first, rotational, recovery-only, non-canonical, aggressively noise-filtered, and bounded to a 32 MiB default target (64/128 MiB optional, 128 MiB max in this phase).
- Added only the minimal internal functions required for Phase 1: recent hot-event record/update/query, interrupted-sequence detection, recovery bundle assembly, compaction, and health reporting.
- Integrated the hot buffer into write/change logging, recent-change query routing, health reporting, minimal audit reporting, and maintenance compaction without adding any new public command.
- Added targeted Phase 1 eval coverage and passed strict release validation.

## 4.0.0-candidate.8 - 2026-04-12
- Completed the originally requested full review scope: 3 cycles total with 18 rounds of alternating Dual Thinking pressure-testing using AI Orchestrator and Qwen Orchestrator.
- Cycle 2 found and landed the last real post-`candidate.7` micro-fixes; cycle 3 then served as a clean no-new-issues convergence proof with 6 additional rounds and no new required changes.
- Moved `Change-memory truth` below `Change-memory authority boundary` to reduce weak-model reading-order risk.
- Changed weak-model rule #3 from `forced semantic/hybrid maintenance check` to `forced semantic/hybrid retrieval query` so normal forced retrieval does not get misclassified as maintenance.
- Normalized `system_hygiene` wording across `Current truth snapshot`, `Current host limitations`, and `Current blocker classification` to the clearer `zero-findings (stale state expected)` phrasing.
- Final campaign verdict after all 3 cycles: no remaining material seam in the skill artifact; release remains candidate, not stable.

## 4.0.0-candidate.7 - 2026-04-12
- Ran a strict 6-round Dual Thinking pressure test of the near-final candidate with alternating AI Orchestrator and Qwen Orchestrator, including one honest Qwen recovery round after session pollution repeated an already-fixed finding.
- Accepted only micro-fixes, with no public command expansion and no scope rewrite.
- Foregrounded current-host truth as `Current host execution mode: degraded lexical-only (semantic-unbuilt)` so weak models cannot miss that this host is still degraded.
- Disambiguated the maintenance note so it points directly to the Runtime Capability Matrix and Implemented-vs-Optional sections instead of the vague phrase `visible contract above`.
- Narrowed WARN continuation rules so read-only degraded queries do not require an invented rollback path, while write/maintenance continuations still require explicit rollback awareness.
- Anchored the stable-release checklist explicitly to equipped-host verification and clarified that unresolved `system_hygiene` status is separate from the non-blocking change-memory candidate classification.
- Replaced `file-derived fallback matches` with `degraded retrieval fallback matches` in the combined stale-index + semantic-unavailable WARN rule so fallback wording stays honest about the retrieval source.
- Final closure verdict for this candidate line: accept with micro-fixes landed; release remains candidate, not stable.

## 4.0.0-candidate.6 - 2026-04-12
- Prepared the next candidate release surface after the SKILL contract cleanup pass, without changing the public command surface or stable-release claim level.
- Release truth for this candidate is explicit: change-memory is implemented and live; change-audit integration is implemented and live; current host remains degraded / safe-first; `system_hygiene` remains stale / zero-findings on the current host; overall release remains candidate, not stable.
- Candidate-line documentation is now aligned around the cleaned current `SKILL.md` contract without introducing a new major refactor.

## 4.0.0-candidate.1 - 2026-04-12
- Published the current-generation local-only runtime as an explicit pre-release candidate so the stable `4.0.0` line remains reserved for the first equipped-host validation pass.
- This release contains the upgraded v4 runtime: local semantic spine, hybrid fusion, temporal-relational rerank, integrity audit, relation-aware writes, repair surface, telemetry, regression pack, release-prep, host-profile shaping, and stable-host readiness guidance.
- Host-state limitation remains explicit: this reference host is semantic-unbuilt, so the release is publish-honest as a candidate/pre-release, not as a stable full-hybrid tag.

## 4.0.0 - 2026-04-12
- Opened the new `4.0.0` candidate line because the runtime is no longer a lexical-first shell: local semantic indexing/search, hybrid fusion, temporal-relational rerank, integrity audit, and relation-aware learning writes now exist in code.
- Added a real local semantic spine: local embedding loading (`local_files_only=True`), semantic readiness/freshness, local Qdrant collection management, semantic rebuild, and semantic search.
- Added real hybrid quality logic: reciprocal-rank fusion, diversity control, temporal/recency shaping, source-confidence weighting, conflict-state handling, and relation-aware rerank pressure.
- Added integrity/audit surfaces: `audit-memory.sh`, `index-memory.sh --audit`, lexical/semantic drift detection, vector-state classification (`semantic-unbuilt`, `stale-vectors`, `orphan-vectors`), and stronger health visibility.
- Added relation-aware write/evolution paths: `memorize.sh` now records stable signatures, source-confidence, conflict status, canonical relation targets, and rejects new freeform relation labels.
- Reworked `mine-patterns.py` to cluster learning blocks instead of whole files and surface relation/conflict/review-status summaries for local memory evolution.
- Synced the skill contract to the new runtime truth: `semantic` and `hybrid` are real implemented modes, `auto` remains the default weak-model route, and release surfaces now distinguish implemented capability from host-state activation.
- Added host-profile shaping for `standard` vs `max` local operation without changing the weak-model public interface.
- Known candidate-line limitation: this host is still semantic-degraded because local embedding dependencies/model and built vectors are absent; the runtime reports that state honestly and does not overclaim semantic-ready operation.

## 3.4.9 - 2026-04-12
- Ran 6 additional full Dual Thinking cycles (36 planned rounds) on top of the published `3.4.8` baseline, using clean-room fresh consultant sessions from cycle 2 onward to reduce session-pollution repeats.
- Accepted only real post-`3.4.8` contract-clarity fixes that survived direct verification; rejected repeated, polluted, or code-false findings.
- Explicitly scoped direct `query-memory.sh --mode semantic` and `--mode hybrid` as compatibility/maintenance-only underlying CLI modes, keeping the active weak-model routing surface at `auto|exact|learning|recent`.
- Made `query-memory.sh` exit-code handling more self-contained and weak-model safe: exit `2` no longer borrows `health-check.sh` WARN semantics, and exit `1` now preserves freshness cautions when `warnings[]` still reports backlog/index-staleness conditions.
- Surfaced freshness-relevant warning categories directly in the visible contract so weak models do not need maintenance-fold context to apply the clean-miss caution rule.
- Tightened visible wording for literal accuracy on the historical lexical-first line before the later v4 candidate reclassification.
- Final closure outcome after the extra 6-cycle campaign: no remaining material blocker for the historical `v3.4.x` release line before the later v4 candidate reclassification.

## 3.4.8 - 2026-04-12
- Ran a fresh 6-round Dual Thinking review of `super-memori` with alternating AI Orchestrator and Qwen Orchestrator, including one honest Qwen recovery round after session pollution repeated an already-fixed finding.
- Added the missing public `memorize.sh` outcome contract and aligned it to real script behavior.
- Added explicit public `health-check.sh` and `index-memory.sh` exit-code handling for weak-model safety.
- Stabilized bad-arguments handling in `memorize.sh`, `health-check.sh`, and `index-memory.sh` so documented `4` paths are real rather than aspirational.
- Made `index-memory.sh` return exit code `2` when warnings are present, so degraded/partial maintenance runs are machine-visible.
- Tightened `query-memory.sh` exit-code `1` wording so weak models must inspect `degraded` first and frame degraded no-results honestly.
- Clarified that `index-memory.sh --rebuild-vectors` returning `2` on lexical-only hosts is an expected degraded outcome, not a hard failure.
- This release remains publish-compatible on a WARN host only because the skill explicitly documents lexical-first degraded operation and does not claim semantic-ready behavior on every host.

## 3.4.7 - 2026-04-11
- Applied the recovered Qwen round-4 documentation fix: replaced the stale `lexical_freshness=fail` phrase in `SKILL.md` with the actual query payload fields `index_stale=true` / `index_fresh=false`.
- Synced `references/verification-evidence.md` header to the accepted `3.4.7` state.

## 3.4.6 - 2026-04-11
- Tightened the new `query-memory.sh` exit-code contract after live validation exposed one more nuance: `exit 1` can occur on a degraded host when no results are returned.
- Clarified in both `SKILL.md` and `references/command-contracts.md` that weak models must inspect `degraded` and `warnings[]` alongside `exit 1` instead of assuming a fully healthy clean miss.

## 3.4.5 - 2026-04-11
- Continued the 6-round Dual Thinking rerun with a real consultant-backed fix from DeepSeek round 3.
- Added an explicit `query-memory.sh` exit-code interpretation table to the public `SKILL.md` contract so weak models can distinguish clean no-results (`1`) from a broken retrieval stack (`3`).
- Bound deterministic fallback handling to those exit-code rules instead of leaving post-command interpretation implicit.

## 3.4.4 - 2026-04-11
- Continued the 6-round Dual Thinking rerun with a real consultant-backed fix from Qwen round 2.
- Refactored `query-memory.sh` exit routing so documented codes are now honest: `4` bad arguments, `3` retrieval stack unavailable, `5` internal error, `2` only for degraded-but-usable results, `1` for clean no-results.
- Separated informational host-state warnings from real degraded execution, so `warnings[]` no longer automatically imply `degraded=true`.
- Version-locked verification evidence to `3.4.4` and replaced placeholder validation notes with concrete observed results.

## 3.4.3 - 2026-04-11
- Continued the 6-round Dual Thinking rerun with a real consultant-backed fix from DeepSeek round 1.
- Clarified that `--mode learning` is a lexical lookup over learning-memory entries in v3.x, not a separate semantic retrieval engine.
- Changed `query-memory.sh` so JSON output preserves `mode_used=learning` for learning-mode requests instead of collapsing them to `exact`.
- Synced the public skill contract and command-contract reference to that learning-mode reality.

## 3.4.2 - 2026-04-11
- Restarted Cycle 2 6-round Dual Thinking rerun from the published `v3.4.1` baseline under the corrected full-inline consultant method.
- Bound the worst-case double-degraded read path explicitly to degraded `query-memory.sh` surfaced fallback results instead of undefined direct file access.
- Aligned the mandatory double-degraded warning string across the Health & Safety Gate and command-contract surface.
- Added explicit structured-warning rule: mandatory degraded notices must live inside `warnings[]` in JSON mode rather than outside the payload.

## 3.4.1 - 2026-04-11
- Cycle 1 additional 6-round Dual Thinking rerun from the published `v3.4.0` baseline.
- Synced `references/command-contracts.md` to the actual `health-check.sh` implementation: real exit codes (`0/2/3/4/5`), required human-readable output contract, required JSON fields, and explicit weak-model interpretation rule.
- Rejected speculative escalations that relied on non-existent files or non-literal terms instead of the current artifact.

## 3.4.0 - 2026-04-11
- Ran a fresh Dual Thinking review cycle for `super-memori` with alternating AI Orchestrator and Qwen Orchestrator.
- Closed the runtime authority gap for the double-degraded WARN state: when lexical freshness is stale and semantic dependencies are unavailable at the same time, canonical markdown files are now the only authoritative source, and returned matches must be described as lexical / grep-derived file matches only.
- Clarified that `query-memory.sh --mode auto` remains valid on degraded hosts only as the script-selected degraded lexical path, not as an implicit guarantee of successful hybrid retrieval.
- Added the minimum honest ClawHub publish surface for this skill: `_meta.json`, `.clawhubignore`, `CHANGELOG.md`, `PACKAGING_CHECKLIST.md`, and release evidence files.
- Added explicit release-health policy: `FAIL` blocks publication, while `WARN` is publish-compatible only when it reflects documented optional/degraded host conditions and the release surface does not imply semantic-ready or fully healthy baseline. On this host, current `WARN` is due to stale lexical freshness plus unavailable semantic dependencies, which is documented degraded operation rather than a false healthy claim.
- Made the WARN publish exception deterministic in release evidence: qualification now records observed health-check state/fields (`WARN`, `lexical_freshness: fail`, `semantic_dependencies: fail`, no `FAIL`) instead of relying on free-text judgment alone.

## 3.3.3 - 2026-04-11
- Previous local release surface prepared during the same hardening pass; superseded by 3.4.0 after registry version collisions on 3.3.2 and 3.3.3, with no new semantic/runtime claim changes.

## 3.3.1 - 2026-04-09
- Added explicit degraded-mode prerequisite note for semantic / hybrid retrieval.
- Kept the public interface constrained to four commands for weak-model safety.
- Preserved lexical-first baseline with honest degraded-mode reporting when semantic retrieval is unavailable.


path: PACKAGING_CHECKLIST.md
content: # Packaging Checklist — super-memori

Before setting publish-ready state, confirm:
- [x] Package root contains `SKILL.md`
- [x] Package root contains `_meta.json`
- [x] Package root contains `.clawhubignore`
- [x] Package root contains `CHANGELOG.md`
- [x] Package root contains `PACKAGING_CHECKLIST.md`
- [x] Package root contains required public entrypoints: `query-memory.sh`, `memorize.sh`, `index-memory.sh`, `health-check.sh`
- [x] Package root contains the startup self-heal entrypoint: `startup-self-check.sh`
- [x] Package root contains the session-start hook assets: `hooks/super-memori-session-start/HOOK.md` and `hooks/super-memori-session-start/handler.js`
- [x] Maintenance-only entrypoints are present and documented without being mistaken for weak-model public commands: `audit-memory.sh`, `repair-memory.sh`, `list-promotion-candidates.sh`, `validate-release.sh`
- [x] Package root contains required runtime helper code under `scripts/`
- [x] Package root contains required references used by the skill
- [x] Publish evidence files exist under `references/verification-evidence.md` and `references/reference-test-log.md`
- [x] `.clawhubignore` excludes transient/local state (`.clawhub/`, `__pycache__/`, `*.pyc`, `*.tmp`, `*.pending`, `*.lock`, `.index_state`, `backups/`, full `reports/` review artifacts, `reports/test-generated/`, and root temporary validation leftovers such as `tmp_round2_strict.txt`)
- [x] Skill heading version matches `_meta.json`
- [x] Release health policy is explicit: `FAIL` blocks publish; `WARN` is allowed only when it reflects documented optional/degraded host conditions and the release surface does not imply semantic-ready or fully healthy baseline
- [x] If release proceeds while the reference host is in `WARN`, the qualification is recorded deterministically from observed health-check fields/state, not free-text intuition alone
- [x] If release proceeds while the reference host is in `WARN`, `CHANGELOG.md` explains why that WARN is still compatible with honest publication
- [x] Stable promotion is blocked until an equipped host passes `references/stable-host-readiness.md`; candidate publication may proceed with explicit host-state qualification
- [x] No fake `.clawhub/origin.json` is created before a real publish; publish metadata is synced only after actual ClawHub publication

Release checks (workspace context, not isolated package-root context):
- `python3 skills/skill-creator-canonical/scripts/quick_validate.py skills/super_memori`
- `python3 skills/skill-creator-canonical/scripts/validate_weak_models.py skills/super_memori`
- `cd skills/super_memori && ./health-check.sh`
- `cd skills/super_memori && ./validate-release.sh --strict`
- `cd skills/super_memori && ./tests/regression/run-all.sh`
- `test -f skills/super_memori/_meta.json`
- `test -f skills/super_memori/.clawhubignore`
- `test -f skills/super_memori/CHANGELOG.md`
- `test -f skills/super_memori/PACKAGING_CHECKLIST.md`
- `test -f skills/super_memori/startup-self-check.sh`

Publish gate note:
- Treat the checklist as a support surface, not the source of truth. Real publish-readiness requires the actual package tree plus strict validation to agree in the intended OpenClaw workspace context.
- `validate-release.sh --strict` is a workspace-coupled release gate: it depends on sibling validator tooling under `../skill-creator-canonical/` and is not designed as an isolated package-directory check.
- If any checked file listed above is missing from the real package root, block publish and advance to a new version instead of mutating an already-published line.


path: _meta.json
content: ownerId: kn71swx2ntmj3me1xjt6vymkdn80m1sa
slug: super-memori
version: 4.0.23

path: audit-memory.sh
content: #!/usr/bin/env python3
# Exit codes: 0=OK, 2=drift/orphans found, 3=audit unavailable, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / "scripts"))
from super_memori_common import audit_memory_integrity, skill_operational_memory_audit, system_hygiene_status, audit_change_memory, hot_buffer_health_status, detect_interrupted_change_sequence, query_unverified_recent_changes


class AuditArgumentParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def main() -> int:
    p = AuditArgumentParser(description="Audit lexical/semantic integrity for super_memori")
    p.add_argument("--json", action="store_true")
    args = p.parse_args()

    result = audit_memory_integrity()
    skill_audit = skill_operational_memory_audit()
    result["skill_operational_memory"] = skill_audit
    result["system_hygiene"] = system_hygiene_status()
    result["agent_change_memory"] = audit_change_memory()
    result["hot_change_buffer"] = hot_buffer_health_status()
    result["hot_interrupted_sequences"] = detect_interrupted_change_sequence(limit=16)
    result["hot_unverified_recent"] = query_unverified_recent_changes(limit=16)
    if args.json:
        print(json.dumps(result, ensure_ascii=False, indent=2))
    else:
        print(f"status: {result['status']}")
        print(f"lexical_chunk_count: {result['lexical_chunk_count']}")
        print(f"vector_point_count: {result['vector_point_count']}")
        if result['orphan_chunks']:
            print("orphan_chunks:")
            for item in result['orphan_chunks']:
                print(f"- {item}")
        if result['orphan_fts_chunks']:
            print("orphan_fts_chunks:")
            for item in result['orphan_fts_chunks']:
                print(f"- {item}")
        if result['orphan_vectors']:
            print("orphan_vectors:")
            for item in result['orphan_vectors']:
                print(f"- {item}")
        if result['missing_vectors']:
            print("missing_vectors:")
            for item in result['missing_vectors']:
                print(f"- {item}")
        if result['broken_relations']:
            print("broken_relations:")
            for item in result['broken_relations']:
                print(f"- {item}")
    return 0 if result["status"] == "ok" else 2


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except Exception as exc:
        print(f"audit-memory internal error: {exc}", file=sys.stderr)
        raise SystemExit(5)


path: auto-learner.sh
content: #!/bin/bash
# auto-learner.sh — Auto-discover learning candidates from command logs
# Usage: ./auto-learner.sh
# Runs during weekly review, finds repeated failures not yet in memory.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_FILE="$HOME/.openclaw/logs/commands.log"
PENDING_DIR="$HOME/.openclaw/data/super_memori_pending"
THRESHOLD="0.85"
EXCLUDE_REGEX='^(grep|find|sed|awk|test|\[|ls|cd|echo|cat|head|tail|wc|sort|uniq|date|whoami|hostname|pwd)'

mkdir -p "$PENDING_DIR"

# Check if commands.log exists
if [ ! -f "$LOG_FILE" ]; then
    echo "⚠️  commands.log not found. Auto-learner inactive."
    echo "   Configure PROMPT_COMMAND in ~/.bashrc to enable."
    exit 0
fi

# Parse similarity score from query-memory.sh human-readable output
check_sim() {
    local text="$1"
    local sim
    sim=$("$SCRIPT_DIR/query-memory.sh" "$text" 2>/dev/null | \
        grep -oE '(score|similarity):? ?[0-9.]+' | head -1 | grep -oE '[0-9.]+' || echo "0")
    echo "${sim:-0}"
}

# Create pending JSON for a candidate
create_pending() {
    local cmd="$1" code="$2" ts="$3"
    local id="auto_$(date +%Y%m%d_%H%M%S)_$$"
    local title="${cmd:0:60} failed with exit $code"
    # Extract likely tags from command
    local tags="auto-discovered"
    
    cat > "$PENDING_DIR/$id.json" << JSONEOF
{
  "id": "$id",
  "type": "error",
  "auto_discovered": true,
  "reviewed": false,
  "created_at": "$(date -Iseconds)",
  "source_command": "$cmd",
  "exit_code": $code,
  "timestamp": "$ts",
  "suggested_title": "$title",
  "suggested_tags": ["$tags"]
}
JSONEOF
    echo "  → Pending: $title"
}

# Main loop: parse commands.log
seven_days_ago=$(date -d '7 days ago' +%s 2>/dev/null || date -v-7d +%s 2>/dev/null || echo "0")
new_count=0
skip_count=0

while IFS= read -r line; do
    # Expected format: 2026-04-05T14:23:10+08:00 | exit:2 | cmd:systemctl restart postgresql
    if [[ "$line" =~ ([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[^\ ]*)\ \|\ exit:([0-9]+)\ \|\ cmd:(.*) ]]; then
        ts="${BASH_REMATCH[1]}"
        code="${BASH_REMATCH[2]}"
        cmd="${BASH_REMATCH[3]}"
        
        # Skip exit 0 and 1 (grep no-match, etc.)
        [ "$code" -eq 0 ] && continue
        [ "$code" -eq 1 ] && continue
        
        # Skip trivial commands
        [[ "$cmd" =~ $EXCLUDE_REGEX ]] && continue
        
        # Check time window (7 days)
        ts_epoch=$(date -d "$ts" +%s 2>/dev/null || echo "0")
        [ "$ts_epoch" -lt "$seven_days_ago" ] && continue
        
        # Check if similar knowledge already exists
        sim=$(check_sim "$cmd")
        if (( $(echo "$sim >= $THRESHOLD" | bc -l 2>/dev/null || echo "0") )); then
            skip_count=$((skip_count + 1))
            continue
        fi
        
        create_pending "$cmd" "$code" "$ts"
        new_count=$((new_count + 1))
    fi
done < "$LOG_FILE"

echo "=== Auto-learner done: $new_count new, $skip_count already known. Pending: $(find "$PENDING_DIR" -name '*.json' 2>/dev/null | wc -l) files ==="


path: check_similarity.py
content: #!/usr/bin/env python3
"""
check_similarity.py — Check if a semantically similar entry already exists in Qdrant learnings.
Usage: python3 check_similarity.py "text to check" [threshold]
Returns the similar text if found above threshold, empty string otherwise.
"""
import sys
import json
import urllib.request
from sentence_transformers import SentenceTransformer

COLLECTION = "memories"
QDRANT_URL = "http://127.0.0.1:6333"
DEFAULT_THRESHOLD = 0.90


def main():
    if len(sys.argv) < 2:
        print("Usage: python3 check_similarity.py <text> [threshold]", file=sys.stderr)
        sys.exit(1)

    text = sys.argv[1]
    threshold = float(sys.argv[2]) if len(sys.argv) > 2 else DEFAULT_THRESHOLD

    try:
        model = SentenceTransformer("intfloat/multilingual-e5-small")
        vector = model.encode(f"query: {text}").tolist()
    except ImportError:
        sys.exit(0)  # Model not installed — skip dedup, not blocking
    except Exception:
        sys.exit(0)  # Embedding failed — skip dedup

    payload = json.dumps({"vector": vector, "limit": 1, "with_payload": True, "score_threshold": threshold}).encode()
    req = urllib.request.Request(f"{QDRANT_URL}/collections/{COLLECTION}/points/search",
                                 data=payload, headers={"Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            data = json.loads(resp.read())
            results = data.get("result", [])
            if results:
                point = results[0].get("payload", {})
                print(point.get("content", point.get("text", "")))
    except Exception:
        pass  # Qdrant unavailable — skip dedup


path: embed-file.py
content: #!/usr/bin/env python3
"""
embed-file.py — Embed a single memory file into Qdrant.
Usage: python3 embed-file.py <filepath> <collection_name> <qdrant_url>
"""
import sys
import os
import hashlib
import json
import urllib.request
import urllib.error
from datetime import datetime

def get_embedding(text):
    """Generate 384-dim embedding using multilingual-e5-small."""
    try:
        from sentence_transformers import SentenceTransformer
        model = SentenceTransformer("intfloat/multilingual-e5-small")
        return model.encode(text).tolist()
    except ImportError:
        print(f"⚠️ sentence-transformers not installed. Skipping: {os.path.basename(filepath)}")
        sys.exit(0)

def ensure_collection(url, collection_name):
    """Create collection if it doesn't exist (384 dims, Cosine)."""
    req = urllib.request.Request(
        f"{url}/collections/{collection_name}",
        data=json.dumps({
            "vectors": {"size": 384, "distance": "Cosine"},
            "optimizers_config": {"memmap_threshold": 50000}
        }).encode(),
        headers={"Content-Type": "application/json"}
    )
    try:
        urllib.request.urlopen(req, timeout=5)
    except urllib.error.URLError:
        pass  # May already exist

def get_hash(filepath):
    return hashlib.md5(filepath.encode()).hexdigest()

def file_needs_update(url, collection_name, point_id):
    """Check if point already exists in Qdrant."""
    req = urllib.request.Request(
        f"{url}/collections/{collection_name}/points/{point_id}",
        headers={"Content-Type": "application/json"}
    )
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            data = json.loads(resp.read())
            return data.get("result") is None  # True if not found
    except Exception:
        return True

def upsert_point(url, collection_name, point_id, vector, payload):
    """Upsert a point into Qdrant."""
    req = urllib.request.Request(
        f"{url}/collections/{collection_name}/points",
        data=json.dumps({
            "points": [{
                "id": point_id,
                "vector": vector,
                "payload": payload
            }]
        }).encode(),
        headers={"Content-Type": "application/json"}
    )
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            return True
    except Exception as e:
        print(f"⚠️ Failed to upsert {point_id}: {e}")
        return False

def extract_text_from_file(filepath):
    """Extract text content from a markdown file, skip metadata."""
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            content = f.read()
        # Remove YAML front matter if present
        if content.startswith("---"):
            parts = content.split("---", 2)
            if len(parts) >= 3:
                content = parts[2].strip()
        return content
    except Exception as e:
        print(f"⚠️ Cannot read {filepath}: {e}")
        return ""

def main():
    if len(sys.argv) < 4:
        print("Usage: python3 embed-file.py <filepath> <collection> <qdrant_url>")
        sys.exit(1)

    filepath = sys.argv[1]
    collection = sys.argv[2]
    qdrant_url = sys.argv[3]

    if not os.path.exists(filepath):
        print(f"⚠️ File not found: {filepath}")
        return

    point_id = get_hash(filepath)

    # Check if already indexed
    if not file_needs_update(qdrant_url, collection, point_id):
        print(f"  ⏭ Already indexed: {os.path.basename(filepath)}")
        return

    content = extract_text_from_file(filepath)
    if not content.strip():
        print(f"  ⏭ Empty file: {os.path.basename(filepath)}")
        return

    print(f"  📄 Embedding: {os.path.basename(filepath)}")
    
    try:
        # E5 model requires "passage: " prefix for documents
        vector = get_embedding(f"passage: {content}")
    except Exception as e:
        print(f"  ❌ Embedding failed: {e}")
        return

    # Determine file type from path
    rel_path = os.path.relpath(filepath, os.path.dirname(os.path.dirname(os.path.dirname(filepath))))
    file_type = rel_path.split("/")[1] if "/" in rel_path else "unknown"  # episodic, semantic, procedural

    payload = {
        "filepath": filepath,
        "file_type": file_type,
        "content": content[:2000],  # Limit payload size
        "date": os.path.basename(filepath)[:10] if "episodic" in filepath else "unknown",
        "filename": os.path.basename(filepath),
        "indexed_at": datetime.utcnow().isoformat()
    }

    if upsert_point(qdrant_url, collection, point_id, vector, payload):
        print(f"  ✅ Indexed: {os.path.basename(filepath)}")
    else:
        print(f"  ❌ Failed: {os.path.basename(filepath)}")

if __name__ == "__main__":
    main()


path: embed-raw-file.py
content: #!/usr/bin/env python3
"""
embed-raw-file.py — Embed any markdown file into Qdrant with custom metadata.

Usage: python3 embed-raw-file.py <filepath> <entry_type>

entry_type: learning, memory, correction, pattern, insight, knowledge_gap

Reuses the same embedding model and Qdrant endpoint as embed-file.py.
"""
import sys
import os
import hashlib
import json
import urllib.request
import urllib.error
from datetime import datetime

COLLECTION = "memories"
QDRANT_URL = "http://127.0.0.1:6333"


def get_embedding(text):
    try:
        from sentence_transformers import SentenceTransformer
        model = SentenceTransformer("intfloat/multilingual-e5-small")
        return model.encode(text).tolist()
    except ImportError:
        print("⚠️ sentence-transformers not installed. Skipping embed.", file=sys.stderr)
        sys.exit(0)


def get_hash(filepath, mod_time):
    return hashlib.md5(f"{filepath}:{mod_time}".encode()).hexdigest()


def point_exists(point_id):
    req = urllib.request.Request(
        f"{QDRANT_URL}/collections/{COLLECTION}/points/{point_id}",
        headers={"Content-Type": "application/json"}
    )
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            data = json.loads(resp.read())
            return data.get("result") is not None
    except Exception:
        return False


def upsert_point(point_id, vector, payload):
    req = urllib.request.Request(
        f"{QDRANT_URL}/collections/{COLLECTION}/points",
        data=json.dumps({
            "points": [{
                "id": point_id,
                "vector": vector,
                "payload": payload
            }]
        }).encode(),
        headers={"Content-Type": "application/json"}
    )
    with urllib.request.urlopen(req, timeout=10) as resp:
        return resp.status == 200


def main():
    if len(sys.argv) < 3:
        print("Usage: python3 embed-raw-file.py <filepath> <entry_type>", file=sys.stderr)
        sys.exit(1)

    filepath = sys.argv[1]
    entry_type = sys.argv[2]

    if not os.path.exists(filepath):
        print(f"⚠️ File not found: {filepath}", file=sys.stderr)
        return

    # Use file's modification time for stable hash
    mod_time = str(os.path.getmtime(filepath))
    point_id = get_hash(filepath, mod_time)

    if point_exists(point_id):
        print(f"  ⏭ Already indexed: {os.path.basename(filepath)}")
        return

    with open(filepath, "r", encoding="utf-8") as f:
        content = f.read()

    if not content.strip():
        return

    print(f"  📄 Embedding: {os.path.basename(filepath)} [{entry_type}]")

    # E5 model requires "passage: " prefix for documents
    vector = get_embedding(f"passage: {content}")
    content_snippet = content[:2000]

    payload = {
        "filepath": filepath,
        "file_type": entry_type,
        "content": content_snippet,
        "date": datetime.utcnow().strftime("%Y-%m-%d"),
        "filename": os.path.basename(filepath),
        "indexed_at": datetime.utcnow().isoformat()
    }

    if upsert_point(point_id, vector, payload):
        print(f"  ✅ Indexed: {os.path.basename(filepath)}")
    else:
        print(f"  ❌ Failed: {os.path.basename(filepath)}", file=sys.stderr)


if __name__ == "__main__":
    main()


path: eval-memory.sh
content: #!/usr/bin/env python3
# Exit codes: 0=ok, 2=degraded/partial, 3=unavailable/fail, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import os
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent
PER_CHECK_TIMEOUT_SECONDS = int(os.environ.get("SUPER_MEMORI_EVAL_TIMEOUT_SECONDS", "20"))

CASES = [
    ("exact retrieval", "exact retrieval honors command/path precision"),
    ("path lookup", "path lookup resolves canonical file references"),
    ("recent-time retrieval", "recent-time retrieval surfaces latest state"),
    ("learning-memory retrieval", "learning retrieval finds relevant lesson blocks"),
    ("semantic paraphrase retrieval", "semantic paraphrase retrieval returns meaning-based matches"),
    ("hybrid retrieval", "hybrid retrieval fuses lexical and semantic candidates"),
    ("temporal disambiguation", "temporal disambiguation prefers newer accepted facts"),
    ("contradiction handling", "contradiction handling prefers higher-confidence current truth"),
    ("relation-aware ranking", "relation-aware ranking respects supersedes/refines/confirms/contradicts/extends"),
    ("stale lexical degraded behavior", "stale lexical index reports degraded warnings"),
    ("semantic unavailable degraded behavior", "semantic-unavailable host stays honest"),
    ("vector-unbuilt degraded behavior", "vector-unbuilt host reports semantic-unbuilt"),
    ("audit integrity issues", "audit detects orphans/drift when present"),
    ("pattern mining misses", "pattern mining surfaces recurring misses"),
    ("pattern mining reuse", "pattern mining surfaces repeated success patterns"),
]


def run(cmd: list[str]) -> tuple[int, str, str]:
    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=PER_CHECK_TIMEOUT_SECONDS)
        return proc.returncode, proc.stdout, proc.stderr
    except subprocess.TimeoutExpired as exc:
        stdout = exc.stdout or ""
        stderr = exc.stderr or ""
        timeout_note = f"timed out after {PER_CHECK_TIMEOUT_SECONDS}s"
        return 2, stdout, f"{stderr}\n{timeout_note}".strip()


def main() -> int:
    parser = argparse.ArgumentParser(description="Run super_memori local evals")
    parser.add_argument("--json", action="store_true")
    args = parser.parse_args()

    results = []
    checks = [
        (["python3", str(ROOT / "query-memory.sh"), "query memory", "--mode", "auto", "--json", "--limit", "3"], "exact retrieval"),
        (["python3", str(ROOT / "query-memory.sh"), "memory/working-buffer.md", "--mode", "exact", "--json", "--limit", "3"], "path lookup"),
        (["python3", str(ROOT / "query-memory.sh"), "current", "--mode", "recent", "--json", "--limit", "3"], "recent-time retrieval"),
        (["python3", str(ROOT / "query-memory.sh"), "learning memory", "--mode", "learning", "--json", "--limit", "3"], "learning-memory retrieval"),
        (["python3", str(ROOT / "query-memory.sh"), "semantic paraphrase retrieval", "--mode", "semantic", "--json", "--limit", "3"], "semantic paraphrase retrieval"),
        (["python3", str(ROOT / "query-memory.sh"), "hybrid retrieval", "--mode", "hybrid", "--json", "--limit", "3"], "hybrid retrieval"),
        (["python3", str(ROOT / "query-memory.sh"), "older fact newer fact", "--mode", "hybrid", "--temporal", "--json", "--limit", "3"], "temporal disambiguation"),
        (["python3", str(ROOT / "audit-memory.sh"), "--json"], "audit integrity issues"),
        (["python3", str(ROOT / "repair-memory.sh"), "--plan", "--json"], "pattern mining misses"),
        (["python3", str(ROOT / "list-promotion-candidates.sh"), "--json"], "pattern mining reuse"),
    ]

    overall = 0
    for cmd, name in checks:
        code, out, err = run(cmd)
        ok = code in {0, 2}
        current = {
            "case": name,
            "exit_code": code,
            "ok": ok,
            "stdout": out[:2000],
            "stderr": err[:1000],
        }
        results.append(current)
        if not ok:
            overall = 2 if overall == 0 else overall
    payload = {"status": "ok" if overall == 0 else "warn", "results": results}
    if args.json:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
    else:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
    return overall


if __name__ == "__main__":
    raise SystemExit(main())


path: health-check.sh
content: #!/usr/bin/env python3
# Exit codes: 0=OK, 2=WARN, 3=FAIL, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / "scripts"))
from super_memori_common import (
    DB_PATH,
    MEMORY_DIR,
    QUEUE_DIR,
    audit_memory_integrity,
    canonical_memory_files,
    ensure_db,
    host_profile_status,
    lexical_index_stale,
    qdrant_collection_info,
    qdrant_ok,
    read_state,
    semantic_index_stale,
    semantic_runtime_status,
    skill_operational_memory_status,
    system_hygiene_status,
    audit_change_memory,
    query_current_known_state,
    hot_buffer_health_status,
)


class MemoriArgumentParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def main() -> int:
    p = MemoriArgumentParser(description="Check health of super_memori")
    p.add_argument("--json", action="store_true")
    args = p.parse_args()

    state = read_state()
    profile = host_profile_status()
    checks = []

    checks.append({"name": "memory_dir", "ok": MEMORY_DIR.exists(), "detail": str(MEMORY_DIR)})
    checks.append({"name": "canonical_files", "ok": len(canonical_memory_files()) > 0, "detail": len(canonical_memory_files())})
    checks.append({"name": "host_profile", "ok": True, "detail": profile})

    db_ok = False
    db_err = None
    fts_ok = False
    fts_err = None
    try:
        conn = ensure_db()
        conn.execute("SELECT 1")
        db_ok = True
        conn.execute("SELECT chunk_id FROM chunks_fts WHERE chunks_fts MATCH 'memory' LIMIT 1").fetchall()
        fts_ok = True
    except Exception as e:
        if not db_ok:
            db_err = str(e)
        else:
            fts_err = str(e)
    checks.append({"name": "lexical_db", "ok": db_ok, "detail": db_err or str(DB_PATH)})
    checks.append({"name": "lexical_fts", "ok": fts_ok, "detail": fts_err or "queryable"})

    qdrant_ready = qdrant_ok()
    qdrant_info = qdrant_collection_info() if qdrant_ready else None
    semantic_status = semantic_runtime_status(state)
    audit = audit_memory_integrity()
    checks.append({"name": "qdrant", "ok": qdrant_ready, "detail": "reachable" if qdrant_ready else "unreachable"})
    checks.append({"name": "qdrant_collection", "ok": bool(qdrant_info), "detail": qdrant_info.get("points_count") if qdrant_info else None})
    checks.append({"name": "semantic_dependencies", "ok": all(semantic_status["deps"].values()), "detail": semantic_status["deps"]})
    checks.append({"name": "local_embedding_model", "ok": semantic_status["model_ready"], "detail": semantic_status["model_error"] or "ready"})
    checks.append({"name": "semantic_vectors", "ok": semantic_status["indexed_vectors"] > 0, "detail": semantic_status["indexed_vectors"]})
    checks.append({"name": "integrity_audit", "ok": audit["status"] == "ok", "detail": audit})

    queue_count = len(list(QUEUE_DIR.glob("*.json"))) if QUEUE_DIR.exists() else 0
    checks.append({"name": "queue_backlog", "ok": queue_count < int(profile.get("queue_backlog_warn") or 500), "detail": queue_count})
    skill_status = skill_operational_memory_status()
    checks.append({"name": "skill_operational_memory", "ok": skill_status["fresh"], "detail": skill_status})
    hygiene_status = system_hygiene_status()
    checks.append({"name": "system_hygiene", "ok": hygiene_status["fresh"], "detail": hygiene_status})
    change_audit = audit_change_memory()
    checks.append({"name": "agent_change_memory", "ok": change_audit["status"] in {"ok", "warn"}, "detail": change_audit})
    hot_buffer = hot_buffer_health_status()
    checks.append({"name": "hot_change_buffer", "ok": hot_buffer["hot_buffer_integrity"] == "ok", "detail": hot_buffer})

    lexical_fresh = bool(state.get("lexical_last_indexed_at")) and not lexical_index_stale(state)
    semantic_fresh = bool(state.get("semantic_last_indexed_at")) and not semantic_index_stale(state)
    checks.append({"name": "lexical_freshness", "ok": lexical_fresh, "detail": state.get("lexical_last_indexed_at")})
    checks.append({"name": "semantic_freshness", "ok": semantic_fresh, "detail": state.get("semantic_last_indexed_at")})

    warnings = []
    if profile.get("detected_profile") == "max" and not semantic_status["model_ready"]:
        warnings.append("max profile requested but semantic stack not equipped on this host")
    if not qdrant_ready or not all(semantic_status["deps"].values()) or not semantic_status["model_ready"]:
        warnings.append("semantic layer degraded or unavailable")
    elif qdrant_info:
        reported_vectors = int(qdrant_info.get("indexed_vectors_count", 0) or qdrant_info.get("points_count", 0) or 0)
        if reported_vectors == 0:
            warnings.append("semantic backend reachable but no indexed vectors reported")
    if not state.get("lexical_last_indexed_at"):
        warnings.append("lexical index has not been built yet")
    elif not lexical_fresh:
        warnings.append("lexical index is stale; run index-memory.sh to refresh")
    if state.get("semantic_last_indexed_at") and not semantic_fresh:
        warnings.append("semantic index is stale; run index-memory.sh --rebuild-vectors to refresh")
    if audit["status"] != "ok":
        warnings.append("memory integrity drift detected; run index-memory.sh --audit and repair before trusting hybrid quality")
    if queue_count:
        warnings.append(f"queue backlog present: {queue_count}")
    if hot_buffer.get("hot_buffer_pressure", 0.0) >= 0.9:
        warnings.append("hot-change-buffer pressure is high; compaction/eviction is active")

    overall = "OK"
    if any(not c["ok"] for c in checks if c["name"] in {"memory_dir", "canonical_files", "lexical_db", "lexical_fts"}):
        overall = "FAIL"
    elif warnings:
        overall = "WARN"

    payload = {"status": overall, "warnings": warnings, "checks": checks}
    if args.json:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
    else:
        print(f"status: {overall}")
        for c in checks:
            print(f"- {c['name']}: {'ok' if c['ok'] else 'fail'} ({c['detail']})")
        if warnings:
            print("warnings:")
            for w in warnings:
                print(f"- {w}")
    return 0 if overall == "OK" else 2 if overall == "WARN" else 3


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except Exception as exc:
        print(f"health-check internal error: {exc}", file=sys.stderr)
        raise SystemExit(5)


path: hooks/super-memori-session-start/HOOK.md
content: ---
name: super-memori-session-start
description: "Run Super Memori startup self-check once per new session via agent:bootstrap"
homepage: https://docs.openclaw.ai/automation/hooks
metadata:
  {
    "openclaw":
      {
        "emoji": "🧠",
        "events": ["agent:bootstrap"],
        "requires": { "config": ["workspace.dir"] }
      },
  }
---

# Super Memori Session-Start Hook

Runs `startup-self-check.sh` exactly once per new OpenClaw session by listening to the real
internal `agent:bootstrap` event and recording the handled `sessionId` in a local ledger.

## Why

`agent:bootstrap` is a real runtime hook point in OpenClaw's bootstrap-context pipeline.
By itself it is not guaranteed to mean "only the first turn of a session" because bootstrap
context can be rebuilt again later. This hook adds a per-session ledger so the startup self-check
runs only once for each new `sessionId`, making the behavior enforceable at session scope.

## Configuration

```json
{
  "hooks": {
    "internal": {
      "enabled": true,
      "load": {
        "extraDirs": [
          "/home/irtual/.openclaw/workspace/skills/super_memori/hooks"
        ]
      },
      "entries": {
        "super-memori-session-start": {
          "enabled": true,
          "command": "cd /home/irtual/.openclaw/workspace/skills/super_memori && ./startup-self-check.sh --json --repair --report-file /home/irtual/.openclaw/workspace/memory/index-state/startup-self-check-last.json",
          "ledgerFile": "/home/irtual/.openclaw/workspace/memory/index-state/session-start-hook-ledger.json",
          "reportFile": "/home/irtual/.openclaw/workspace/memory/index-state/startup-self-check-last.json",
          "injectBootstrapReport": true,
          "maxLedgerEntries": 400
        }
      }
    }
  }
}
```

## Notes

- This hook does **not** rely on the model following instructions in `AGENTS.md`.
- It uses a real OpenClaw internal hook event: `agent:bootstrap`.
- The one-shot guarantee is enforced by the ledger keyed by `sessionId`.
- The report is written to `memory/index-state/startup-self-check-last.json`.
- When enabled with `injectBootstrapReport=true`, the latest report summary is appended as an
  extra bootstrap context file for the first handled turn of the session.


path: hooks/super-memori-session-start/handler.js
content: import fs from "node:fs/promises";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { a as isAgentBootstrapEvent } from "/home/irtual/.npm-global/lib/node_modules/openclaw/dist/internal-hooks-4i4Rq3Qq.js";
import { r as resolveHookConfig } from "/home/irtual/.npm-global/lib/node_modules/openclaw/dist/config-DiksYOxU.js";
import { t as createSubsystemLogger } from "/home/irtual/.npm-global/lib/node_modules/openclaw/dist/subsystem-Cgmckbux.js";

const execFileAsync = promisify(execFile);
const HOOK_KEY = "super-memori-session-start";
const log = createSubsystemLogger("hooks/super-memori-session-start");
const DEFAULT_COMMAND = "cd /home/irtual/.openclaw/workspace/skills/super_memori && ./startup-self-check.sh --json --repair --report-file /home/irtual/.openclaw/workspace/memory/index-state/startup-self-check-last.json";
const DEFAULT_LEDGER = "/home/irtual/.openclaw/workspace/memory/index-state/session-start-hook-ledger.json";
const DEFAULT_REPORT = "/home/irtual/.openclaw/workspace/memory/index-state/startup-self-check-last.json";
const DEFAULT_MAX_LEDGER_ENTRIES = 400;

async function fileExists(target) {
  try {
    await fs.access(target);
    return true;
  } catch {
    return false;
  }
}

async function readJsonSafe(file, fallback) {
  try {
    return JSON.parse(await fs.readFile(file, "utf8"));
  } catch {
    return fallback;
  }
}

async function writeJson(file, value) {
  await fs.mkdir(path.dirname(file), { recursive: true });
  await fs.writeFile(file, `${JSON.stringify(value, null, 2)}\n`, "utf8");
}

function normalizePositiveInt(value, fallback) {
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallback;
}

function summarizeReport(report) {
  if (!report || typeof report !== "object") return "startup self-check report unavailable";
  const status = typeof report.status === "string" ? report.status : "UNKNOWN";
  const repaired = report.repaired === true ? "yes" : "no";
  const smoke = report.smoke_query_ok === true ? "ok" : report.smoke_query_ok === false ? "failed" : "unknown";
  const warnings = Array.isArray(report.warnings) ? report.warnings : [];
  const warningText = warnings.length > 0 ? warnings.join("; ") : "none";
  return `status=${status}; repaired=${repaired}; smoke_query=${smoke}; warnings=${warningText}`;
}

async function runCommand(command, cwd) {
  return await execFileAsync("bash", ["-lc", command], {
    cwd,
    timeout: 15 * 60 * 1000,
    maxBuffer: 4 * 1024 * 1024
  });
}

async function ensureLedgerEntry(params) {
  const ledger = await readJsonSafe(params.ledgerFile, { version: 1, sessions: {} });
  if (!ledger || typeof ledger !== "object") return { shouldRun: true, ledger: { version: 1, sessions: {} } };
  if (!ledger.sessions || typeof ledger.sessions !== "object") ledger.sessions = {};
  const existing = ledger.sessions[params.sessionId];
  if (existing && typeof existing === "object") return { shouldRun: false, ledger, existing };
  return { shouldRun: true, ledger };
}

function trimLedgerSessions(sessions, maxEntries) {
  const entries = Object.entries(sessions).sort((a, b) => {
    const aTs = typeof a[1]?.handledAt === "string" ? Date.parse(a[1].handledAt) : 0;
    const bTs = typeof b[1]?.handledAt === "string" ? Date.parse(b[1].handledAt) : 0;
    return bTs - aTs;
  });
  return Object.fromEntries(entries.slice(0, maxEntries));
}

const superMemoriSessionStartHook = async (event) => {
  if (!isAgentBootstrapEvent(event)) return;
  const context = event.context || {};
  const cfg = context.cfg;
  const hookConfig = resolveHookConfig(cfg, HOOK_KEY);
  if (!hookConfig || hookConfig.enabled === false) return;

  const workspaceDir = typeof context.workspaceDir === "string" && context.workspaceDir.trim() ? context.workspaceDir : "/home/irtual/.openclaw/workspace";
  const sessionId = typeof context.sessionId === "string" && context.sessionId.trim() ? context.sessionId.trim() : "";
  const sessionKey = typeof context.sessionKey === "string" && context.sessionKey.trim() ? context.sessionKey.trim() : event.sessionKey;
  if (!sessionId) {
    log.warn("skipping: missing sessionId on agent:bootstrap event", { sessionKey });
    return;
  }

  const command = typeof hookConfig.command === "string" && hookConfig.command.trim() ? hookConfig.command.trim() : DEFAULT_COMMAND;
  const ledgerFile = typeof hookConfig.ledgerFile === "string" && hookConfig.ledgerFile.trim() ? hookConfig.ledgerFile.trim() : DEFAULT_LEDGER;
  const reportFile = typeof hookConfig.reportFile === "string" && hookConfig.reportFile.trim() ? hookConfig.reportFile.trim() : DEFAULT_REPORT;
  const injectBootstrapReport = hookConfig.injectBootstrapReport !== false;
  const maxLedgerEntries = normalizePositiveInt(hookConfig.maxLedgerEntries, DEFAULT_MAX_LEDGER_ENTRIES);

  const ledgerState = await ensureLedgerEntry({ ledgerFile, sessionId });
  if (!ledgerState.shouldRun) {
    if (injectBootstrapReport && await fileExists(reportFile) && Array.isArray(context.bootstrapFiles)) {
      const report = await readJsonSafe(reportFile, null);
      context.bootstrapFiles = [
        ...context.bootstrapFiles,
        {
          name: "TOOLS.md",
          path: reportFile,
          content: `# Super Memori Session-Start Report\n\nThis session was already handled by the enforceable session-start hook for sessionId ${sessionId}.\n\nLatest known report summary: ${summarizeReport(report)}\n`,
          missing: false
        }
      ];
    }
    return;
  }

  let runStatus = "ok";
  let exitCode = 0;
  let stdout = "";
  let stderr = "";
  try {
    const result = await runCommand(command, workspaceDir);
    stdout = result.stdout ?? "";
    stderr = result.stderr ?? "";
  } catch (error) {
    runStatus = "failed";
    exitCode = typeof error?.code === "number" ? error.code : 1;
    stdout = typeof error?.stdout === "string" ? error.stdout : "";
    stderr = typeof error?.stderr === "string" ? error.stderr : String(error);
    log.warn("session-start self-check command failed", { sessionId, sessionKey, exitCode });
  }

  const report = await readJsonSafe(reportFile, null);
  ledgerState.ledger.sessions[sessionId] = {
    sessionKey,
    handledAt: new Date().toISOString(),
    status: runStatus,
    exitCode,
    reportFile,
    reportStatus: report && typeof report.status === "string" ? report.status : null
  };
  ledgerState.ledger.sessions = trimLedgerSessions(ledgerState.ledger.sessions, maxLedgerEntries);
  await writeJson(ledgerFile, ledgerState.ledger);

  if (injectBootstrapReport && Array.isArray(context.bootstrapFiles)) {
    const summary = summarizeReport(report);
    const trailer = [
      "# Super Memori Session-Start Report",
      "",
      `This session was enforceably checked at bootstrap for sessionId ${sessionId}.`,
      `Hook status: ${runStatus}`,
      `Command exit code: ${exitCode}`,
      `Report summary: ${summary}`,
      stderr.trim() ? "" : "",
      stderr.trim() ? `stderr: ${stderr.trim().slice(0, 1200)}` : ""
    ].filter(Boolean).join("\n");
    context.bootstrapFiles = [
      ...context.bootstrapFiles,
      {
        name: "TOOLS.md",
        path: reportFile,
        content: trailer,
        missing: false
      }
    ];
  }
};

export default superMemoriSessionStartHook;


path: index-daily.sh
content: #!/bin/bash
# index-daily.sh — Incremental memory indexing into Qdrant
# Run via cron: 0 2 * * * ~/.openclaw/skills/memory-enhancement/index-daily.sh
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
MEMORY_DIR="$HOME/.openclaw/workspace/memory"
COLLECTION="memories"
QDRANT_URL="http://127.0.0.1:6333"

# Lock to prevent overlapping runs
exec 200>/tmp/memory-index.lock
flock -n 200 || { echo "[$(date)] Another indexing run in progress, skipping."; exit 0; }

STATE_FILE="$SCRIPT_DIR/.index_state"

# Ensure collection exists
create_collection() {
    curl -s -X PUT "${QDRANT_URL}/collections/${COLLECTION}" \
        -H 'Content-Type: application/json' \
        -d '{
            "vectors": {
                "size": 384,
                "distance": "Cosine"
            },
            "optimizers_config": {
                "memmap_threshold": 50000
            }
        }' 2>/dev/null || true
}

echo "[$(date)] Starting memory index..."

# Create state file if it doesn't exist
touch "$STATE_FILE"

# Find only changed or new files since last index
changed_files=$(find "$MEMORY_DIR" -name "*.md" -type f -newer "$STATE_FILE" 2>/dev/null)

if [ -z "$changed_files" ]; then
    echo "[$(date)] No changes detected since last index."
    touch "$STATE_FILE"
    exit 0
fi

echo "$changed_files" | while read -r filepath; do
    # Check if file needs embedding (modified since last index)
    python3 "$SCRIPT_DIR/embed-file.py" "$filepath" "$COLLECTION" "$QDRANT_URL"
done

echo "[$(date)] Index complete."


path: index-memory.sh
content: #!/usr/bin/env python3
# Exit codes: 0=OK, 2=degraded/partial, 3=FAIL, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import sqlite3
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / "scripts"))
from super_memori_common import (
    DB_PATH,
    QUEUE_DIR,
    audit_memory_integrity,
    ensure_db,
    host_profile_status,
    rebuild_lexical_index,
    rebuild_semantic_index,
    read_state,
    semantic_runtime_status,
    refresh_skill_operational_memory,
    scan_system_hygiene,
    plan_hygiene_cleanup,
    record_agent_change,
    reconcile_routine_change_history,
    compact_hot_buffer,
)


class MemoriArgumentParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def main() -> int:
    p = MemoriArgumentParser(description="Maintain super_memori indexes")
    p.add_argument("--incremental", action="store_true")
    p.add_argument("--full", action="store_true")
    p.add_argument("--rebuild-fts", action="store_true")
    p.add_argument("--rebuild-vectors", action="store_true")
    p.add_argument("--stats", action="store_true")
    p.add_argument("--vacuum", action="store_true")
    p.add_argument("--audit", action="store_true")
    p.add_argument("--json", action="store_true")
    args = p.parse_args()

    state = read_state()
    profile = host_profile_status()
    payload = {"actions": [], "warnings": [], "host_profile": profile}

    if args.stats:
        conn = ensure_db()
        payload["lexical_entries"] = conn.execute("SELECT COUNT(*) FROM entries").fetchone()[0]
        payload["lexical_chunks"] = conn.execute("SELECT COUNT(*) FROM chunks").fetchone()[0]
        payload["db_path"] = str(DB_PATH)
        payload["state"] = state
        payload["semantic"] = semantic_runtime_status(state)
        payload["audit"] = audit_memory_integrity()
        if payload["audit"]["status"] != "ok":
            payload["warnings"].append("integrity audit reported drift or orphans")
    else:
        if args.full or args.incremental or args.rebuild_fts or not any([args.full, args.incremental, args.rebuild_fts, args.rebuild_vectors, args.vacuum, args.audit]):
            stats = rebuild_lexical_index(full=bool(args.full or args.rebuild_fts))
            payload["actions"].append({"lexical_index": stats})

        queue_processed = 0
        queue_errors = 0
        processed_dir = QUEUE_DIR / "processed"
        processed_dir.mkdir(parents=True, exist_ok=True)
        if QUEUE_DIR.exists():
            for item in sorted(QUEUE_DIR.glob("*.json")):
                try:
                    data = json.loads(item.read_text(encoding="utf-8"))
                    target = processed_dir / item.name
                    target.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
                    item.unlink()
                    queue_processed += 1
                except Exception:
                    queue_errors += 1
            if queue_processed:
                payload["actions"].append({"queue_processed": queue_processed})
            if queue_errors:
                payload["warnings"].append(f"queue items with processing errors: {queue_errors}")

        if args.rebuild_vectors:
            try:
                stats = rebuild_semantic_index(recreate=bool(args.full), limit=None)
                payload["actions"].append({"semantic_rebuild": stats})
            except Exception as exc:
                payload["warnings"].append(f"semantic rebuild not executed: {exc}")

        if args.audit:
            audit = audit_memory_integrity()
            payload["actions"].append({"audit": audit})
            if audit["status"] != "ok":
                payload["warnings"].append("integrity audit reported drift or orphans")

        maintenance_record = record_agent_change(
            status="applied_but_unverified",
            target_scope="openclaw",
            action_type="reindex",
            exact_paths=[str(DB_PATH)],
            why_change_was_made="Refresh index state during maintenance",
            expected_effect="keep lexical indexes and operational memories current",
            risk_level="low",
            rollback_possible=True,
            rollback_method="git restore / db vacuum rollback",
            verification_steps=["run health-check.sh", "run audit-memory.sh"],
            verification_state="unverified",
            command_or_patch_summary="index-memory maintenance path",
            related_skills=["super-memori"],
        )
        payload["actions"].append({"agent_change_memory": maintenance_record})

        hot_buffer_compaction = compact_hot_buffer()
        payload["actions"].append({"hot_change_buffer": hot_buffer_compaction})
        hygiene_scan = scan_system_hygiene()
        payload["actions"].append({"system_hygiene_scan": hygiene_scan})
        hygiene_plan = plan_hygiene_cleanup(allow_destructive=False)
        payload["actions"].append({"system_hygiene_cleanup_plan": hygiene_plan})

        skill_refresh = refresh_skill_operational_memory()
        payload["actions"].append({"skill_operational_memory": skill_refresh})

        post_audit = audit_memory_integrity()
        payload["actions"].append({"post_audit": post_audit})
        if post_audit["status"] != "ok":
            payload["warnings"].append("integrity audit reported drift or orphans after maintenance")
        state_after = read_state()
        post_semantic = semantic_runtime_status(state_after)
        if not all(post_semantic["deps"].values()) or not post_semantic["model_ready"]:
            payload["warnings"].append("semantic layer degraded or unavailable after maintenance")

        if not payload["warnings"]:
            reconciled = reconcile_routine_change_history(
                target_scope="openclaw",
                action_type="reindex",
                exact_paths=[str(DB_PATH)],
                command_or_patch_summary="index-memory maintenance path",
                risk_level="low",
                related_skill="super-memori",
                observed_effect="maintenance completed successfully; health/audit verification passed",
                verification_steps=["run health-check.sh", "run audit-memory.sh"],
            )
            payload["actions"].append({"agent_change_memory_reconciled": reconciled})

        if args.vacuum:
            conn = ensure_db()
            conn.execute("VACUUM")
            payload["actions"].append({"vacuum": "done"})

    if args.json:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
    else:
        print(payload)
    if payload["warnings"]:
        return 2
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except sqlite3.Error as exc:
        print(f"index-memory failure: {exc}", file=sys.stderr)
        raise SystemExit(3)
    except Exception as exc:
        print(f"index-memory internal error: {exc}", file=sys.stderr)
        raise SystemExit(5)


path: list-promotion-candidates.sh
content: #!/usr/bin/env python3
# Exit codes: 0=ok, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / 'scripts'))
from super_memori_common import list_promotion_candidates


class CandidateParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def main() -> int:
    p = CandidateParser(description='List local promotion/evolution candidate signals')
    p.add_argument('--limit', type=int, default=10)
    p.add_argument('--json', action='store_true')
    args = p.parse_args()
    payload = {'status': 'ok', 'candidates': list_promotion_candidates(limit=args.limit)}
    if args.json:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
    else:
        print(payload)
    return 0


if __name__ == '__main__':
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except Exception as exc:
        print(f'list-promotion-candidates internal error: {exc}', file=sys.stderr)
        raise SystemExit(5)


path: memorize.sh
content: #!/usr/bin/env python3
# Exit codes: 0=written/duplicate, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import os
import sys
import tempfile
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / "scripts"))
from super_memori_common import LEARNINGS_DIR, QUEUE_DIR, VALID_LEARNING_TYPES, MAX_LEARNING_CHARS, ensure_dirs, learning_text_exists, now_iso, validate_relation_target

VALID_CONFLICT_STATUS = {"none", "active", "superseded", "contradicted", "stale"}


class MemoriArgumentParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def normalize_list_arg(value: str) -> list[str]:
    items = []
    for piece in value.replace(";", ",").split(","):
        cleaned = piece.strip()
        if cleaned:
            items.append(cleaned)
    return items


def derive_signature(text: str, learning_type: str, tags: list[str], source: str) -> str:
    head = " ".join(text.strip().split())[:180].casefold()
    payload = json.dumps({"type": learning_type, "source": source.casefold(), "tags": sorted(tag.casefold() for tag in tags), "head": head}, ensure_ascii=False)
    import hashlib
    return hashlib.sha1(payload.encode("utf-8")).hexdigest()[:16]


def main() -> int:
    p = MemoriArgumentParser(description="Record a useful learning entry")
    p.add_argument("text")
    p.add_argument("type", nargs="?", default="lesson")
    p.add_argument("--pending", action="store_true")
    p.add_argument("--reviewed", action="store_true")
    p.add_argument("--tags", default="")
    p.add_argument("--source", default="agent")
    p.add_argument("--signature", default="")
    p.add_argument("--source-confidence", type=float)
    p.add_argument("--conflict-status", default="none")
    p.add_argument("--supersedes", default="")
    p.add_argument("--contradicts", default="")
    p.add_argument("--confirms", default="")
    p.add_argument("--refines", default="")
    p.add_argument("--extends", dest="extends_rel", default="")
    p.add_argument("--json", action="store_true")
    args = p.parse_args()

    if args.type not in VALID_LEARNING_TYPES:
        print(f"invalid learning type: {args.type}", file=sys.stderr)
        return 4
    if args.conflict_status not in VALID_CONFLICT_STATUS:
        print(f"invalid conflict status: {args.conflict_status}", file=sys.stderr)
        return 4
    if args.source_confidence is not None and not (0.0 <= args.source_confidence <= 1.0):
        print("source confidence must be between 0.0 and 1.0", file=sys.stderr)
        return 4

    ensure_dirs()
    LEARNINGS_DIR.mkdir(parents=True, exist_ok=True)
    day = time.strftime("%Y-%m-%d")
    target = LEARNINGS_DIR / f"{day}.md"
    status = "reviewed" if args.reviewed else "pending"
    tags = [t.strip() for t in args.tags.split(",") if t.strip()]

    text = args.text.strip()
    truncated = False
    if len(text) > MAX_LEARNING_CHARS:
        text = text[:MAX_LEARNING_CHARS].rstrip() + "\n[truncated]"
        truncated = True

    signature = args.signature.strip() or derive_signature(text, args.type, tags, args.source)
    duplicate_path = learning_text_exists(text, signature=signature)
    if duplicate_path:
        payload = {
            "status": "duplicate",
            "file": str(duplicate_path),
            "type": args.type,
            "review_status": status,
            "signature": signature,
        }
        print(json.dumps(payload, ensure_ascii=False, indent=2) if args.json else f"duplicate skipped: {duplicate_path}")
        return 0

    if not target.exists():
        target.write_text(f"# Learnings — {day}\n\n", encoding="utf-8")

    relation_map = {
        "supersedes": normalize_list_arg(args.supersedes),
        "contradicts": normalize_list_arg(args.contradicts),
        "confirms": normalize_list_arg(args.confirms),
        "refines": normalize_list_arg(args.refines),
        "extends": normalize_list_arg(args.extends_rel),
    }
    for rel_key, targets in relation_map.items():
        for target in targets:
            ok, reason = validate_relation_target(rel_key, target)
            if not ok:
                print(f"invalid relation target for {rel_key}: {target} ({reason})", file=sys.stderr)
                return 4
    relation_lines = []
    for key, values in relation_map.items():
        if values:
            relation_lines.append(f"- {key}: {', '.join(values)}")
    source_confidence = args.source_confidence if args.source_confidence is not None else (0.7 if status == "reviewed" else 0.45)

    block = (
        f"## {args.type} — {now_iso()}\n"
        f"- status: {status}\n"
        f"- source: {args.source}\n"
        f"- tags: {', '.join(tags) if tags else 'none'}\n"
        f"- signature: {signature}\n"
        f"- source_confidence: {source_confidence:.2f}\n"
        f"- conflict_status: {args.conflict_status}\n"
        + ("\n".join(relation_lines) + "\n" if relation_lines else "")
        + f"\n{text}\n\n"
    )
    existing_text = target.read_text(encoding="utf-8", errors="ignore") if target.exists() else f"# Learnings — {day}\n\n"
    merged_text = existing_text + block
    with tempfile.NamedTemporaryFile("w", encoding="utf-8", delete=False, dir=str(target.parent), prefix=f"{target.name}.", suffix=".tmp") as tmp:
        tmp.write(merged_text)
        tmp_path = Path(tmp.name)
    os.replace(tmp_path, target)

    queue_item = QUEUE_DIR / f"learn-{int(time.time())}.json"
    queue_item.write_text(json.dumps({
        "kind": "learning",
        "file": str(target),
        "type": args.type,
        "review_status": status,
        "source": args.source,
        "tags": tags,
        "signature": signature,
        "source_confidence": source_confidence,
        "conflict_status": args.conflict_status,
        "relations": relation_map,
        "created_at": now_iso(),
    }, ensure_ascii=False, indent=2), encoding="utf-8")

    payload = {
        "status": "written",
        "file": str(target),
        "queue_item": str(queue_item),
        "type": args.type,
        "review_status": status,
        "truncated": truncated,
        "signature": signature,
        "source_confidence": source_confidence,
        "conflict_status": args.conflict_status,
        "relations": relation_map,
    }
    print(json.dumps(payload, ensure_ascii=False, indent=2) if args.json else f"written: {target}\nqueued: {queue_item}")
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except Exception as exc:
        print(f"memorize internal error: {exc}", file=sys.stderr)
        raise SystemExit(5)


path: mine-patterns.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import re
from collections import Counter, defaultdict
from datetime import date, datetime
from pathlib import Path

ROOT = Path(__file__).resolve().parent
WORKSPACE = Path.home() / ".openclaw" / "workspace"
LEARNINGS_DIR = WORKSPACE / ".learnings"
OUT_DIR = ROOT / "reports"
OUT_PATH = OUT_DIR / "pattern-report.json"

STOP = {
    "the", "and", "for", "with", "that", "this", "from", "into", "after", "before",
    "ошибка", "ошибки", "error", "failed", "failure", "lesson", "insight", "correction",
}


def normalize(text: str) -> str:
    text = text.lower()
    text = re.sub(r"`[^`]+`", " ", text)
    text = re.sub(r"https?://\S+", " ", text)
    text = re.sub(r"[^\w\s-]", " ", text)
    text = re.sub(r"\b\d+\b", " ", text)
    text = re.sub(r"\s+", " ", text).strip()
    return text


def tokens(text: str) -> list[str]:
    toks = [t for t in normalize(text).split() if len(t) > 2 and t not in STOP]
    return toks[:32]


def representative_key(text: str) -> str:
    toks = tokens(text)
    if not toks:
        return "misc"
    return " ".join(toks[:10])


def parse_ts(text: str) -> date | None:
    try:
        return datetime.strptime(text.strip(), "%Y-%m-%dT%H:%M:%S%z").date()
    except Exception:
        return None


def read_learning_blocks() -> list[dict]:
    blocks: list[dict] = []
    if not LEARNINGS_DIR.exists():
        return blocks
    for path in sorted(LEARNINGS_DIR.rglob("*.md")):
        try:
            raw = path.read_text(encoding="utf-8", errors="ignore")
        except Exception:
            continue
        if path.name.isupper() or path.stem.isupper():
            continue
        current = None
        body_lines: list[str] = []
        for line in raw.splitlines():
            if line.startswith("## "):
                if current:
                    current["body"] = "\n".join(body_lines).strip()
                    blocks.append(current)
                title = line[3:].strip()
                current = {
                    "path": str(path),
                    "title": title,
                    "type": title.split("—", 1)[0].strip().casefold(),
                    "timestamp": title.split("—", 1)[1].strip() if "—" in title else None,
                    "status": "unknown",
                    "source": "unknown",
                    "tags": [],
                    "signature": None,
                    "source_confidence": None,
                    "conflict_status": "none",
                    "relations": {},
                }
                body_lines = []
                continue
            if not current:
                continue
            low = line.strip().lower()
            if low.startswith("- status:"):
                current["status"] = line.split(":", 1)[1].strip().casefold()
            elif low.startswith("- source:"):
                current["source"] = line.split(":", 1)[1].strip()
            elif low.startswith("- tags:"):
                current["tags"] = [t.strip() for t in line.split(":", 1)[1].split(",") if t.strip() and t.strip().lower() != "none"]
            elif low.startswith("- signature:"):
                sig = line.split(":", 1)[1].strip()
                current["signature"] = None if sig.lower() == "none" else sig
            elif low.startswith("- source_confidence:"):
                try:
                    current["source_confidence"] = float(line.split(":", 1)[1].strip())
                except Exception:
                    pass
            elif low.startswith("- conflict_status:"):
                current["conflict_status"] = line.split(":", 1)[1].strip().casefold()
            elif any(low.startswith(f"- {key}:") for key in ["supersedes", "contradicts", "confirms", "refines", "extends"]):
                key = low.split(":", 1)[0].replace("-", "").strip()
                values = [v.strip() for v in line.split(":", 1)[1].replace(";", ",").split(",") if v.strip()]
                current["relations"][key] = values
            else:
                body_lines.append(line)
        if current:
            current["body"] = "\n".join(body_lines).strip()
            blocks.append(current)
    return blocks


def reuse_signal(items: list[dict]) -> str:
    combined = normalize(" ".join((item.get("body") or "") for item in items))
    hits = sum(
        1
        for needle in [
            "reused successfully", "worked again", "still valid", "repeated use",
            "успешно переиспользовано", "работает повторно", "повторно сработало",
        ]
        if needle in combined
    )
    if hits >= 3:
        return "high"
    if hits == 2:
        return "moderate"
    if hits == 1:
        return "low"
    return "none"


def suggested_target(key: str, items: list[dict]) -> str:
    if any(item.get("type") == "correction" for item in items):
        return "procedural"
    if any(item.get("conflict_status") in {"superseded", "contradicted", "stale"} for item in items):
        return "lessons"
    if any(word in key for word in ["command", "script", "restart", "fix", "debug"]):
        return "procedural"
    if any(word in key for word in ["prefer", "always", "never", "infrastructure", "host"]):
        return "semantic"
    if any(word in key for word in ["mistake", "failure", "anti", "wrong"]):
        return "lessons"
    return "review-only"


def retrieval_signal(items: list[dict]) -> str:
    combined = normalize(" ".join((item.get("body") or "") + " " + " ".join(item.get("tags") or []) for item in items))
    if any(word in combined for word in ["fallback", "lexical", "semantic down", "degraded"]):
        return "lexical-fallback"
    if any(word in combined for word in ["no result", "miss", "not found", "missing knowledge"]):
        return "miss-cluster"
    if any(word in combined for word in ["stale", "reindex", "fragment", "orphan", "drift"]):
        return "index-fragmentation"
    return "review-needed" if len(items) >= 3 else "none"


def group_key(item: dict) -> str:
    return item.get("signature") or representative_key((item.get("body") or "") + " " + item.get("title", ""))


def main() -> int:
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    groups: dict[str, list[dict]] = defaultdict(list)
    for item in read_learning_blocks():
        body = (item.get("body") or "").strip()
        if not body:
            continue
        groups[group_key(item)].append(item)

    clusters = []
    for idx, (key, items) in enumerate(sorted(groups.items(), key=lambda kv: len(kv[1]), reverse=True), start=1):
        if key == "misc" and len(items) == 1:
            continue
        counts = Counter(tokens(" ".join((i.get("body") or "") for i in items)))
        confidence = "high" if len(items) >= 5 else "medium" if len(items) >= 3 else "low"
        days = [parse_ts(i.get("timestamp") or "") for i in items if parse_ts(i.get("timestamp") or "")]
        first_seen = min(days).isoformat() if days else None
        last_seen = max(days).isoformat() if days else None
        active_window_days = (max(days) - min(days)).days if len(days) >= 2 else 0
        recency_weight = "current"
        if days:
            age_days = (date.today() - max(days)).days
            if age_days > 90:
                recency_weight = "stale"
            elif age_days > 30:
                recency_weight = "cooling"
        reuse = reuse_signal(items)
        stale_success_candidate = bool(reuse in {"moderate", "high"} and recency_weight == "stale")
        source_files = sorted({i["path"] for i in items})
        relation_summary = defaultdict(int)
        for item in items:
            for rel_key, rel_values in (item.get("relations") or {}).items():
                relation_summary[rel_key] += len(rel_values)
        clusters.append({
            "cluster_id": f"pattern-{idx:03d}",
            "frequency": len(items),
            "representative_title": items[0]["title"],
            "normalized_key": key,
            "signature": items[0].get("signature"),
            "top_terms": [w for w, _ in counts.most_common(10)],
            "suggested_target": suggested_target(key, items),
            "retrieval_audit_signal": retrieval_signal(items),
            "reuse_signal": reuse,
            "confidence": confidence,
            "first_seen": first_seen,
            "last_seen": last_seen,
            "active_window_days": active_window_days,
            "recency_weight": recency_weight,
            "stale_success_candidate": stale_success_candidate,
            "source_files": source_files,
            "conflict_states": Counter(item.get("conflict_status", "none") for item in items),
            "relation_summary": dict(relation_summary),
            "review_status_mix": Counter(item.get("status", "unknown") for item in items),
        })

    report = {
        "status": "ok",
        "learnings_dir": str(LEARNINGS_DIR),
        "cluster_count": len(clusters),
        "clusters": clusters,
        "note": "Review-only report. Do not auto-promote without human approval.",
    }
    OUT_PATH.write_text(json.dumps(report, ensure_ascii=False, indent=2, default=lambda x: dict(x)), encoding="utf-8")
    print(json.dumps(report, ensure_ascii=False, indent=2, default=lambda x: dict(x)))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: query-memory.sh
content: #!/usr/bin/env python3
# Exit codes: 0=results, 1=no-results, 2=degraded/partial, 3=retrieval-stack-unavailable, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / "scripts"))
from super_memori_common import (
    diversify_results,
    grep_fallback,
    host_profile_status,
    lexical_index_stale,
    lexical_search,
    log_retrieval_event,
    read_state,
    reciprocal_rank_fuse,
    semantic_index_stale,
    semantic_runtime_status,
    semantic_search,
    skill_operational_decision,
    skill_operational_recall,
    skill_recall_gate,
    temporal_relational_rerank,
    build_hot_recovery_bundle,
    summarize_authority_surface,
)


class QueryMemoryArgError(Exception):
    pass


class QueryMemoryParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        raise QueryMemoryArgError(message)


def build_parser() -> QueryMemoryParser:
    p = QueryMemoryParser(description="Query local memory")
    p.add_argument("query")
    p.add_argument("--mode", default="auto", choices=["auto", "exact", "semantic", "hybrid", "recent", "learning"])
    p.add_argument("--type", dest="memory_type", default="all", choices=["episodic", "semantic", "procedural", "learning", "buffer", "all"])
    p.add_argument("--json", action="store_true")
    p.add_argument("--limit", type=int, default=5)
    p.add_argument("--from", dest="date_from")
    p.add_argument("--to", dest="date_to")
    p.add_argument("--tags")
    p.add_argument("--reviewed-only", action="store_true")
    p.add_argument("--temporal", action="store_true")
    return p


def emit_error(message: str, *, as_json: bool = False) -> None:
    if as_json:
        print(json.dumps({"error": message}, ensure_ascii=False, indent=2))
    else:
        print(message, file=sys.stderr)


def apply_recent_filter(results: list[dict], date_from: str | None, date_to: str | None) -> list[dict]:
    if not date_from and not date_to:
        return results
    filtered = []
    for item in results:
        updated_at = item.get("updated_at") or ""
        date_part = updated_at[:10] if updated_at else ""
        if date_from and date_part and date_part < date_from:
            continue
        if date_to and date_part and date_part > date_to:
            continue
        filtered.append(item)
    return filtered


def main() -> int:
    parser = build_parser()
    try:
        args = parser.parse_args()
    except QueryMemoryArgError as e:
        emit_error(f"bad arguments: {e}")
        return 4

    try:
        warnings: list[str] = []
        degraded_reasons: list[str] = []
        state = read_state()
        profile = host_profile_status()
        skill_gate = skill_recall_gate(args.query)
        hot_query = any(token in args.query.casefold() for token in ["что только что изменилось", "только что изменилось", "что было недавно", "что не завершилось", "какие последние изменения не проверены", "recent changes", "just changed", "latest changes", "unfinished", "unverified"])
        hot_bundle = build_hot_recovery_bundle(query=args.query, limit=max(args.limit, 5)) if hot_query else None
        index_stale = lexical_index_stale(state)
        semantic_stale = semantic_index_stale(state)
        semantic_probe_needed = args.mode in {"auto", "semantic", "hybrid"}
        if semantic_probe_needed:
            semantic_status = semantic_runtime_status(state)
            semantic_ready = semantic_status["semantic_ready"]
            if not semantic_status["qdrant"]:
                warnings.append("semantic backend unavailable; lexical-only mode active")
            elif not semantic_status["deps"].get("sentence_transformers") or not semantic_status["deps"].get("numpy"):
                warnings.append("semantic dependencies missing; lexical-only mode active")
            elif not semantic_status["model_ready"]:
                warnings.append(f"local embedding model unavailable; lexical-only mode active ({semantic_status['model_error']})")
            elif not semantic_status["collection_present"]:
                warnings.append("semantic collection missing; lexical-only mode active")
            elif semantic_status["indexed_vectors"] <= 0:
                warnings.append("semantic collection has no indexed vectors; lexical-only mode active")
            elif semantic_stale:
                warnings.append("semantic index is stale; semantic retrieval may miss recent changes")
                degraded_reasons.append("semantic index is stale")
        else:
            semantic_status = {
                "deps": {"sentence_transformers": None, "numpy": None},
                "qdrant": None,
                "collection_present": None,
                "collection_info": None,
                "model_ready": None,
                "model_error": None,
                "indexed_vectors": int(state.get("semantic_indexed_vectors") or 0),
                "semantic_fresh": bool(state.get("semantic_last_indexed_at")) and not semantic_stale,
                "semantic_last_indexed_at": state.get("semantic_last_indexed_at"),
                "semantic_ready": False,
                "host_profile": profile,
            }
            semantic_ready = False

        mode_used = args.mode
        lexical_results: list[dict] = []
        semantic_results: list[dict] = []
        skill_candidates = skill_operational_recall(args.query, limit=3) if skill_gate.get("should_recall") else []
        lexical_error = None
        semantic_error = None
        fallback_error = None

        if args.mode == "auto":
            mode_used = "hybrid" if semantic_ready else "exact"
            if not semantic_ready:
                warnings.append("auto mode selected lexical search because semantic retrieval is unavailable")
                degraded_reasons.append("auto mode degraded to lexical search")
        elif args.mode in {"semantic", "hybrid"} and not semantic_ready:
            warnings.append(f"requested mode '{args.mode}' degraded to lexical search")
            degraded_reasons.append(f"requested mode '{args.mode}' degraded to lexical search")
            mode_used = "exact"
        elif args.mode == "learning":
            mode_used = "learning"
            if not semantic_ready:
                warnings.append("learning mode ran on lexical search on this host")

        if index_stale:
            warnings.append("lexical index is stale; results may miss recent changes")
            degraded_reasons.append("lexical index is stale")

        lexical_mode = mode_used in {"exact", "hybrid", "recent", "learning"}
        semantic_mode = mode_used in {"semantic", "hybrid"} and semantic_ready

        if lexical_mode:
            try:
                lexical_results = lexical_search(
                    args.query,
                    memory_type=("learning" if args.mode == "learning" else args.memory_type),
                    limit=max(args.limit * 4, 10),
                    reviewed_only=args.reviewed_only,
                )
            except Exception as e:
                lexical_error = str(e)
                warnings.append(f"lexical index unavailable: {e}")
                degraded_reasons.append("lexical index unavailable")

        if semantic_mode:
            try:
                semantic_results = semantic_search(
                    args.query,
                    memory_type=("learning" if args.mode == "learning" else args.memory_type),
                    limit=max(args.limit * int(profile["retrieval"]["fusion_multiplier"]), profile["retrieval"]["semantic_top_k"]),
                    reviewed_only=args.reviewed_only,
                )
                if not semantic_results:
                    warnings.append("semantic retrieval returned no candidates; using lexical path only")
                    degraded_reasons.append("semantic retrieval returned no candidates")
            except Exception as e:
                semantic_error = str(e)
                warnings.append(f"semantic retrieval unavailable: {e}")
                degraded_reasons.append("semantic retrieval unavailable")
                semantic_results = []

        if mode_used == "hybrid" and semantic_results:
            fused_results = reciprocal_rank_fuse(lexical_results, semantic_results, limit=max(args.limit * int(profile["retrieval"]["fusion_multiplier"]), profile["retrieval"]["rerank_limit"]))
        elif mode_used == "semantic" and semantic_results:
            fused_results = list(semantic_results)
        else:
            fused_results = list(lexical_results)

        fused_results = apply_recent_filter(fused_results, args.date_from, args.date_to)
        if fused_results and (mode_used in {"hybrid", "semantic"} or args.temporal):
            fused_results = temporal_relational_rerank(
                fused_results,
                query=args.query,
                limit=max(args.limit * int(profile["retrieval"]["fusion_multiplier"]), profile["retrieval"]["rerank_limit"]),
            )
        fused_results = diversify_results(fused_results, limit=max(args.limit, profile["retrieval"]["rerank_limit"]), per_source_cap=int(profile["retrieval"]["diversity_cap"]))

        if not fused_results and lexical_error is None:
            fallback_type = "learning" if args.mode == "learning" else args.memory_type
            try:
                fallback_results = grep_fallback(args.query, memory_type=fallback_type, limit=args.limit, reviewed_only=args.reviewed_only)
            except Exception as e:
                fallback_error = str(e)
                warnings.append(f"fallback retrieval unavailable: {e}")
            else:
                if fallback_results:
                    fused_results = fallback_results[: args.limit]
                    warnings.append("using grep fallback")
                    degraded_reasons.append("using grep fallback")

        degraded = bool(degraded_reasons)
        authority_surface = summarize_authority_surface(fused_results, query=args.query, mode_used=mode_used, degraded=degraded)
        fused_results = authority_surface["results"]
        if authority_surface["requires_low_authority_warning"]:
            warnings.append("low-authority matches only; results are heuristic/fallback and not confirmed memory truth")

        retrieval_stack_unavailable = lexical_error is not None and (fallback_error is not None or args.mode in {"exact", "recent", "learning"}) and (semantic_error is not None or args.mode in {"semantic", "hybrid"}) and not fused_results

        if retrieval_stack_unavailable:
            log_retrieval_event(
                'retrieval_stack_unavailable',
                query_text=args.query,
                mode_requested=args.mode,
                mode_used=mode_used,
                degraded=True,
                payload={'warnings': warnings},
            )
            payload = {
                "query": args.query,
                "mode_requested": args.mode,
                "mode_used": mode_used,
                "degraded": True,
                "warnings": warnings,
                "index_fresh": bool(state.get("lexical_last_indexed_at")) and not index_stale,
                "index_stale": index_stale,
                "semantic_fresh": bool(state.get("semantic_last_indexed_at")) and not semantic_stale,
                "semantic_stale": semantic_stale,
                "semantic_ready": semantic_ready,
                "host_profile": profile,
                "results": [],
                "error": "retrieval stack unavailable",
            }
            if args.json:
                print(json.dumps(payload, ensure_ascii=False, indent=2))
            else:
                print(f"mode_requested: {payload['mode_requested']}")
                print(f"mode_used: {payload['mode_used']}")
                print("degraded: true")
                print("error: retrieval stack unavailable")
            return 3

        relation_hits = sum(1 for item in fused_results if item.get('relation_hits'))
        if not fused_results:
            log_retrieval_event(
                'retrieval_miss',
                query_text=args.query,
                mode_requested=args.mode,
                mode_used=mode_used,
                degraded=degraded,
                payload={'warnings': warnings},
            )
        else:
            if relation_hits:
                log_retrieval_event(
                    'relation_traversal_hit',
                    query_text=args.query,
                    mode_requested=args.mode,
                    mode_used=mode_used,
                    degraded=degraded,
                    payload={'hits': relation_hits},
                )
            if any((item.get('freshness_days') or 0) > 90 for item in fused_results):
                log_retrieval_event(
                    'stale_success',
                    query_text=args.query,
                    mode_requested=args.mode,
                    mode_used=mode_used,
                    degraded=degraded,
                    payload={'results': len(fused_results)},
                )
        payload = {
            "query": args.query,
            "mode_requested": args.mode,
            "mode_used": mode_used,
            "degraded": degraded,
            "warnings": warnings,
            "index_fresh": bool(state.get("lexical_last_indexed_at")) and not index_stale,
            "index_stale": index_stale,
            "semantic_fresh": bool(state.get("semantic_last_indexed_at")) and not semantic_stale,
            "semantic_stale": semantic_stale,
            "semantic_ready": semantic_ready,
            "host_profile": profile,
            "skill_recall_gate": skill_gate,
            "skill_candidates": skill_candidates,
            "authoritative_result_present": authority_surface["authoritative_result_present"] if fused_results else False,
            "low_authority_only": authority_surface["low_authority_only"] if fused_results else False,
            "results": fused_results[: args.limit],
            "hot_recovery": hot_bundle,
        }

        if args.json:
            print(json.dumps(payload, ensure_ascii=False, indent=2))
        else:
            print(f"mode_requested: {payload['mode_requested']}")
            print(f"mode_used: {payload['mode_used']}")
            print(f"degraded: {str(payload['degraded']).lower()}")
            if warnings:
                print("warnings:")
                for w in warnings:
                    print(f"- {w}")
            print(f"authoritative_result_present: {str(payload['authoritative_result_present']).lower()}")
            print(f"low_authority_only: {str(payload['low_authority_only']).lower()}")
            if hot_bundle and hot_bundle.get("selected"):
                print("hot_recovery:")
                print(f"- truth_note: {hot_bundle.get('truth_note')}")
                for idx, item in enumerate(hot_bundle.get("selected", []), 1):
                    print(f"{idx}. [hot-change-buffer] {item.get('action_type','unknown')} :: {item.get('command_or_patch_summary','').strip()}")
            print("results:")
            for idx, item in enumerate(payload["results"], 1):
                print(f"{idx}. [{item.get('memory_type','unknown')}] {item.get('source_path','?')}")
                print(f"   authority: {item.get('match_authority','unknown')}")
                print(f"   {item.get('snippet','').strip()}")

        if hot_bundle and hot_bundle.get("selected"):
            return 2 if degraded else 0
        if payload["results"]:
            return 2 if degraded else 0
        if skill_candidates:
            return 2 if degraded else 0
        return 1
    except Exception as e:
        emit_error(f"internal error: {e}", as_json=("--json" in sys.argv))
        return 5


if __name__ == "__main__":
    raise SystemExit(main())


path: references/architecture.md
content: # super_memori v4 — Architecture

## Objective
Operate a current-generation local-only memory system for OpenClaw agents where files are canonical truth, retrieval can be lexical/semantic/hybrid, degraded host states remain honest, and weak models still face a compact interface.

## Canonical layers
1. Markdown/files in `memory/` are the source of truth.
2. SQLite FTS5 is the lexical / metadata index.
3. Qdrant is the semantic index.
4. Optional CPU reranker improves final ranking but is not required for core operation.
5. Host-profile shaping is derived, never canonical: it adjusts chunking, candidate budgets, audit depth, and truth surfaces, but it never replaces file truth.

## Why this architecture
- Files survive index corruption and remain readable to humans.
- SQLite FTS5 handles exact/path/tag/command recall in the active runtime.
- Qdrant backs semantic recall and filtered vector retrieval when the host is equipped.
- Weak models perform best when the retrieval logic stays hidden behind a small CLI surface.

## Runtime pipeline now implemented in code
```text
query
  → hard filters
  → lexical retrieval (FTS5)
  → semantic retrieval when local semantic stack is ready
  → reciprocal-rank fusion
  → temporal / relation-aware rerank
  → dedupe/diversity pass
  → result bundle + warnings + host-state truth
```

Host-profile shaping is applied before this flow chooses budgets and audit depth:
- `standard` for smaller hosts and conservative operation
- `max` for EPYC-class local hosts with large RAM and enough CPU

The host profile changes candidate budgets and audit depth, not the public command surface.

## Storage model
- Canonical memory remains file-based.
- Chunk metadata lives in SQLite.
- Embeddings live in Qdrant.
- Queue/backlog state lives locally on disk.

## Required degraded modes
- Qdrant down or semantic host stack unavailable → lexical-only mode with warning
- semantic-unbuilt host state → lexical runtime remains valid; semantic path reported unavailable
- stale vectors → lexical or partial hybrid with warning until rebuild
- lexical DB damaged → emergency grep fallback if possible
- broken relation targets → maintenance/audit warning; new writes must reject them

## Target environment
- Ubuntu
- CPU-only host
- OpenClaw agent controlled by weaker models
- Local-first operation with no mandatory external APIs

## Non-goals for the current v4 line
- graph database first
- autonomous self-rewrite of memory policy
- cloud-backed memory services
- many public scripts for weak models
- canonical storage inside vector DB


path: references/command-contracts.md
content: # super_memori v4 — Command Contracts

## Public commands
The active v4 weak-model public interface remains exactly four commands:

1. `query-memory.sh`
2. `memorize.sh`
3. `index-memory.sh`
4. `health-check.sh`

## `query-memory.sh`
### Purpose
Retrieve memory by exact, semantic, hybrid, recent, or learning-oriented query.

### Target flags
- `--mode auto|exact|semantic|hybrid|recent|learning`
- `--type episodic|semantic|procedural|learning|buffer|all`
- `--json`
- `--limit N`
- `--from DATE`
- `--to DATE`
- `--tags a,b,c`
- `--reviewed-only`

### Active-contract scope note
- `--mode semantic` and `--mode hybrid` are now real implemented runtime modes.
- For normal weak-model/runtime use, still prefer `auto`, `exact`, `learning`, or `recent`; use direct `semantic` / `hybrid` mainly when the task explicitly requires that maintenance/runtime check.
- `mode_used` remains authoritative over `mode_requested` for what actually executed on the current host.

### Target exit codes
- `0` results found without degraded execution for the current request
- `1` no results found; check `degraded` and `warnings[]` to distinguish a clean miss from a degraded no-results outcome
- `2` degraded mode with usable results returned
- `3` retrieval stack unavailable
- `4` bad arguments
- `5` internal error

### Required output fields
- `mode_requested`
- `mode_used`
- `degraded`
- `warnings`
- `index_fresh`
- `authoritative_result_present`
- `low_authority_only`
- `results`

### `mode_used` honesty rule
- If `--mode learning` was requested, `mode_used` must remain `learning` so downstream automation can tell intent from the underlying lexical engine.
- `mode_used=exact` is reserved for the normal exact/lexical retrieval path and for degraded `semantic` / `hybrid` requests that fall back to lexical search.

### Degraded vs informational warnings
- `warnings[]` may include informational host-state notes that do not by themselves force `degraded=true`.
- `degraded=true` is reserved for cases where the requested retrieval path became partial, stale, or fallback-backed for the current query.
- `authoritative_result_present` indicates whether at least one returned result still counts as confirmed exact/hybrid memory under the current runtime contract.
- `low_authority_only` indicates that every returned result is only semantic/fallback heuristic support rather than confirmed exact/hybrid memory.
- Informational notes such as "learning mode ran on lexical search on this host" may appear in `warnings[]` while `degraded=false` if the request was satisfied exactly as designed.
- `exit 2` must be interpreted from the query response itself (`degraded=true` plus returned `warnings[]`/results), without delegating to any separate health-state branch.
- `exit 1` does not by itself mean the host was fully healthy; it means no results were returned. Weak models must inspect `degraded` and `warnings[]` first, then frame the result as either a clean miss (`degraded=false`) or a degraded no-results outcome (`degraded=true`).
- A clean miss (`degraded=false`) does not cancel freshness-relevant warning notes. If `warnings[]` still reports queue/backlog delay, stale lexical index, or similar recent-changes-may-be-missing conditions, the response should keep the clean-miss wording but append a short caution that recent changes may not yet be reflected.

### Degraded authority rule
- `--mode learning` remains the learning-memory-oriented retrieval lane. It may still execute on a degraded lexical path on weaker/degraded hosts, but it no longer implies that the whole skill is only a lexical-first baseline.
- If `mode_requested=auto` on a degraded host, `auto` remains valid because the script may degrade internally to the lexical path.
- If semantic dependencies are unavailable but lexical freshness remains OK, lexical/index-backed results remain authoritative for exact/path/time-style retrieval only; they must not be described as semantic or meaning-based retrieval.
- If `authoritative_result_present=false` and `low_authority_only=true`, returned matches must be framed as heuristic/fallback support only, not as confirmed memory truth.
- If lexical freshness is stale but semantic dependencies are otherwise available, indexed results remain usable but freshness-limited; they must not be described as fully current until `./index-memory.sh` is run.
- If lexical freshness is stale and semantic dependencies are unavailable at the same time, rely only on the degraded results surfaced by `query-memory.sh` itself.
- In that double-degraded state, returned matches must be interpreted as file-derived fallback matches only, not fresh indexed truth and not semantic matches.
- Do not invent direct manual file-reading steps outside the four public commands; the public degraded path remains `query-memory.sh` plus its explicit warnings.
- Queue/backlog WARN states require explicit freshness caution but do not by themselves remove read/query authority.
- The operator warning for the double-degraded state must explicitly say: `Missing recent changes and meaning-based matches. Run ./index-memory.sh and restore semantic prerequisites to resolve.`

## `memorize.sh`
### Purpose
Write a useful learning record that can improve future behavior.

### Target types
- `error`
- `correction`
- `lesson`
- `insight`

### Actual exit codes
- `0` learning written successfully, or exact duplicate safely skipped
- `4` bad arguments
- `5` internal error

### Output contract
- Human-readable mode may print either `written: ...` / `queued: ...` or `duplicate skipped: ...`
- JSON mode must include at least `status` plus the target `file`
- `status=written` and `status=duplicate` are both successful non-error outcomes and share exit code `0`
- Weak models must not invent separate failure meanings for duplicates; inspect JSON `status` when the distinction matters

### Rule
Do not auto-log every failure. Only write when the event teaches future behavior.

## `index-memory.sh`
### Purpose
Manage lexical and semantic index maintenance.

### Target modes
- `--incremental`
- `--full`
- `--rebuild-fts`
- `--rebuild-vectors`
- `--stats`
- `--vacuum`
- `--audit`

### Actual exit codes
- `0` requested action completed without warnings
- `2` completed with warnings or expected degraded conditions
- `3` storage/runtime failure during index maintenance
- `4` bad arguments
- `5` internal error

### Required output fields
- `actions`
- `warnings`
- mode-specific fields such as `lexical_entries`, `lexical_chunks`, `db_path`, `state`, `semantic`, and `audit` when `--stats` is used

### Interpretation rule
- `warnings[]` is authoritative for whether the maintenance run completed only partially or with degraded follow-up obligations.
- `--rebuild-vectors` may legitimately return `2` when semantic rebuild was requested but not executed because Qdrant or semantic dependencies are unavailable.
- Weak models must not treat `2` as a clean success; they must report the warning state explicitly.

## `health-check.sh`
### Purpose
Verify the health of canonical files, indexes, freshness, backlog, semantic host state, and integrity drift.

### Actual exit codes
- `0` overall status `OK`
- `2` overall status `WARN`
- `3` overall status `FAIL`
- `4` bad arguments
- `5` internal error

### Exit-code mapping rule
- `0` maps to the public `OK` branch
- `2` maps to the public `WARN` branch
- `3` maps to the public `FAIL` branch
- `4` and `5` are execution errors, not health states

### Required output contract
Human-readable mode must emit:
- leading `status: <OK|WARN|FAIL>` line
- one `- <check_name>: <ok|fail> (<detail>)` line per check
- `warnings:` block only when warnings exist

JSON mode must emit:
- `status`
- `warnings`
- `checks[]`

### Structured warning rule
- Mandatory degraded-mode notices must be represented inside the structured `warnings[]` payload in JSON mode rather than breaking JSON with extra free text.
- Human-readable mode may render the same notices as plain warning lines in stdout.

### Check/interpretation rule
- `health-check.sh` summarizes degraded state through explicit status plus named checks/warnings; weak models must not infer freshness or semantic readiness from unrelated raw logs.
- Different degraded causes share the same `WARN` exit code; automation should use the named checks/warnings payload to distinguish stale lexical index, semantic dependency loss, semantic-unbuilt host state, queue backlog, broken relations, or other degraded-but-usable states.

## `audit-memory.sh` (maintenance-only)
### Purpose
Audit lexical/semantic integrity drift without expanding the weak-model public command surface.

### Actual exit codes
- `0` audit clean
- `2` drift/warn state found
- `4` bad arguments
- `5` internal error

### Expected semantics
- `vector_state=semantic-unbuilt` means semantic vectors are simply not built on this host yet; this is a host-state warning, not corruption by itself
- `vector_state=stale-vectors` means lexical truth has outrun vector rebuild and hybrid quality is degraded until rebuild
- `orphan_vectors` and `orphan_chunks` are true integrity drift signals
- `broken_relations` means relation-target discipline was violated or references point at missing canonical targets

### Status model
- `OK`
- `WARN`
- `FAIL`


path: references/full-hybrid-mode.md
content: # super_memori — Full Hybrid Mode

## Purpose
Define the **stable full-hybrid target state** of `super_memori` where lexical, semantic, fusion, rerank, freshness, relation-aware scoring, repair semantics, and health all operate as one coherent local-memory subsystem.

This file is intentionally a **stable-release target contract**, not a claim that the current host already runs all layers. The current line may implement these features in code while still running in degraded host state.

## Full Hybrid Definition
A stable full-hybrid `super_memori` run means:
1. canonical files are readable
2. lexical registry/index is healthy
3. semantic backend is healthy
4. embeddings can be generated locally
5. lexical + semantic candidates are fused
6. temporal / relation-aware rerank operates correctly
7. degraded states are surfaced explicitly
8. repair/audit semantics distinguish semantic-unbuilt from true drift

## Target runtime flow
```text
query-memory.sh --mode auto "query"
  → normalize query
  → apply hard filters
  → lexical retrieval (SQLite FTS5)
  → semantic retrieval (Qdrant)
  → fuse candidate sets
  → optional rerank (top-N only)
  → dedupe/diversify
  → return results + warnings + freshness
```

## Full-Hybrid Readiness Checklist
A host is only in full-hybrid mode if **all** are true:
- `health-check.sh` reports lexical DB healthy
- `health-check.sh` reports Qdrant reachable
- embedding dependencies are installed
- vector collection exists and is populated
- lexical freshness is current
- semantic freshness is current
- backlog queue is within threshold
- `query-memory.sh --mode hybrid --json` returns `mode_used=hybrid` without semantic degradation warnings

## Semantic Layer Requirements
The semantic layer in the v4 candidate line supports:
- local embeddings on CPU
- vector upsert/update
- vector search with payload filters
- freshness visibility
- explicit degraded mode when unavailable
- a fixed embedding contract (dimension + normalization + rebuild rules)

## Fusion Contract
The skill should eventually implement one deterministic fusion method:
- reciprocal rank fusion, or
- weighted lexical + semantic merge

Default recommendation:
- lexical candidates: top 20
- semantic candidates: top 20
- rerank candidates: top 10 after fusion
- if semantic returns no usable candidates, continue lexical-only and warn

This must happen inside `query-memory.sh`, not inside model reasoning.

## Rerank Requirements
Reranking is optional for baseline operation but required for full-hybrid quality mode.

Rules:
- rerank only small candidate sets
- skip rerank for exact/path-heavy queries
- do not block the whole query if reranker is unavailable
- surface reranker degradation as a warning, not a silent failure

## Stable-release validation order
Before promoting the current candidate line to a stable full-hybrid release:
1. run `validate-release.sh`
2. verify lexical baseline still passes
3. verify semantic dependencies/model are installed on at least one target host
4. verify vector collection exists and is populated on that host
5. verify semantic freshness reporting
6. verify hybrid query output on the semantic-ready host
7. verify temporal / relation-aware rerank on the semantic-ready host
8. only then consider stable promotion

## Acceptance Criteria for Full Hybrid Mode
A stable release can claim full hybrid mode only if all are true:
- lexical search works
- semantic search works
- hybrid search works
- health check distinguishes lexical-only, semantic-unbuilt, and hybrid-ready states
- queue/backlog is monitored
- stale semantic state is surfaced
- repair/audit semantics are validated
- at least one live semantic-ready host proves hybrid mode selected automatically
- documentation matches the actual runtime behavior

## Anti-Patterns
Do not call the skill full-hybrid if any of these are true:
- semantic dependencies are missing
- vector collection exists but is stale and unreported
- reranker is required for all queries
- the model must manually choose lexical vs semantic backend
- degraded states are only visible in logs, not in command output


path: references/health-model.md
content: # super_memori v4 — Health Model

## Core health checks
These determine `OK/WARN/FAIL`.
1. canonical files readable
2. lexical DB openable
3. FTS query works
4. semantic backend reachable
5. local embedding model readiness
6. queue backlog within threshold
7. lexical freshness age acceptable
8. integrity audit state acceptable for the current host profile

## Quality checks
These are important, but do not alone define system health.
9. orphaned metadata/chunks below threshold
10. duplicate growth under control
11. pending review load acceptable
12. semantic-unbuilt vs stale-vectors vs orphan-vectors state differentiated correctly

## Health statuses
- `OK` — current host profile is healthy for the capabilities it can truthfully activate
- `WARN` — degraded but usable, or semantic-ready features remain unavailable on this host
- `FAIL` — reliable lexical retrieval is not available
- `EQUIPPED` — host snapshot matches the local MAX PROFILE threshold; this is a truth surface, not a health status

## Freshness policy
- lexical stale: canonical files changed after last lexical index time
- semantic stale: canonical files changed after last semantic index time
- warning threshold: 24 hours stale
- critical threshold: 7 days stale
- stale results should be returned with warnings rather than silently hidden

## Freshness fields
- lexical freshness
- semantic freshness
- queue backlog age
- last successful full build
- last successful incremental update

## Rule
If users or agents are likely to make wrong decisions because memory is stale, degraded, semantically unbuilt, structurally drifted, or running on a lower-than-target host profile, health must say so explicitly. Do not collapse semantic-unbuilt host state into fake corruption, do not collapse real orphan drift into a harmless degraded warning, and do not confuse `standard` host shaping with `max` host shaping.


path: references/implementation-order.md
content: # super_memori — Implementation Order for Weak Models

## Goal
Provide a deterministic sequence that a weaker model can follow later to finish the remaining hybrid-memory implementation safely.

## Order

### Step 1 — Check baseline
Run:
```bash
health-check.sh --json
index-memory.sh --stats --json
query-memory.sh --json "test"
```
Do not start semantic work until lexical baseline is healthy.

### Step 2 — Verify semantic prerequisites
Confirm:
- Qdrant reachable
- embedding dependencies installed
- vector collection configured
- host has enough disk/RAM for local embeddings

### Step 3 — Implement semantic indexing path
Add a deterministic path from canonical files → embeddings → Qdrant upsert.
Do not change canonical storage.

### Step 4 — Add semantic freshness state
Track:
- last semantic index build
- semantic queue backlog
- semantic stale age

### Step 5 — Implement hybrid retrieval
Inside `query-memory.sh`:
- lexical retrieve
- semantic retrieve
- fuse
- return one result bundle

### Step 6 — Verify degraded behavior
Simulate or observe:
- semantic unavailable
- lexical still healthy
- warnings surfaced correctly

### Step 7 — Add reranker last
Only after hybrid retrieval is stable.
Reranker is a quality layer, not a prerequisite.

### Step 8 — Update docs last
Only after runtime behavior is verified.
Never lead with docs when the mechanism is not ready.

## Exit rule
If any step fails, stop and fix that layer before moving forward.
Weak models must not skip forward into a more complex stage.


path: references/learning-improvement.md
content: # super_memori v4 — Learning Improvement Loop

## Purpose
Turn repeated failures, corrections, reuse signals, and recurring retrieval misses into reviewable manual-promotion candidates without pretending that promotion is fully automatic.

## Why this exists
Learning memory is useful only if repeated signals become visible.
A flat pile of lessons is not enough.
This workflow aggregates patterns so a human or strong maintenance run can decide what deserves durable manual promotion.

## Maintenance workflow
1. Run `health-check.sh`.
2. Confirm rollback exists.
3. Run `python3 mine-patterns.py` from the skill root.
4. Review the generated pattern report.
5. Promote only patterns that are repeated, specific, and not already covered.
6. Re-index memory after approved manual promotions.

## Recommended review cadence
On a CPU-rich host, run `python3 mine-patterns.py` proactively:
- after every 50 new learning entries
- at least once per week during active development
- before any major version bump of `super-memori`

If two consecutive reports show no new actionable clusters, the cadence may be reduced.

## What `mine-patterns.py` and telemetry surfaces should surface
- repeated failures by normalized block signature
- repeated user corrections
- recurring missing-knowledge queries
- lessons that were reused several times
- stale learnings that no longer match current practice
- likely duplicates that should be merged manually
- retrieval-audit signals such as recurring lexical-only fallbacks, domain-specific miss clusters, or index-fragmentation hints
- positive reuse signals that show which procedures keep working and may deserve durable promotion
- local telemetry signals such as `retrieval_miss`, `stale_success`, and `relation_traversal_hit`

## Recommended promotion threshold
Use promotion review only when at least one is true:
- the same lesson pattern appears 3+ times
- the same correction changes future behavior repeatedly
- the same retrieval miss appears across multiple sessions
- the same reusable procedure is successfully referenced multiple times

## Safe outputs
The pattern report should include:
- cluster id
- frequency
- first seen / last seen
- active window days
- representative title
- suggested target (`procedural`, `semantic`, `lessons`, or `review-only`)
- confidence note
- source file list
- retrieval audit signal (`none`, `lexical-fallback`, `miss-cluster`, `index-fragmentation`, or `review-needed`)
- reuse signal (`none`, `low`, `moderate`, or `high`)
- recency weight (`current`, `cooling`, or `stale`)
- stale success candidate flag
- signature when available

## Retrieval-audit handoff
Use the pattern report plus telemetry surfaces for two parallel review tracks:
1. promotion-candidate review for durable memory
2. retrieval-quality review for `index-memory.sh`, `health-check.sh`, repair classification, and degraded-mode diagnostics

The pattern report and telemetry surfaces remain review-only.
They may recommend retrieval-quality investigation, but they must not silently rewrite retrieval scripts or health thresholds.

## Safety rules
- Do not auto-promote directly from the pattern report.
- Do not delete source learnings automatically.
- Do not claim a pattern is durable truth just because it is frequent.
- Prefer reviewable reports over silent mutation.
- When a stale success candidate appears, manually verify that the procedure or lesson still works before promoting or retaining it as current best practice.

## Host assumption
This workflow assumes a strong CPU-only local host.
Use CPU-heavy batch analysis when it improves clustering or deduplication quality.
Do not pretend GPU-only paths are required.


path: references/maintenance.md
content: # Maintenance Reference — Super Memori v4

> ⚠️ **This file is extracted from the SKILL.md `<details>` maintenance block.**
> Human maintainers only. Weak models must not use this during normal operation.

<details>
<summary>⚠️ MAINTENANCE — DO NOT EXPAND DURING NORMAL USE — HUMAN USE ONLY ⚠️</summary>

> ⚠️ SCOPING DISCLAIMER: All text inside this block is frozen maintenance context at packaging time (`v4.0.0-candidate.23`). It does not override the active operating contract, health gates, or exit-code rules above. Weak models must treat it as historical/reference material, not live runtime truth.

## Execution Notes

> ⚠️ This block is HUMAN / MAINTENANCE ONLY. Weak models must not expand it during normal operation.
> ⚠️ All content below is frozen maintenance reference captured at `v4.0.0-candidate.23`. Do not treat it as active operating contract.
> Active mode selection, health gates, and execution rules are fully defined in the Runtime Capability Matrix and Implemented vs Optional sections above.
> This section contains reference material, future-spec mapping, and migration notes only.

## Core Position

Build memory like an operating system component, not like a demo.

Maintenance assumptions for this skill:
- **Files are canonical truth**
- **SQLite FTS5 handles exact / lexical retrieval**
- **Qdrant handles semantic retrieval when available**
- **A small CPU reranker is optional quality lift, not the foundation**
- **Weak models should stay on the 4-command public interface**

Everything else in this block is maintenance-only reference material for improving the skill rather than using it.

## Retrieval Contract [REFERENCE ONLY — DESCRIBES CURRENT V4 RUNTIME SHAPE, NOT A MANUAL EXECUTION PLAN]

> ⚠️ This describes the current v4 retrieval pipeline shape as implemented inside `query-memory.sh`. Do not reconstruct, simulate, or manually sequence these steps.

The current v4 retrieval path is:

```text
query
  → filters (type, time, tags, namespace)
  → lexical retrieval (SQLite FTS5)
  → semantic retrieval (Qdrant, when available / needed)
  → fusion
  → optional rerank
  → deduplicate / diversify
  → results + warnings + freshness state
```

**Health integration:** `query-memory.sh` already surfaces degraded state in its output. You do not need to run `health-check.sh` before every query. Use `health-check.sh` before major memory surgery, after suspicious behavior, or when you need to confirm whether degraded results are still trustworthy.

## Write / Learning / Change-Memory Contract (target behavior)

Use `memorize.sh` only when the new information is likely to help future runs.

### What learning memory is for
Learning memory is the scratch lane for self-improvement:
- reusable failures
- corrections
- lessons
- recurring anti-patterns
- meaningful capability gaps
- recurring retrieval misses
- repeated success patterns worth operationalizing
- stale or superseded lessons that should be reviewed

Learning memory is not durable truth by default. It must earn promotion after repeated reuse or explicit permanence signals.
Repeated signals should be aggregated into reviewable pattern reports instead of staying as isolated notes.

### Change-memory operational rules
- Treat change-memory as operational truth for agent-made changes, not as a substitute for direct live inspection when exact current machine state is required.
- A minimal hot-change-buffer may retain very recent recovery-only event records for interrupted runs and recent-change recall; it is not canonical truth, not durable truth, and not exact current machine state.
- Do not present `reverted` or `unverified` change records or hot events as active current state.
- Harmless reads must not be logged as change-memory events or hot-buffer events.
- Log only state-changing actions, failed writes, risky cleanup, package/service/config/runtime changes, rollback events, and recent multi-step risky change boundaries.

### Before major memory work
Major work = policy edits, index changes, mass rewrites, command-contract changes, retrying a previously failed memory task, or editing `SKILL.md`, references, or public scripts for this skill.
1. Run `./health-check.sh`. If status is `FAIL`, stop. If status is `WARN`, continue only with explicit degraded-mode awareness and a rollback path.
2. Verify rollback exists (`git status`, backup directory, or untouched canonical files).
3. Run `./query-memory.sh --mode learning --limit 5` and reuse any clearly matching lesson.
4. If learning quality or repeated misses matter, run `python3 mine-patterns.py` before changing promotion policy, retrieval logic, or memory structure.
5. If the pattern report shows stale-success candidates, recurring misses, or related lesson clusters, review them manually before patching or proposing promotion.
Read `references/learning-improvement.md` when you need the full pattern-mining workflow. Purpose: run promotion/retrieval review safely.

### Good candidates
- an unexpected failure with a reusable lesson
- a user correction that changes future behavior
- a better repeatable procedure
- a recurring anti-pattern
- a meaningful knowledge gap

### Bad candidates
- expected no-match results
- one-off noise
- weak guesses
- duplicate lessons already recorded
- `checked, nothing relevant`

### Promotion to durable memory

**DO NOT PERFORM PROMOTION.** Promotion is maintenance-only/manual review in the current v4 candidate line, not an automated runtime command.

For the current skill:
- **Promotion is manual. Do not auto-promote.**
- `memorize.sh` writes stay in learning memory.
- If a learning seems durable, say only: `Learning <summary> appears valuable for later manual promotion.`
- If repeated lessons, corrections, misses, or successful reuse signals appear related, aggregate them through `python3 mine-patterns.py` before proposing manual promotion.
- Use the same pattern report to identify retrieval-quality issues before changing indexing or degraded-mode rules.
- If reuse evidence is stale, verify it manually before treating it as current best practice.

### Durable target mapping (manual review mapping, not active runtime automation)
Promotion into procedural or semantic memory is a manual maintenance layer, not a fifth public command.
No current runtime command performs this promotion automatically.
If a human-approved manual promotion exists later, use this mapping:
- repeatable commands / debugging steps → procedural memory
- anti-patterns / post-mortems → procedural lessons memory
- durable facts / preferences / infrastructure facts → semantic memory
- decisions with rationale → semantic decisions memory

### Anti-patterns
- Do not auto-log every non-zero exit code.
- Do not duplicate the same lesson in multiple learning records.
- Do not promote one-off context into durable memory.
- Do not attempt to promote learnings without human action.
- Do not invent commands for promotion.
- Do not log that nothing relevant was found.

## Health / Freshness Contract

Health is not only about indexes. Health also means the agent can tell whether learning-memory is being used honestly instead of as a dumping ground, whether degraded mode is safe to continue in, and whether the host-side conditions still support trustworthy memory work.

`health-check.sh` currently reports at least:
- canonical files readable
- lexical index status
- semantic index status
- queue backlog
- last successful index update / freshness state
- degraded state

Advanced checks such as duplicate / orphan risk belong to the fuller semantic layer and should not be claimed until runtime support exists.

`query-memory.sh` currently reports at least:
- `mode_requested`
- `mode_used`
- `degraded`
- `warnings[]`
- `index_fresh`
- `results[]`

`--reviewed-only` excludes learning entries marked `- status: pending`. Grep fallback respects this behavior too.

### Memory-safe operation rules
- Follow the `WARN` vs `FAIL` rules from the Memory Health Contract before relying on results.
- Before major memory surgery, confirm a rollback path exists and is accessible (`git status`, backup directory listing, or verified canonical file copies).
- Prefer reversible changes to indexing, health scripts, and retrieval contracts.
- Do not schedule recurring maintenance or audits without explicit approval.

## Current Folder Meaning [MAINTENANCE REFERENCE — ARCHIVED AT v4.0.0-candidate.23; CURRENT STABLE: 4.0.1]

### Active public entrypoints
- `query-memory.sh`
- `memorize.sh`
- `index-memory.sh`
- `health-check.sh`

These implemented the v4.0.0-candidate.23 local-only runtime at packaging time: lexical retrieval is active by default, semantic/hybrid retrieval exists in code, and host state determines whether semantic/hybrid can activate on this machine.
This block is historical packaging context, not the current release-state authority. The active stable line is now `4.0.1`; use `SKILL.md`, `references/release-status.md`, and live command outputs for current truth.
Promotion-to-durable-memory remains maintenance-only/manual review. The stable-host readiness gate described below is archived historical context for the pre-`4.0.1` promotion path, not pending work for the current stable line.

### Maintenance-only entrypoints and support surfaces
- `audit-memory.sh`
- `repair-memory.sh`
- `list-promotion-candidates.sh`
- `validate-release.sh`
- `validate-equipped-host.sh`
- `mine-patterns.py`
- `auto-learner.sh`
- `references/`

These are maintenance-only. Weak models must not open or inspect them during normal query/memorize/index/health operations.
Only read or run them when an explicit maintenance step says `Read ...` or `Run ...`.

### Legacy baseline (`scripts/legacy/` + archive material)
These preserve older v2/v3-era behavior as historical reference only.
They are not the current runtime truth.

### Current references
Detailed v4 architecture, contracts, release-state, and host-readiness surfaces live in the `references/` directory. Human maintainers should consult those files when improving the current stable line, while treating explicitly archived candidate-era sections as historical only.

## Backup / Exposure / Risk Notes

- Files remain the canonical recovery path if indexes degrade or semantic dependencies disappear.
- If the host is remote, exposed, or lightly backed up, prefer plan-first changes over direct edits to memory scripts.
- If backups or snapshots are unknown, assume caution and avoid irreversible cleanup.
- Health guidance for this skill covers memory reliability, not full host hardening. Use `healthcheck` for broader host security decisions.

## How to use this skill today

### If you need current memory behavior
Use the active root commands:
- `query-memory.sh`
- `memorize.sh`
- `index-memory.sh`
- `health-check.sh`

### If you are improving the skill
Do this order:
1. Run `health-check.sh`
2. Review recent learning-memory if the task is major or previously failed
3. Confirm whether rollback exists (git, backup, untouched canonical files)
4. Read `references/architecture.md`
5. Read `references/command-contracts.md`
6. Read `references/migration-plan.md`
7. For learning-quality or promotion-policy work, run `python3 mine-patterns.py` and read `references/learning-improvement.md`
8. Review both promotion candidates and retrieval-audit signals from the pattern report
9. Check whether repeated successful reuse signals change the promotion decision
10. Check whether stale success candidates or cooling clusters require manual re-validation
11. Only then patch or replace scripts
12. Re-run health after the patch

## Design target [MAINTENANCE REFERENCE — HISTORICAL CONTEXT]

The goal is not “the fanciest memory system”.
The goal is:

> **the strongest honest local-only memory skill that weak models can use reliably on a CPU-only Ubuntu OpenClaw host**

That was the packaging bar for the `v4.0.0-candidate.23` candidate line. Current runtime behavior and live health gates override this reference if host state diverges.

## Release interpretation [MAINTENANCE REFERENCE — ARCHIVED]
> ⚠️ ARCHIVED: This promotion path was completed for `4.0.1`. Treat the candidate-era notes below as historical reference, not pending work.

- **Historical v3 line (`3.x`)** = lexical-first baseline with degraded semantic reporting and a smaller runtime claim surface
- **Archived candidate snapshot (`4.0.0-candidate.23`)** = historical v4 candidate state where lexical, semantic, hybrid, temporal-relational, audit, change-memory, and change-audit capabilities existed in code while learning-improvement surfaces remained maintenance-only and host-state activation could still be degraded on a given machine
- **Current stable line (`4.0.1`)** = the first equipped-host-verified stable release state for the current-generation local-only runtime, with current host execution truth still read from live command outputs rather than inferred from the release label alone

## Stable-release gate [HUMAN/MAINTENANCE ONLY — ARCHIVED]

> ⚠️ ARCHIVED: This promotion path was completed for `4.0.1`. Keep this checklist only as historical evidence of the pre-stable gate; do not treat it as pending work for the current stable line.
>
> ⚠️ This is a stable-release verification checklist for already-implemented runtime features on an equipped host. Weak models must not execute, track, or report on these steps.

Do not skip steps **when verifying on an equipped host**.
1. [ ] `health-check.sh --json` shows lexical baseline healthy
2. [ ] semantic prerequisites are installed and verified on an equipped host
3. [ ] semantic / vector indexing for canonical files is verified on an equipped host
4. [ ] semantic freshness / backlog state is visible and healthy on an equipped host
5. [ ] `query-memory.sh --mode hybrid` is verified on an equipped host to fuse lexical + semantic results
6. [ ] optional reranker, if enabled, is healthy on an equipped host and documented honestly
7. [ ] docs, runtime, and health semantics all align before any 4.0.0 stable release

For the short archived promotion instruction, read `references/roadmap-to-4.0.0.md` as historical context only.

</details>
## Maintenance Anti-Patterns (human reference)

When editing this skill, do not:
- claim semantic retrieval is fully active on the current host unless dependencies, local model, vectors, and health checks actually pass
- add more public commands for weak models
- make Qdrant the canonical source
- let degraded lexical-only mode fail silently
- log harmless reads as change-memory events
- auto-log every non-zero exit code as a lesson
- treat repeated failures as isolated one-off notes when they should be aggregated into pattern reports
- ignore retrieval-audit signals when pattern mining shows recurring misses, lexical fallbacks, or fragmentation hints
- ignore repeated successful reuse when deciding whether a procedure or lesson deserves manual promotion review
- treat stale success as current truth without manual re-validation
- auto-promote pattern clusters without human review
- schedule recurring checks or maintenance without explicit approval
- mix architecture notes back into the public command interface



path: references/migration-plan.md
content: # super_memori v4 — Migration / Release-Line Plan

## Current state
The root scripts in this skill folder now implement the v4 candidate runtime line: lexical search, semantic indexing/search, hybrid fusion, temporal-relational rerank, integrity audit, and relation-aware learning writes all exist in code. Older v2/v3 interpretations remain only as historical reference surfaces.

## Migration goal
Finish the shift from the frozen lexical-first historical line to a fully synced current-generation local-memory subsystem with explicit implemented-vs-host-state truth, canonical relation targeting, stronger eval coverage, and publish-honest release evidence.

## Recommended phases

### Phase 1 — Stabilize public interface
- Keep the root public surface at four commands
- Keep lexical retrieval, health, queue, and freshness working
- Stop making stronger semantic claims than implementation supports

### Phase 2 — Introduce lexical registry
- Add SQLite metadata + FTS5
- Keep files canonical
- Route exact/path/tag retrieval through lexical index

### Phase 3 — Finish semantic host-state discipline
- Keep Qdrant as derived semantic backend
- Preserve freshness reporting and degraded-mode warnings
- Distinguish semantic-unbuilt host state from true integrity drift
- Keep user-facing text honest about implemented runtime vs current host state

### Phase 4 — Add quality/ops layer
- Optional CPU reranker
- queue/backlog handling
- stronger health checks
- duplicate/orphan detection
- Follow `implementation-order.md` exactly when weaker models finish the remaining work

## Rule during migration
Never claim a stable `v4.0.0 release` until scripts, contracts, health/audit semantics, and release evidence all match the current runtime truth.


path: references/reference-test-log.md
content: # Reference Test Log — super-memori 4.0.23

> **Evidence model:** `4.0.23` keeps the stable equipped-host validation basis inherited from `4.0.1`, preserves the 2026-04-24 six-round hardening work and the `4.0.9` post-publish truth-sync pass, preserves the now-published `4.0.13` line, preserves the published `4.0.14` release-surface sync-gate line, preserves the `4.0.15` release-gate completeness patch, preserves the `4.0.16` maintenance/evidence gate-completeness patch, preserves the `4.0.17` document-truth / stale-header guard patch, preserves the `4.0.18` maintenance-contract sync patch, preserves the `4.0.19` clean-failure / readability patch, preserves the `4.0.20` execution-anchor correction, preserves the published `4.0.21` release-status clarity correction, preserves the published `4.0.22` startup self-check/self-heal hardening line, and now adds the `4.0.23` enforceable per-session startup-hook line.

## 2026-04-12
Validation set for the current candidate line:
- `python3 -m py_compile skills/super_memori/query-memory.sh skills/super_memori/index-memory.sh skills/super_memori/health-check.sh skills/super_memori/audit-memory.sh skills/super_memori/memorize.sh skills/super_memori/scripts/super_memori_common.py skills/super_memori/mine-patterns.py`
- `python3 skills/super_memori/tests/test_temporal_retrieval.py`
- `python3 skills/super_memori/audit-memory.sh --json`
- `cd skills/super_memori && ./health-check.sh --json`
- `cd skills/super_memori && ./index-memory.sh --stats --json`
- `cd skills/super_memori && ./query-memory.sh "relation-aware memory" --mode auto --json --limit 3`
- `cd skills/super_memori && ./memorize.sh --json --reviewed --refines semantic-spine "invalid relation target test" lesson`
- `cd skills/super_memori && ./query-memory.sh "learning memory" --mode learning --json --limit 3`

Observed interpretation:
- The artifact is no longer merely a lexical-first shell; the runtime now contains a real local semantic/hybrid/temporal/audit spine.
- On the 2026-04-12 validation host snapshot, host reality was still degraded because semantic dependencies/model and built vectors were absent on that machine.
- `query-memory.sh --mode learning` still runs as a learning-oriented lane over the current runtime and reports host-specific execution honestly.
- `audit-memory.sh` distinguishes `semantic-unbuilt` host state from stronger integrity drift categories instead of collapsing everything into false `ok` or fake corruption.
- New freeform relation targets are now rejected at write time; remaining broken-relation findings are historical residue from earlier pre-canonical writes.
- The strongest remaining work after this historical sync pass was equipped-host stable-release validation, not contract drift cleanup.

Validation evidence was originally captured at `4.0.0-candidate.12`; the runtime contract advanced through `4.0.1` without breaking capability regressions.

## 2026-04-20 live validation snapshot (stable 4.0.1)
- `cd skills/super_memori && ./health-check.sh --json`
- `cd skills/super_memori && python3 audit-memory.sh --json`
- `cd skills/super_memori && ./validate-release.sh --strict`
- `cd skills/super_memori && ./tests/regression/run-all.sh`

Observed interpretation:
- The current validation host is semantic-ready for this candidate line (`semantic_ready=true`, `semantic_vectors=4`, `qdrant_collection points_count=4`).
- Integrity audit currently reports `status=ok`, `vector_state=ok`, no orphan chunk/vector drift, and no broken relations.
- `skill_operational_memory` is now clean/current after refresh (`status=ok`, no stale/changed skill tail).
- `agent_change_memory` routine maintenance noise is now reconciled truthfully (`status=ok`, `records=1`, no duplicate/unverified/interrupted tail).
- The pre-patch health WARN was a false positive caused by `health-check.sh` trusting `indexed_vectors_count` alone instead of falling back to `points_count` when the former is zero.
- Current remaining release gate is still stable-host promotion, not candidate-line semantic readiness honesty.

Additional rerun evidence on 2026-04-12:
- a fresh full 3-cycle / 18-round Dual Thinking rerun from the published `4.0.0-candidate.11` baseline completed honestly
- accepted fixes in that rerun were limited to three narrow contract-clarity hardenings: lexical-authority revocation in the combined degraded WARN state; removal of misleading `zero-findings` host-state wording; and an exit-code `1` override back to the exact Health & Safety Gate degraded notice for that same combined WARN state
- all later confirmatory rounds converged cleanly with no further accepted fixes

Stable-line conclusion:
- the active line at the time of this validation evidence was `4.0.1`
- stable publication was justified because an equipped host passed the stable-host readiness sequence cleanly, and because `4.0.0` in ClawHub was already occupied by an older historical artifact

## 2026-04-21 local hardening + latency validation snapshot (stable 4.0.4 inherited by 4.0.6)
- `cd skills/super_memori && ./health-check.sh --json`
- `cd skills/super_memori && ./index-memory.sh --full --rebuild-vectors --json`
- `cd skills/super_memori && ./validate-release.sh --strict`
- `python3 benchmarks/locomo/run_benchmark.py --workspace /home/irtual/.openclaw/workspace --dataset /home/irtual/.openclaw/workspace/benchmarks/locomo/examples/hardening_cases.jsonl --modes auto exact semantic hybrid --include-simple-baselines --simple-baseline-modes bm25 vector rag-hybrid`
- direct current-host timing probe for `exact`, `semantic`, `hybrid`, `auto`
- semantic daemon idle check via `/health`

Observed interpretation:
- The benchmark harness hardening from `4.0.3` remains intact: staged benchmark files are still written into canonical episodic memory and the stage-coverage gate passes on the current workspace run.
- Final current-workspace hardening benchmark `benchmarks/locomo/results/20260421T093801.239655Z` returned `answer_hit_at_1=1.0` and `answer_hit_at_5=1.0` for `auto`, `exact`, `semantic`, and `hybrid`.
- Final current-workspace latency on that run was no longer stuck in the old ~11-12s subprocess cold-load regime: `exact=130.4 ms`, `semantic=212.6 ms`, `hybrid=217.2 ms`, `auto=219.2 ms` average.
- Separate direct timing on the current host also showed the daemon-backed warm path behaving as intended: `exact [0.125, 0.124, 0.126]`, `semantic [10.612, 0.246, 0.205]`, `hybrid [0.207, 0.204, 0.234]`, `auto [0.209, 0.207, 0.205]`.
- The semantic daemon safety check stayed within the machine-safety bar during release verification (`/health` reported idle CPU `0.0%` for the daemon process).
- One strict-gate flake was fixed honestly during release work: the daemon surface test now checks stable fast daemon-backed behavior instead of a brittle exact ordering assumption between two near-identical warm calls.
- After a clean sequential reindex/rebuild pass, the current workspace finished with `audit-memory.sh --json` reporting `status=ok`, `vector_state=ok`, no orphan chunk/vector drift, and `./validate-release.sh --strict` passing cleanly.
- The `4.0.5` follow-up release did not change runtime behavior; it corrected release packaging hygiene by restoring the missing `.clawhubignore` file that the packaging checklist already required.
- The `4.0.6` follow-up release also does not change runtime behavior; it hardens the operator/support contract after the 2026-04-22 six-round review.
- The `4.0.8` line also does not change runtime behavior; it restores package-root `.clawhubignore`, advances version truth beyond the already-published `4.0.7` line, resets `_meta.json.publishedAt` for the unpublished line, and re-synchronizes release/support surfaces before the next real ClawHub publish.
- The `4.0.9` line also does not change runtime behavior; it only truth-syncs the support/package surfaces after the real `4.0.8` publish so the packaged artifact no longer describes itself as still unpublished.
- The `4.0.22` line adds startup self-check/self-heal coverage and OpenClaw bootstrap wiring on gateway startup, plus documented session-start reuse guidance.
- The `4.0.23` line adds an actual `agent:bootstrap` workspace hook with a `sessionId` ledger so startup self-check execution is enforceable once per new session on this host; runtime retrieval capability claims remain otherwise unchanged.


path: references/release-status.md
content: # super_memori — Release Status Model

## 3.x meaning
`3.x` is the lexical-first historical line.
That line means:
- public four-command surface
- lexical index baseline
- health check + degraded semantic reporting
- semantic/hybrid mostly treated as future-spec or compatibility surface

It is now a frozen historical interpretation line, not the current runtime truth.

## 4.0.23 meaning
`4.0.23` is the current-generation local-only current candidate line advanced from the previously-published `4.0.22` baseline by adding a real internal OpenClaw workspace hook that listens to `agent:bootstrap`, runs the startup self-check through a host-enforced path, and suppresses repeat execution within the same session via a `sessionId` ledger.
It means the runtime itself can truthfully claim:
- lexical search implemented
- semantic search implemented in code
- vector indexing implemented in code
- hybrid fusion implemented in code
- temporal / relation-aware rerank implemented in code
- integrity audit implemented in code
- pattern-mining / manual-promotion support implemented in maintenance surfaces
- docs distinguish implemented capability from host-state activation
- host-profile shaping can distinguish `standard` from `max` without changing canonical truth

It does **not** mean every host is semantic-ready by default.
A host may still legitimately run `4.0.23` in degraded lexical mode if local semantic prerequisites or vector build state are missing.

## Stable-line interpretation
The current active release line is `4.0.23`.
The previous already-published release was `4.0.22`.
That means:
- the v4 runtime capabilities claimed as implemented are present in code
- the stable equipped-host validation basis inherited from `4.0.1` remains intact through all subsequent patches, including the current `4.0.23` enforceable per-session startup-hook hardening line
- host-state activation may still be degraded on some other machine, and that must still be reported from live command output rather than inferred from the release label
- maintenance-only capabilities remain maintenance-only and are not weak-model runtime commands
- `max` host shaping is only a truthfully detectable runtime optimization on an actually equipped host; it must not be claimed from a smaller machine
- `4.0.22` is the last published stable line before the current candidate release line.
- `.clawhub/origin.json` is local install/update state, not publish truth; use it only as local installation metadata


path: references/retrieval-pipeline.md
content: # super_memori v4 — Retrieval Pipeline

## Default behavior
Use `--mode auto` unless a more specific mode is clearly required.

## Chunking contract
All retrieval layers must work against the same chunking model.
- Target chunk size: ~1200 characters
- Overlap: ~150 characters
- Canonical chunk id: hash of `{entry_id}:{chunk_index}:{chunk_prefix}`
- Chunk source of truth: canonical markdown/file content after frontmatter stripping
- Lexical and semantic indexes must refer to the same chunk granularity

## Embedding contract
The active v4 line standardizes on one CPU-friendly embedding profile unless explicitly changed.
- Default model family: multilingual small embedding model
- Expected vector size is fixed per collection
- Cosine-ready normalization stays consistent across all vectors
- Model changes require explicit reindex/rebuild

## Stage 1 — Hard filters
Apply memory type, time range, tags, namespace, and reviewed-state filters before retrieval.

## Stage 2 — Lexical retrieval
Use SQLite FTS5/BM25 for exact strings, commands, paths, identifiers, tags, and high-precision retrieval.
Default candidate budget: top 20.

## Stage 3 — Semantic retrieval
Use Qdrant for meaning-based recall, paraphrases, and similar-past-problem lookups.
Default candidate budget: top 20.
Semantic candidates below the chosen minimum score should be discarded.

## Stage 4 — Fusion
The active runtime combines lexical and semantic candidates using reciprocal-rank fusion.
If semantic returns no usable candidates, it continues lexical-only and surfaces a warning.

## Stage 5 — Temporal / relation-aware rerank
Rerank only the short candidate list. Skip heavy rerank behavior for exact/path-heavy queries when that would add no value.
Current runtime uses temporal freshness, source confidence, conflict status, and relation metadata when available.

## Stage 6 — Diversity pass
Prevent near-duplicate final results. Prefer a mix of exact, fresh, and authoritative matches.
Default rule: avoid flooding the final set with many chunks from the same source file.

## Stage 7 — Result bundle
Return snippets, source paths, scores, warnings, freshness state, and degraded flags.

## Execution decision tree
```text
query
  → hard filters
  → lexical retrieval (always)
  → semantic stack ready on this host?
      yes → semantic retrieval → fusion → temporal/relation-aware rerank
      no  → warning → lexical-only path
  → diversity pass
  → result bundle
```

## Degraded behavior
- If semantic is unavailable but lexical works, return lexical results and say so.
- If the host is `semantic-unbuilt`, keep lexical runtime authoritative and report that semantic/hybrid cannot activate yet.
- If lexical is unavailable but emergency grep works, return grep fallback and say so.
- Never silently downgrade quality.


path: references/roadmap-to-4.0.0.md
content: # super_memori — Historical Roadmap to 4.0.1 (Archive Only)

> ⚠️ ARCHIVED: This promotion path has already been completed for the current stable `4.0.1` line. Keep this file only as historical evidence of the pre-stable promotion sequence; do not treat it as pending work.

## Purpose
This is the archived short instruction that was used to turn the earlier `super_memori` `4.0.0 candidate` line into the first equipped-host-verified stable full-hybrid runtime release later published as `4.0.1`.

Use this roadmap only when reviewing historical promotion logic, not when inferring the current release state.

## 7-step roadmap

### 1. Freeze and verify the baseline
Run:
```bash
health-check.sh --json
index-memory.sh --stats --json
query-memory.sh --json "deepseek"
```
Do not start hybrid work until lexical baseline is healthy.

### 2. Install and verify semantic prerequisites
Confirm all of these:
- Qdrant reachable
- embedding dependencies installed
- vector collection exists
- local CPU embedding path works

If any prerequisite fails, stop and fix it before moving on.

### 3. Verify semantic indexing for canonical files
The deterministic path from canonical memory files to vector upserts now exists in code.
Do not move canonical truth out of markdown/files.
Verify it on the target host by building vectors successfully.

### 4. Add semantic freshness + backlog visibility
Track and surface:
- last semantic index build
- semantic stale age
- semantic queue backlog
- semantic degraded state

### 5. Verify hybrid retrieval inside `query-memory.sh`
The runtime already does this internally:
- lexical retrieve
- semantic retrieve
- candidate fusion
- temporal / relation-aware rerank
- unified result bundle

Do not make weaker models choose the backend.

### 6. Add reranker last
Only after hybrid retrieval works.
Reranker is a quality layer, not the foundation.
It must degrade safely if unavailable.

### 7. Re-test and only then release the first stable line
That promotion path could be called complete only if all were true:
- lexical search verified
- semantic search verified
- hybrid search verified
- health-check distinguishes healthy hybrid vs degraded mode
- documentation matches runtime reality

## Hard rule
Do not call the skill stable full hybrid just because the code path exists or Qdrant is running.
It is stable full hybrid only when host validation, retrieval, freshness, audit semantics, and docs all agree.
That gate was later satisfied for the stable `4.0.1` line; current host truth must still be taken from live command outputs.


path: references/stable-host-readiness.md
content: # super_memori — Stable Host Readiness

> ⚠️ ARCHIVED GATE: This equipped-host promotion path was satisfied for the current stable `4.0.1` line. Keep this file as historical validation criteria, not as pending release work.

## Purpose
Define the first equipped-host validation path that was required before promoting the earlier `v4.0.0 candidate` line into the first stable full-hybrid release later published as `4.0.1`.

## Required equipped-host conditions
All must be true on at least one local Ubuntu host:
- `sentence-transformers` installed locally
- `numpy` installed locally
- the configured embedding model is available locally (`local_files_only=True` path succeeds)
- local Qdrant reachable
- vector collection built from current lexical chunks
- `health-check.sh --json` shows `semantic_ready=true` or equivalent fully ready semantic state
- `audit-memory.sh --json` does not show orphan drift

## Required validation sequence
1. `./index-memory.sh --incremental --json`
2. `./index-memory.sh --rebuild-vectors --json`
3. `./query-memory.sh "agent memory" --mode hybrid --json --limit 3`
4. `./validate-release.sh --strict`
5. `./scripts/release-prep.sh`

## Required success signals
- `mode_requested=hybrid`
- `mode_used=hybrid`
- `semantic_ready=true`
- no semantic degradation warning in query output
- temporal/relation-aware result shaping still present in result payloads when relevant
- release gate remains green on the equipped host

## Blocking outcomes
For the archived pre-stable promotion path, do not promote to stable if any of these are true:
- embedding model still unavailable locally
- vector rebuild fails
- `mode_used` falls back to lexical on the equipped host
- audit reports orphan drift
- health reports `FAIL`
- docs claim stable full-hybrid behavior before an equipped host has actually passed this sequence


path: references/verification-evidence.md
content: # Verification Evidence — super-memori 4.0.23

> **Evidence model:** `4.0.23` inherits the stable equipped-host validation evidence documented below from `4.0.1`, preserves the 2026-04-24 six-round hardening work, preserves the post-publish truth-sync captured in `4.0.9`, preserves the now-published `4.0.13` truth-sync line, preserves the published `4.0.14` release-surface sync-gate line, preserves the unpublished `4.0.15` release-gate completeness patch, preserves the unpublished `4.0.16` maintenance/evidence gate-completeness patch, preserves the unpublished `4.0.17` document-truth / stale-header guard patch, preserves the unpublished `4.0.18` maintenance-contract sync patch, preserves the unpublished `4.0.19` clean-failure / readability patch, preserves the unpublished `4.0.20` execution-anchor correction, preserves the published `4.0.21` release-status clarity correction, preserves the published `4.0.22` startup self-check/self-heal hardening line, and now adds the unpublished `4.0.23` enforceable per-session startup-hook line.

## Current line classification
- line: `4.0.23`
- evidence basis: inherited stable equipped-host validation from `4.0.1`, preserved `4.0.4` stable runtime evidence, preserved the `4.0.8` hardening work, preserved the `4.0.9` post-publish truth-sync release, preserved the now-published `4.0.13` line, preserved the published `4.0.14` line, preserved the unpublished `4.0.15` patch, preserved the unpublished `4.0.16` patch, preserved the unpublished `4.0.17` patch, preserved the unpublished `4.0.18` patch, preserved the unpublished `4.0.19` patch, preserved the unpublished `4.0.20` patch, preserved the published `4.0.21` patch, preserved the published `4.0.22` patch, and added the unpublished `4.0.23` enforceable per-session startup-hook line
- stable verdict: equipped host passed `./validate-equipped-host.sh` cleanly; stable promotion remains justified for the artifact family.
- reason: runtime architecture has crossed the lexical-first v3 boundary and now includes real local semantic indexing/search, hybrid fusion, temporal-relational rerank, integrity audit, and relation-aware learning writes
- frozen historical line: `3.4.9 release`
- current host state: host-dependent; read the live `./health-check.sh --json` snapshot for the active machine instead of inferring a degraded or semantic-ready state from this file alone
- publish state for this line: current published ClawHub line remains `4.0.22` as of `2026-04-27T04:23:28Z`; `4.0.23` is the current unpublished candidate line and `_meta.json.publishedAt=null` records that unpublished state.
- startup enforcement scope for this line: verified working startup self-check command, verified gateway-start execution path via `boot-md`, and now an actual per-session OpenClaw hook path via `agent:bootstrap` plus a `sessionId` ledger; this line does claim a real enforceable per-session startup hook on this host.

## Accepted structural upgrades in this line
1. Added a real local semantic runtime spine in code:
   - local embedding model loading with `local_files_only=True`
   - semantic readiness/freshness state
   - local Qdrant collection management
   - semantic index rebuild and semantic search
2. Added real hybrid quality logic:
   - reciprocal-rank fusion
   - diversity pass
   - temporal/recency shaping
   - source-confidence weighting
   - conflict-state handling
   - relation-aware rerank pressure
3. Added integrity/audit surfaces:
   - `audit-memory.sh`
   - `index-memory.sh --audit`
   - vector-state classification (`semantic-unbuilt`, `stale-vectors`, `orphan-vectors`)
   - stronger `health-check.sh` integrity visibility
4. Added relation-aware learning writes and evolution metadata:
   - stable signatures
   - `source_confidence`
   - `conflict_status`
   - canonical relation targets
   - rejection of new freeform relation labels
5. Reworked pattern mining from file-level noise toward block-level clustering with relation/conflict/review-status summaries.
6. Reclassified the skill contract and release surfaces onto an honest v4 candidate pre-release line.
7. Fresh 3-cycle / 18-round rerun from `4.0.0-candidate.11` accepted three additional micro-fixes:
   - explicit lexical-authority revocation in the combined stale-index + semantic-unavailable WARN state
   - full removal of the misleading `zero-findings` host-state token from current-host wording
   - explicit exit-code `1` override back to the exact Health & Safety Gate degraded notice for that same combined WARN state

## Historical validation snapshot (2026-04-12 / candidate.12)
- lexical index: working (`lexical_db` and `lexical_fts` were healthy on that validation host snapshot)
- lexical freshness: stale on that validation host snapshot (`lexical_freshness.ok=false`)
- semantic deps/model: missing on that validation host snapshot
- qdrant reachable: yes
- qdrant collection populated: no
- semantic vectors: absent
- semantic host state: degraded / unbuilt
- integrity audit: that validation snapshot reported `status=ok` with `vector_state=semantic-unbuilt`, no orphan chunk/vector drift, and no broken relations

Validation evidence was originally captured at `4.0.0-candidate.12`; the runtime contract later advanced through `4.0.1` without breaking capability regressions.

## Live validation snapshot (2026-04-20 / stable 4.0.1)
- lexical index: working (`lexical_db` and `lexical_fts` are healthy on the current validation host)
- lexical freshness: current (`lexical_freshness.ok=true`)
- semantic deps/model: ready on the current validation host
- qdrant reachable: yes
- qdrant collection populated: yes (`points_count=4`)
- semantic vectors: present (`semantic_vectors=4`)
- semantic host state: semantic-ready on the current validation host (`semantic_ready=true`)
- integrity audit: currently reports `status=ok` with `vector_state=ok`, no orphan chunk/vector drift, and no broken relations
- skill operational memory: currently reports `status=ok`, `stale_skills=[]`, `changed_skills=[]`
- agent change memory: currently reports `status=ok`, `records=1`, `duplicates=[]`, `unverified=[]`, `hot_interrupted=[]`

## Validation evidence recorded so far
### code syntax / runtime compile
Command:
- `python3 -m py_compile skills/super_memori/query-memory.sh skills/super_memori/index-memory.sh skills/super_memori/health-check.sh skills/super_memori/audit-memory.sh skills/super_memori/memorize.sh skills/super_memori/scripts/super_memori_common.py skills/super_memori/mine-patterns.py`
Observed result:
- no syntax failures

### temporal retrieval eval
Command:
- `python3 skills/super_memori/tests/test_temporal_retrieval.py`
Observed result:
- `[OK] temporal retrieval rerank cases passed`

### integrity audit surface
Command:
- `python3 skills/super_memori/audit-memory.sh --json`
Observed result:
- `status=warn`
- `vector_state=semantic-unbuilt`
- no orphan chunks
- no orphan FTS chunks
- missing vectors reported explicitly
- legacy non-canonical relation targets still flagged as broken relation residue

### host health surface
Command:
- `cd skills/super_memori && ./health-check.sh --json`
Observed result:
- `status=WARN`
- warnings reflect semantic host degradation and integrity drift visibility
- lexical DB / FTS healthy
- semantic-ready not claimed

### negative relation-target validation
Command:
- `cd skills/super_memori && ./memorize.sh --json --reviewed --refines semantic-spine "invalid relation target test" lesson`
Observed result:
- exit code `4`
- invalid freeform relation target rejected as intended

## 2026-04-21 hardening + latency validation snapshot (stable 4.0.4 inherited by 4.0.6)
- benchmark harness staging path: canonical `memory/episodic/benchmark-locomo`
- stage coverage gate: added earlier and still passing (`indexed_entries == stage_file_count` on the current benchmark runs)
- final current-workspace hardening benchmark: `benchmarks/locomo/results/20260421T093801.239655Z`
- current-workspace benchmark accuracy: `answer_hit_at_1=1.0`, `answer_hit_at_5=1.0` for `auto`, `exact`, `semantic`, and `hybrid`
- current-workspace benchmark latency: `exact=130.4 ms`, `semantic=212.6 ms`, `hybrid=217.2 ms`, `auto=219.2 ms` average
- direct timing probe on the current host: `exact [0.125, 0.124, 0.126]`, `semantic [10.612, 0.246, 0.205]`, `hybrid [0.207, 0.204, 0.234]`, `auto [0.209, 0.207, 0.205]`
- semantic daemon idle safety check: `/health` returned `200` and `{"pid": 40053, "idle_cpu_percent_of_total": 0.0}` during release verification
- current workspace release gate: `./validate-release.sh --strict` passes after a clean sequential lexical/vector refresh with `audit-memory.sh --json status=ok` and `vector_state=ok`

## Release honesty rule for this line
- The inherited stable validation basis for this line is the `4.0.1` equipped-host release, because an equipped host proved semantic-ready execution via `validate-equipped-host.sh` and the stable-host readiness sequence, and because `4.0.0` was already occupied in ClawHub by an older historical artifact.
- The `4.0.4` patch adds real benchmark/runtime latency hardening on top of the `4.0.3` benchmark harness work, so it is not described as a documentation-only republish; it carries fresh 2026-04-21 local validation plus the inherited equipped-host stable basis above.
- The `4.0.5` patch restored the missing `.clawhubignore` package surface that the packaging checklist already required, without changing runtime capability claims or pretending to add new semantic/runtime behavior.
- The `4.0.6` patch is also narrow: it hardens operator/support surfaces after the six-round 2026-04-22 review, without changing runtime capability claims or pretending to add new semantic/runtime behavior.
- The `4.0.8` line is narrow: it restores the missing `.clawhubignore`, advances version truth without mutating the already-published `4.0.7` line, resets `_meta.json.publishedAt` for the unpublished line, and re-synchronizes release/support surfaces without changing runtime capability claims.
- The `4.0.9` line is narrower still: it updates support surfaces and package metadata to match the real already-completed `4.0.8` publish, rather than leaving pre-publish wording in the post-publish artifact.
- `.clawhub/origin.json` remains local install/update metadata only; it is not publish evidence.
- Historical `3.x` remains historical only; it is no longer the current runtime truth.
- Stable publication remains host-truth-bound: current host readiness or degraded state on any future machine must still be taken from live command output, not inferred from historical evidence blocks in this file.


path: references/weak-model-guidance.md
content: # super_memori v4 — Weak Model Guidance

## Why this file exists
The target operator is a weaker model. The interface must be safe under literal, uneven execution.

## Rules
1. Expose only four public commands.
2. Hide backend choice behind `query-memory.sh`.
3. Prefer `--mode auto` by default and trust `mode_used` for what actually executed.
4. Use explicit exit codes and explicit warnings.
5. Do not require the model to interpret index freshness from raw logs.
6. Do not use absolute wording like "never grep manually" unless it is truly always correct.
7. Degraded mode is valid operation, but must be surfaced clearly.
8. `semantic` and `hybrid` are real runtime modes now; weak models should still default to `auto` unless the task explicitly requires forcing them.
9. Do not invent freeform relation targets in `memorize.sh`; relation links must use canonical forms such as `learn:<signature>`, `chunk:<chunk_id>`, or `path:<canonical_path>`.

## Good pattern
"Run `query-memory.sh --json <query>` and use the returned warnings/results."

## Bad pattern
"Decide whether to use grep, vectors, or direct file scans based on intuition."


path: repair-memory.sh
content: #!/usr/bin/env python3
# Exit codes: 0=repair plan or action completed, 2=repair recommended / partial, 3=unsafe or unavailable, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import shutil
import subprocess
import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / 'scripts'))
from super_memori_common import LEARNINGS_DIR, audit_memory_integrity, read_state, semantic_runtime_status

ROOT = Path(__file__).resolve().parent
BACKUP_DIR = ROOT / 'backups'


class RepairArgumentParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def run(cmd: list[str]) -> tuple[int, str, str]:
    proc = subprocess.run(cmd, capture_output=True, text=True)
    return proc.returncode, proc.stdout, proc.stderr


def classify_state(audit: dict, semantic: dict) -> dict:
    continue_ok = []
    rebuild = []
    rollback = []
    if audit['vector_state'] == 'semantic-unbuilt':
        continue_ok.append('lexical runtime remains valid; semantic stack not built on this host yet')
    if audit['vector_state'] == 'stale-vectors':
        rebuild.append('run vector rebuild before trusting hybrid quality')
    if audit['orphan_chunks'] or audit['orphan_fts_chunks'] or audit['orphan_vectors']:
        rollback.append('storage drift/orphans detected; inspect backups before further mutation')
    if audit['broken_relations']:
        rebuild.append('migrate or remove broken relation targets in learning files')
    if not semantic['model_ready']:
        continue_ok.append('semantic rebuild cannot run until local deps/model exist')
    return {'continue': continue_ok, 'rebuild': rebuild, 'rollback': rollback}


def backup_file(path: Path) -> Path:
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    stamp = time.strftime('%Y%m%dT%H%M%S')
    target = BACKUP_DIR / f'{path.name}.{stamp}.bak'
    shutil.copy2(path, target)
    return target


def migrate_relations() -> dict:
    changed = []
    backups = []
    for path in sorted(LEARNINGS_DIR.glob('*.md')):
        raw = path.read_text(encoding='utf-8', errors='ignore')
        new = raw
        new = new.replace('- refines: semantic-spine', '- refines: path:/home/irtual/.openclaw/workspace/skills/super_memori/scripts/super_memori_common.py')
        new = new.replace('- extends: retrieval-quality', '- extends: path:/home/irtual/.openclaw/workspace/skills/super_memori/query-memory.sh')
        if new != raw:
            backup = backup_file(path)
            path.write_text(new, encoding='utf-8')
            changed.append(str(path))
            backups.append(str(backup))
    return {'changed_files': changed, 'backups': backups}


def rebuild_safe() -> dict:
    audit = audit_memory_integrity()
    actions = []
    if audit['orphan_chunks'] or audit['orphan_fts_chunks'] or audit['orphan_vectors']:
        return {'status': 'unsafe', 'reason': 'orphan drift detected; inspect manually before rebuild'}
    code, out, err = run(['bash', '-lc', f'cd {ROOT} && ./index-memory.sh --incremental --json'])
    actions.append({'step': 'lexical_incremental', 'exit_code': code, 'stdout': out, 'stderr': err})
    if audit['vector_state'] == 'stale-vectors':
        code, out, err = run(['bash', '-lc', f'cd {ROOT} && ./index-memory.sh --rebuild-vectors --json'])
        actions.append({'step': 'vector_rebuild', 'exit_code': code, 'stdout': out, 'stderr': err})
    return {'status': 'applied', 'actions': actions}


def main() -> int:
    p = RepairArgumentParser(description='Plan or run deterministic super_memori repair actions')
    p.add_argument('--plan', action='store_true')
    p.add_argument('--apply', action='store_true')
    p.add_argument('--classify', action='store_true')
    p.add_argument('--migrate-relations', action='store_true')
    p.add_argument('--rebuild-safe', action='store_true')
    p.add_argument('--json', action='store_true')
    args = p.parse_args()

    if not any([args.plan, args.apply, args.classify, args.migrate_relations, args.rebuild_safe]):
        args.plan = True

    state = read_state()
    semantic = semantic_runtime_status(state)
    audit = audit_memory_integrity()
    decision = classify_state(audit, semantic)
    payload = {
        'status': 'plan',
        'audit': audit,
        'semantic': semantic,
        'decision': decision,
        'actions': [],
        'warnings': [],
    }

    exit_code = 0
    if args.classify:
        payload['status'] = 'classified'
        payload['actions'] = decision
        exit_code = 2 if decision['rebuild'] or decision['rollback'] else 0
    elif args.migrate_relations:
        result = migrate_relations()
        payload['status'] = 'migrated'
        payload['actions'] = [result]
        exit_code = 0 if result['changed_files'] else 2
    elif args.rebuild_safe:
        result = rebuild_safe()
        payload['status'] = result['status']
        payload['actions'] = result.get('actions', [])
        payload['reason'] = result.get('reason')
        exit_code = 0 if result['status'] == 'applied' else 3
    elif args.apply:
        applied = []
        if audit['broken_relations']:
            applied.append({'migrate_relations': migrate_relations()})
        applied.append({'classify': decision})
        safe_result = rebuild_safe()
        applied.append({'rebuild_safe': safe_result})
        payload['status'] = 'applied'
        payload['actions'] = applied
        exit_code = 0 if safe_result.get('status') == 'applied' else 2
    else:
        payload['actions'] = decision
        payload['warnings'] = decision['continue']
        exit_code = 2 if decision['rebuild'] or decision['rollback'] or decision['continue'] else 0

    if args.json:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
    else:
        print(payload)
    return exit_code


if __name__ == '__main__':
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except Exception as exc:
        print(f'repair-memory internal error: {exc}', file=sys.stderr)
        raise SystemExit(5)


path: run-skill-memory-refresh.py
content: #!/usr/bin/env python3
from __future__ import annotations

import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT / "scripts"))
from skill_operational_memory import refresh_skill_operational_memory

if __name__ == "__main__":
    print(refresh_skill_operational_memory())


path: scripts/legacy/index-daily-v2.sh
content: #!/bin/bash
# Legacy v2 baseline preserved for reference.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
exec "$SCRIPT_DIR/index-daily.sh" "$@"


path: scripts/legacy/memorize-v2.sh
content: #!/bin/bash
# Legacy v2 baseline preserved for reference.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
exec "$SCRIPT_DIR/memorize.sh" "$@"


path: scripts/legacy/query-memory-v2.sh
content: #!/bin/bash
# Legacy v2 baseline preserved for reference.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
exec "$SCRIPT_DIR/query-memory.sh" "$@"


path: scripts/release-prep.sh
content: #!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

echo "=== Cleaning build artifacts ==="
find . -type d -name '__pycache__' -prune -exec rm -rf {} + 2>/dev/null || true
find . -type f -name '*.pyc' -delete 2>/dev/null || true
rm -rf reports/test-generated 2>/dev/null || true

echo "=== Running regression pack ==="
./tests/regression/run-all.sh

echo "=== Verifying strict release gate ==="
./validate-release.sh --strict

echo "=== Ready for super-memori packaging (current active line) ==="


path: scripts/semantic_daemon.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import os
import sys
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from typing import Any

os.environ.setdefault("OMP_NUM_THREADS", "1")
os.environ.setdefault("MKL_NUM_THREADS", "1")
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
os.environ.setdefault("NUMEXPR_NUM_THREADS", "1")
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")

ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT))

from super_memori_common import (  # noqa: E402
    EMBED_MODEL,
    QDRANT_COLLECTION,
    qdrant_request,
    get_embedding_model,
    now_iso,
    SEMANTIC_SCORE_THRESHOLD,
)

HOST = os.environ.get("SUPER_MEMORI_SEMANTIC_DAEMON_HOST", "127.0.0.1")
PORT = int(os.environ.get("SUPER_MEMORI_SEMANTIC_DAEMON_PORT", "8765"))
IDLE_TIMEOUT_SECONDS = int(os.environ.get("SUPER_MEMORI_SEMANTIC_DAEMON_IDLE_TIMEOUT", "60"))
LAST_ACTIVITY_AT = time.time()


def _touch() -> None:
    global LAST_ACTIVITY_AT
    LAST_ACTIVITY_AT = time.time()


def _embed_texts(texts: list[str], *, prefix: str) -> list[list[float]]:
    if not texts:
        return []
    model = get_embedding_model()
    prepared = [f"{prefix}: {text}" for text in texts]
    vectors = model.encode(prepared, normalize_embeddings=True)
    return [vector.tolist() for vector in vectors]


def semantic_search_via_qdrant(query: str, *, memory_type: str = "all", limit: int = 5, reviewed_only: bool = False) -> list[dict[str, Any]]:
    vector = _embed_texts([query], prefix="query")[0]
    must = []
    if memory_type != "all":
        must.append({"key": "memory_type", "match": {"value": memory_type}})
    if reviewed_only:
        must.append({"key": "reviewed", "match": {"value": 1}})
    payload: dict[str, Any] = {
        "vector": vector,
        "limit": max(limit, 1),
        "with_payload": True,
        "score_threshold": SEMANTIC_SCORE_THRESHOLD,
    }
    if must:
        payload["filter"] = {"must": must}
    data = qdrant_request(f"/collections/{QDRANT_COLLECTION}/points/search", method="POST", payload=payload, timeout=30)
    hits = data.get("result", [])
    results = []
    for hit in hits:
        point = hit.get("payload") or {}
        snippet = (point.get("snippet") or point.get("text") or "").strip().replace("\n", " ")
        results.append({
            "source_path": point.get("source_path", "?"),
            "memory_type": point.get("memory_type", "unknown"),
            "updated_at": point.get("updated_at"),
            "chunk_id": point.get("chunk_id") or hit.get("id"),
            "snippet": snippet[:280] + ("..." if len(snippet) > 280 else ""),
            "reviewed": point.get("reviewed", 1),
            "semantic_score": hit.get("score", 0.0),
            "rank": hit.get("score", 0.0),
            "relation_json": point.get("relation_json") or {},
            "conflict_status": point.get("conflict_status") or "none",
            "source_confidence": float(point.get("source_confidence") or 0.5),
        })
    return results


class Handler(BaseHTTPRequestHandler):
    server_version = "super-memori-semantic-daemon/0.1"

    def log_message(self, format: str, *args: object) -> None:
        return

    def _read_json(self) -> dict[str, Any]:
        length = int(self.headers.get("Content-Length", "0") or 0)
        raw = self.rfile.read(length) if length > 0 else b"{}"
        return json.loads(raw.decode("utf-8") or "{}")

    def _send(self, code: int, payload: dict[str, Any]) -> None:
        body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
        try:
            self.send_response(code)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
        except BrokenPipeError:
            return

    def do_GET(self) -> None:
        _touch()
        if self.path == "/health":
            ready = True
            error = None
            try:
                get_embedding_model()
            except Exception as exc:
                ready = False
                error = str(exc)
            self._send(200 if ready else 503, {"status": "ok" if ready else "error", "model": EMBED_MODEL, "ready_at": now_iso(), "error": error})
            return
        self._send(404, {"error": "not found"})

    def do_POST(self) -> None:
        _touch()
        if self.path == "/embed":
            try:
                payload = self._read_json()
                texts = payload.get("texts") or []
                prefix = str(payload.get("prefix") or "query")
                vectors = _embed_texts([str(t) for t in texts], prefix=prefix)
                self._send(200, {"vectors": vectors})
            except Exception as exc:
                self._send(500, {"error": str(exc)})
            return
        if self.path == "/semantic-search":
            try:
                payload = self._read_json()
                results = semantic_search_via_qdrant(
                    str(payload.get("query") or ""),
                    memory_type=str(payload.get("memory_type") or "all"),
                    limit=int(payload.get("limit") or 5),
                    reviewed_only=bool(payload.get("reviewed_only")),
                )
                self._send(200, {"results": results})
            except Exception as exc:
                self._send(500, {"error": str(exc)})
            return
        self._send(404, {"error": "not found"})


def main() -> int:
    try:
        os.nice(10)
    except Exception:
        pass
    try:
        import torch
        torch.set_num_threads(1)
        if hasattr(torch, 'set_num_interop_threads'):
            torch.set_num_interop_threads(1)
    except Exception:
        pass
    server = HTTPServer((HOST, PORT), Handler)
    server.timeout = 1.0
    while True:
        server.handle_request()
        if time.time() - LAST_ACTIVITY_AT > IDLE_TIMEOUT_SECONDS:
            break
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: scripts/system_hygiene_memory.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import os
import time
from pathlib import Path
from typing import Optional

from super_memori_common import MEMORY_DIR, WORKSPACE, now_iso, read_state, write_state, sha256_text

HYGIENE_DIR = MEMORY_DIR / "semantic" / "system-hygiene"
HYGIENE_FINDINGS = HYGIENE_DIR / "findings.md"
HYGIENE_CLEANUP_PLANS = HYGIENE_DIR / "cleanup-plans.md"
HYGIENE_RECOVERY = HYGIENE_DIR / "recovery-lessons.md"
SYSTEM_HYGIENE_MIN_BYTES = int(os.environ.get("SUPER_MEMORI_HYGIENE_MIN_BYTES", str(10 * 1024 * 1024)))
SYSTEM_HYGIENE_SCAN_ROOTS = [str(WORKSPACE), str(Path.home() / ".cache"), str(Path.home() / ".local" / "share")]


def _human_size(num_bytes: int) -> str:
    units = ["B", "KB", "MB", "GB", "TB"]
    size = float(num_bytes)
    for unit in units:
        if size < 1024.0 or unit == units[-1]:
            return f"{size:.1f}{unit}" if unit != "B" else f"{int(size)}B"
        size /= 1024.0
    return f"{int(size)}B"


def _path_owner_guess(path: Path) -> str:
    low = str(path).casefold()
    if "openclaw" in low:
        return "openclaw"
    if "cache" in low:
        return "cache"
    if "log" in low:
        return "log"
    if "backup" in low or "bak" in low:
        return "backup"
    return "unknown"


def _candidate_category(path: Path) -> str:
    low = path.name.casefold()
    full = str(path).casefold()
    if any(low.endswith(ext) for ext in [".log", ".out", ".err"]) or "/log" in full:
        return "stale-log"
    if any(token in low for token in ["backup", ".bak", ".old", ".orig", ".copy"]) or "/backup" in full:
        return "stale-backup"
    if any(token in full for token in ["cache", ".cache", "tmp", "temp", "/tmp/"]):
        return "cache-or-temp"
    if any(token in full for token in ["index-state", "memory.db", "telemetry.db"]):
        return "memory-index-health"
    if any(token in full for token in [".learnings", "semantic/skill-memory"]):
        return "openclaw-runtime-health"
    return "disk-bloat"


def system_hygiene_status() -> dict:
    state = read_state()
    return {
        "findings_exists": HYGIENE_FINDINGS.exists(),
        "cleanup_plans_exists": HYGIENE_CLEANUP_PLANS.exists(),
        "recovery_exists": HYGIENE_RECOVERY.exists(),
        "last_scanned_at": state.get("system_hygiene_last_scanned_at"),
        "findings_count": int(state.get("system_hygiene_findings_count", 0) or 0),
        "scan_roots": state.get("system_hygiene_scan_roots", SYSTEM_HYGIENE_SCAN_ROOTS),
        "fresh": HYGIENE_FINDINGS.exists() and bool(state.get("system_hygiene_last_scanned_at")),
        "validation_state": "current" if HYGIENE_FINDINGS.exists() else "stale",
    }


def _scan_tree(root: Path, *, min_bytes: int) -> list[dict]:
    findings: list[dict] = []
    if not root.exists():
        return findings
    for path in sorted(root.rglob("*")):
        try:
            if path.is_dir():
                continue
            if len(path.relative_to(root).parts) > 6:
                continue
            stat = path.stat()
            size = stat.st_size
            age_days = round(max(0.0, time.time() - stat.st_mtime) / 86400.0, 2)
            category = _candidate_category(path)
            if size < min_bytes and category not in {"memory-index-health", "openclaw-runtime-health"}:
                continue
            if category == "stale-log" and age_days < 7:
                continue
            if category == "stale-backup" and age_days < 14:
                continue
            full = str(path).casefold()
            findings.append({
                "finding_id": sha256_text(f"{path}:{stat.st_mtime}:{size}")[:16],
                "category": category,
                "path": str(path),
                "size": size,
                "size_human": _human_size(size),
                "age": age_days,
                "owner": _path_owner_guess(path),
                "source": str(root),
                "why_it_matters": f"{category} may consume disk space or confuse OpenClaw hygiene checks",
                "affects_openclaw": any(tok in full for tok in ["openclaw", "memory", ".learnings", "skills"]),
                "affects_os": category in {"stale-log", "stale-backup", "cache-or-temp", "disk-bloat"},
                "safe_action": "run index-memory.sh / refresh index state" if category == "memory-index-health" else ("rotate or archive after review" if category == "stale-log" else ("review duplicate backup before deletion" if category == "stale-backup" else "cleanup only if confirmed disposable")),
                "dangerous_action": "delete without confirmation" if category in {"stale-backup", "stale-log", "cache-or-temp"} else "touch without audit",
                "review_required": category != "memory-index-health",
                "confidence": 0.72 if category != "disk-bloat" else 0.56,
                "freshness": "current",
                "validation_state": "discovered",
                "last_seen": now_iso(),
                "recovery_if_removed_wrongly": f"restore from backup if {category} was deleted incorrectly",
            })
        except Exception:
            continue
    return findings


def scan_system_hygiene(*, roots: Optional[list[str]] = None, min_bytes: int = SYSTEM_HYGIENE_MIN_BYTES) -> dict:
    HYGIENE_DIR.mkdir(parents=True, exist_ok=True)
    scan_roots = [Path(r) for r in roots] if roots else [Path(r) for r in SYSTEM_HYGIENE_SCAN_ROOTS]
    findings: list[dict] = []
    for root in scan_roots:
        findings.extend(_scan_tree(root, min_bytes=min_bytes))
    findings.sort(key=lambda item: (item["category"], -(item["size"] or 0), item["path"]))
    state = read_state()
    state["system_hygiene_last_scanned_at"] = now_iso()
    state["system_hygiene_findings_count"] = len(findings)
    state["system_hygiene_scan_roots"] = [str(r) for r in scan_roots]
    write_state(state)
    HYGIENE_FINDINGS.write_text("# System Hygiene Findings\n#tags: system-hygiene, disk-bloat, stale-backups, stale-logs\n\n" + "\n".join(json.dumps(item, ensure_ascii=False) for item in findings), encoding="utf-8")
    return {
        "status": "ok",
        "findings_count": len(findings),
        "findings": findings[:200],
        "scan_roots": [str(r) for r in scan_roots],
        "last_scanned_at": state["system_hygiene_last_scanned_at"],
        "paths": {"findings": str(HYGIENE_FINDINGS), "cleanup": str(HYGIENE_CLEANUP_PLANS), "recovery": str(HYGIENE_RECOVERY)},
    }


def plan_hygiene_cleanup(*, allow_destructive: bool = False) -> dict:
    status = system_hygiene_status()
    findings = []
    if HYGIENE_FINDINGS.exists():
        for line in HYGIENE_FINDINGS.read_text(encoding="utf-8", errors="ignore").splitlines():
            if line.startswith("{"):
                try:
                    findings.append(json.loads(line))
                except Exception:
                    continue
    plans = []
    for finding in findings[:100]:
        plans.append({
            "plan_id": sha256_text(finding.get("finding_id", finding.get("path", "")))[:16],
            "finding_id": finding.get("finding_id"),
            "category": finding.get("category"),
            "path": finding.get("path"),
            "risk_level": "high" if finding.get("review_required") else "low",
            "safe_action": finding.get("safe_action", "review"),
            "dangerous_action": finding.get("dangerous_action", "touch without audit"),
            "auto_fix": bool(allow_destructive and not finding.get("review_required")),
            "requires_confirmation": not allow_destructive or bool(finding.get("review_required")),
            "recovery_if_removed_wrongly": finding.get("recovery_if_removed_wrongly"),
            "validation_state": finding.get("validation_state", "discovered"),
            "freshness": finding.get("freshness", "current"),
        })
    HYGIENE_CLEANUP_PLANS.write_text("# System Hygiene Cleanup Plans\n#tags: system-hygiene, cleanup, recovery\n\n" + "\n".join(json.dumps(item, ensure_ascii=False) for item in plans), encoding="utf-8")
    return {"status": "ok", "plans": plans, "requires_confirmation": any(p["requires_confirmation"] for p in plans), "scan_status": status}


def record_hygiene_recovery(note: str) -> dict:
    HYGIENE_DIR.mkdir(parents=True, exist_ok=True)
    timestamp = now_iso()
    entry = {"timestamp": timestamp, "note": note}
    existing = HYGIENE_RECOVERY.read_text(encoding="utf-8", errors="ignore") if HYGIENE_RECOVERY.exists() else "# System Hygiene Recovery Lessons\n#tags: system-hygiene, recovery, lessons\n\n"
    HYGIENE_RECOVERY.write_text(existing + json.dumps(entry, ensure_ascii=False) + "\n", encoding="utf-8")
    return {"status": "ok", "last_recorded_at": timestamp, "path": str(HYGIENE_RECOVERY)}


path: startup-self-check.sh
content: #!/usr/bin/env python3
# Exit codes: 0=OK, 2=WARN/degraded, 3=FAIL, 4=bad-args, 5=internal-error
from __future__ import annotations

import argparse
import json
import subprocess
import sys
import time
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parent


class StartupArgParser(argparse.ArgumentParser):
    def error(self, message: str) -> None:
        print(message, file=sys.stderr)
        raise SystemExit(4)


def run_json_command(args: list[str]) -> dict[str, Any]:
    proc = subprocess.run(
        args,
        cwd=str(ROOT),
        text=True,
        capture_output=True,
    )
    stdout = (proc.stdout or "").strip()
    stderr = (proc.stderr or "").strip()
    payload = None
    if stdout:
        try:
            payload = json.loads(stdout)
        except json.JSONDecodeError:
            payload = {"_raw_stdout": stdout}
    return {
        "command": args,
        "exit_code": proc.returncode,
        "stdout": stdout,
        "stderr": stderr,
        "payload": payload,
    }


def check_ok(health: dict[str, Any] | None, name: str) -> bool | None:
    if not isinstance(health, dict):
        return None
    checks = health.get("checks") or []
    for item in checks:
        if item.get("name") == name:
            return bool(item.get("ok"))
    return None


def should_attempt_repair(health_payload: dict[str, Any]) -> tuple[bool, str]:
    status = str(health_payload.get("status") or "FAIL")
    if status == "WARN":
        return True, "warn-state safe self-heal"
    if status != "FAIL":
        return False, "healthy"
    memory_dir_ok = check_ok(health_payload, "memory_dir")
    canonical_ok = check_ok(health_payload, "canonical_files")
    lexical_db_ok = check_ok(health_payload, "lexical_db")
    lexical_fts_ok = check_ok(health_payload, "lexical_fts")
    if memory_dir_ok and canonical_ok and (lexical_db_ok is False or lexical_fts_ok is False):
        return True, "fail-state lexical repair"
    return False, "fail-state not safely repairable"


def build_summary(report: dict[str, Any]) -> str:
    final_status = report.get("final_status") or "FAIL"
    repair_attempted = bool(report.get("repair_attempted"))
    smoke = report.get("smoke_query") or {}
    smoke_mode = (smoke.get("payload") or {}).get("mode_used")
    parts = [f"final_status={final_status}"]
    if repair_attempted:
        parts.append("repair_attempted=true")
    if smoke_mode:
        parts.append(f"smoke_mode={smoke_mode}")
    if smoke.get("exit_code") is not None:
        parts.append(f"smoke_exit={smoke['exit_code']}")
    return ", ".join(parts)


def main() -> int:
    parser = StartupArgParser(description="Startup health-check and safe self-heal for super-memori")
    parser.add_argument("--json", action="store_true")
    parser.add_argument("--repair", dest="repair", action="store_true")
    parser.add_argument("--no-repair", dest="repair", action="store_false")
    parser.add_argument("--report-file")
    parser.set_defaults(repair=True)
    args = parser.parse_args()

    started_at = time.strftime("%Y-%m-%dT%H:%M:%S%z")
    health_before = run_json_command(["./health-check.sh", "--json"])
    before_payload = health_before.get("payload") if isinstance(health_before.get("payload"), dict) else None
    if not isinstance(before_payload, dict):
        report = {
            "started_at": started_at,
            "final_status": "FAIL",
            "repair_enabled": bool(args.repair),
            "repair_attempted": False,
            "health_before": health_before,
            "error": "health-check did not return JSON",
        }
        if args.report_file:
            path = Path(args.report_file).expanduser()
            path.parent.mkdir(parents=True, exist_ok=True)
            path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
        if args.json:
            print(json.dumps(report, ensure_ascii=False, indent=2))
        else:
            print("startup self-check failed: health-check did not return JSON", file=sys.stderr)
        return 5

    repair_attempted = False
    repair_reason = "disabled"
    maintenance = None
    if args.repair:
        do_repair, repair_reason = should_attempt_repair(before_payload)
        if do_repair:
            repair_attempted = True
            maintenance = run_json_command(["./index-memory.sh", "--full", "--rebuild-vectors", "--audit", "--json"])

    health_after = run_json_command(["./health-check.sh", "--json"])
    after_payload = health_after.get("payload") if isinstance(health_after.get("payload"), dict) else None
    if not isinstance(after_payload, dict):
        after_payload = {"status": "FAIL", "warnings": ["health-check post-pass did not return JSON"]}

    smoke_query = None
    final_status = str(after_payload.get("status") or "FAIL")
    if final_status != "FAIL":
        smoke_query = run_json_command(["./query-memory.sh", "test query", "--mode", "auto", "--json", "--limit", "1"])

    report = {
        "started_at": started_at,
        "skill_root": str(ROOT),
        "repair_enabled": bool(args.repair),
        "repair_attempted": repair_attempted,
        "repair_reason": repair_reason,
        "health_before": health_before,
        "maintenance": maintenance,
        "health_after": health_after,
        "smoke_query": smoke_query,
        "final_status": final_status,
        "summary": "",
    }
    report["summary"] = build_summary(report)

    if args.report_file:
        path = Path(args.report_file).expanduser()
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")

    if args.json:
        print(json.dumps(report, ensure_ascii=False, indent=2))
    else:
        print(report["summary"])
        warnings = (after_payload.get("warnings") or []) if isinstance(after_payload, dict) else []
        if warnings:
            print("remaining warnings:")
            for item in warnings:
                print(f"- {item}")

    smoke_exit = smoke_query.get("exit_code") if isinstance(smoke_query, dict) else None
    if final_status == "OK" and smoke_exit not in {3, 4, 5}:
        return 0
    if final_status == "WARN":
        return 2
    return 3


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except SystemExit:
        raise
    except Exception as exc:
        print(f"startup-self-check internal error: {exc}", file=sys.stderr)
        raise SystemExit(5)


path: tests/regression/run-all.sh
content: #!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
python3 tests/test_temporal_retrieval.py
python3 tests/test_relation_target_validation.py
python3 tests/test_repair_plan.py
python3 tests/test_semantic_unbuilt_state.py
python3 tests/test_promotion_candidates.py
python3 tests/test_hot_change_buffer.py
python3 tests/test_startup_self_check_surface.py


path: tests/test_evals_surface.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import subprocess
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


def main() -> int:
    proc = subprocess.run([str(ROOT / 'eval-memory.sh'), '--json'], capture_output=True, text=True, check=False)
    payload = json.loads(proc.stdout)
    assert payload['status'] in {'ok', 'warn'}
    assert isinstance(payload['results'], list)
    assert payload['results'], 'eval surface should return cases'
    print('[OK] eval surface passed')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())


path: tests/test_host_profile.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import subprocess
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


def main() -> int:
    proc = subprocess.run([str(ROOT / 'health-check.sh'), '--json'], capture_output=True, text=True, check=False)
    assert proc.returncode in (0, 2), proc.stdout + proc.stderr
    payload = json.loads(proc.stdout)
    assert any(c['name'] == 'host_profile' for c in payload['checks'])
    host_profile = next(c['detail'] for c in payload['checks'] if c['name'] == 'host_profile')
    assert isinstance(host_profile, dict)
    assert 'detected_profile' in host_profile
    print('[OK] host profile truth surface passed')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())


path: tests/test_hot_change_buffer.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import os
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
os.environ.setdefault("OPENCLAW_WORKSPACE", str(Path(tempfile.mkdtemp(prefix="super-memori-hot-workspace-"))))
os.environ["SUPER_MEMORI_HOT_BUFFER_DIR"] = tempfile.mkdtemp(prefix="super-memori-hot-buffer-")

import sys
sys.path.insert(0, str(ROOT / "scripts"))

from agent_change_memory import (  # noqa: E402
    build_hot_recovery_bundle,
    compact_hot_buffer,
    detect_interrupted_change_sequence,
    hot_buffer_health_status,
    query_recent_hot_changes,
    query_unverified_recent_changes,
    record_hot_change_event,
    record_agent_change,
    record_reverted_change,
    reconcile_routine_change_history,
    query_current_known_state,
    audit_change_memory,
)


def expect(condition: bool, message: str) -> None:
    if not condition:
        raise AssertionError(message)


def main() -> int:
    file_edit = record_hot_change_event(
        action_type="edit",
        target_scope="workspace",
        affected_paths=["/tmp/demo.txt"],
        command_or_patch_summary="patched demo.txt",
        status="applied_but_unverified",
        verification_state="unverified",
        risk_level="medium",
        rollback_hint="git restore /tmp/demo.txt",
    )
    expect(file_edit["status"] == "written", "state-changing file edit should land in hot buffer")

    harmless = record_hot_change_event(
        action_type="read",
        target_scope="workspace",
        affected_paths=["/tmp/demo.txt"],
        command_or_patch_summary="cat /tmp/demo.txt",
        status="applied",
        verification_state="verified",
        risk_level="low",
    )
    expect(harmless["status"] == "skipped-noise", "harmless read must not land in hot buffer")

    recovery = build_hot_recovery_bundle(query="какие последние изменения не проверены", limit=8)
    expect(any(item.get("event_id") == file_edit["event_id"] for item in recovery["selected"]), "unverified recent change should appear in recovery bundle")

    seq = record_hot_change_event(
        action_type="sequence_start",
        target_scope="workspace",
        affected_paths=["/tmp/sequence.txt"],
        command_or_patch_summary="start risky sequence",
        status="planned",
        verification_state="unverified",
        risk_level="high",
    )
    interrupted = detect_interrupted_change_sequence(limit=8)
    expect(any(item.get("event_id") == seq["event_id"] for item in interrupted["interrupted"]), "interrupted recent sequence should be detectable")

    reverted = record_reverted_change(
        target_scope="workspace",
        action_type="rollback",
        exact_paths=["/tmp/reverted.txt"],
        command_or_patch_summary="reverted temp file change",
        rollback_method="already reverted",
        risk_level="low",
        verification_state="verified",
    )
    recent = query_recent_hot_changes(limit=16, include_reverted=True)
    reverted_event = next(item for item in recent if item.get("linked_change_id") == reverted["change_id"])
    expect(reverted_event.get("active_final_state") is False, "reverted hot event must not be shown as active final state")

    durable = record_agent_change(
        target_scope="workspace",
        action_type="config_patch",
        exact_paths=["/etc/demo.conf"],
        command_or_patch_summary="patched /etc/demo.conf",
        risk_level="medium",
        verification_state="verified",
        status="applied",
    )
    current = query_current_known_state(target_scope="workspace")
    expect("/etc/demo.conf" in current["current"], "canonical current known state should still come from durable change-memory")
    expect(any("not canonical truth" in (current.get("hot_truth_note") or "") for _ in [0]), "hot buffer should not override canonical truth")

    routine1 = record_agent_change(
        target_scope="workspace",
        action_type="reindex",
        exact_paths=["/tmp/index.db"],
        command_or_patch_summary="index-memory maintenance path",
        risk_level="low",
        verification_state="unverified",
        status="applied_but_unverified",
        related_skills=["super-memori"],
    )
    routine2 = record_agent_change(
        target_scope="workspace",
        action_type="reindex",
        exact_paths=["/tmp/index.db"],
        command_or_patch_summary="index-memory maintenance path",
        risk_level="low",
        verification_state="unverified",
        status="applied_but_unverified",
        related_skills=["super-memori"],
    )
    reconciled = reconcile_routine_change_history(
        target_scope="workspace",
        action_type="reindex",
        exact_paths=["/tmp/index.db"],
        command_or_patch_summary="index-memory maintenance path",
        risk_level="low",
        related_skill="super-memori",
        observed_effect="maintenance completed successfully; health/audit verification passed",
        verification_steps=["run health-check.sh", "run audit-memory.sh"],
    )
    expect(reconciled["status"] == "reconciled", "routine maintenance history should reconcile cleanly")
    expect(routine1["change_id"] in reconciled["matched_change_ids"], "older routine record should be part of reconciliation set")
    expect(routine2["change_id"] in reconciled["matched_change_ids"], "latest routine record should be part of reconciliation set")
    audit = audit_change_memory()
    expect(reconciled["record"].get("verification_state") == "verified", "kept routine record should end verified")
    expect(reconciled["record"].get("status") == "applied", "kept routine record should end applied")
    expect(
        all(event_id not in set(audit["hot_unverified"]) for event_id in reconciled["updated_hot_events"]),
        "reconciled routine hot events must no longer remain unverified",
    )
    interrupted_events = {item.get("event_id") for item in detect_interrupted_change_sequence(limit=16).get("interrupted", [])}
    expect(
        all(event_id not in interrupted_events for event_id in reconciled["updated_hot_events"]),
        "reconciled routine hot events must no longer remain interrupted",
    )

    for idx in range(80):
        record_hot_change_event(
            action_type="edit",
            target_scope="workspace",
            affected_paths=[f"/tmp/low-{idx}.txt"],
            command_or_patch_summary=f"low risk patch {idx}",
            status="applied",
            verification_state="verified",
            risk_level="low",
        )
    compact = compact_hot_buffer()
    expect(compact["hot_buffer_drop_count"] >= 0, "rotation should run and track drop count")

    health = hot_buffer_health_status()
    expect(health["hot_buffer_enabled"] is True, "health should report hot buffer enabled")
    expect(health["hot_buffer_size_target"] <= 128 * 1024 * 1024, "phase 1 hot buffer must stay at or below 128 MiB")

    payload = json.loads(Path(os.environ["SUPER_MEMORI_HOT_BUFFER_DIR"]) .joinpath("openclaw-super-memori-hot-buffer.json").read_text(encoding="utf-8"))
    serialized = json.dumps(payload, ensure_ascii=False)
    expect("/etc/demo.conf" not in serialized or "patched /etc/demo.conf" in serialized, "hot buffer should keep only compact summaries, not full file snapshots")

    print("[OK] hot change buffer phase-1 cases passed")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: tests/test_skill_lesson_quality.py
content: #!/usr/bin/env python3
from __future__ import annotations

import json
import subprocess
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


def main() -> int:
    proc = subprocess.run([str(ROOT / 'eval-memory.sh'), '--json'], capture_output=True, text=True, check=False)
    payload = json.loads(proc.stdout)
    assert payload['status'] in {'ok', 'warn'}
    assert payload['results'], 'expected eval cases'
    print('[OK] skill lesson quality eval passed')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)