# DragonScale Memory Guide DragonScale Memory is an optional extension for `claude-obsidian`. It adds conservative helpers for log rollups, stable page addresses, duplicate-page linting, and frontier topic suggestion. Start with [docs/install-guide.md](./install-guide.md). For the design spec and rationale, read [wiki/concepts/DragonScale Memory.md](../wiki/concepts/DragonScale%20Memory.md). This page stays close to shipped behavior in `v1.6.0`. It explains what setup creates, what each mechanism actually does, what it needs, and how to turn it off safely without uninstalling the repo. ## What DragonScale Is ### Scope and opt-in status DragonScale is a memory-layer extension for the wiki. It covers rollups, deterministic page IDs, duplicate detection, and one opt-in topic-selection path for `/autoresearch`. It is not required for the base vault. If you never run `bash bin/setup-dragonscale.sh`, the base install and the original skill behavior remain in place. The repo uses feature detection so DragonScale can stay optional instead of becoming a hard dependency. The concept page is broader than this guide. This guide is operational. When the spec and implementation differ in detail, prefer the shipped scripts and skills for day-to-day behavior. ### What ships in 1.6.0 Version `1.6.0` ships all four DragonScale mechanisms as opt-in features: - Mechanism 1, Fold Operator: `skills/wiki-fold/` - Mechanism 2, Deterministic Page Addresses: `scripts/allocate-address.sh` plus `wiki-ingest` and `wiki-lint` integration - Mechanism 3, Semantic Tiling Lint: `scripts/tiling-check.py` plus `wiki-lint` integration - Mechanism 4, Boundary-First Autoresearch: `scripts/boundary-score.py` plus `skills/autoresearch/SKILL.md` Topic Selection logic Use `CHANGELOG.md` for the release trail, [docs/install-guide.md](./install-guide.md) for the quick-start view, and [wiki/concepts/DragonScale Memory.md](../wiki/concepts/DragonScale%20Memory.md) for the full design context. ## Before You Enable It ### Base install requirements DragonScale is an add-on, not a replacement for base setup. Do the normal vault install first by following [docs/install-guide.md](./install-guide.md). At minimum: - clone the repo or install the plugin - run `bash bin/setup-vault.sh` - open the folder as an Obsidian vault - use `/wiki` to scaffold or continue setup The DragonScale setup script accepts one optional argument, the vault path: ```bash bash bin/setup-dragonscale.sh ``` ```bash bash bin/setup-dragonscale.sh /path/to/vault ``` If you omit the path, it uses the repo root inferred from `bin/`. ### Universal prerequisite: flock `flock` is the universal prerequisite. Mechanism 2 uses it directly in `scripts/allocate-address.sh` to guard `.vault-meta/.address.lock`. Mechanism 3 uses flock from Python to guard `.vault-meta/.tiling.lock` around cache I/O. Quick check: ```bash command -v flock ``` If that prints nothing, install `flock` before relying on DragonScale. On Linux it is usually already present. On macOS it commonly comes from `util-linux`. If `flock` is missing, setup can still create files, but the address allocator and tiling cache path are not reliable. Treat that as a blocker. ### Mechanism 3 extra prerequisites: python3, ollama, nomic-embed-text Mechanism 3 is the only mechanism with the full local embeddings stack. You need `python3`, `ollama`, and the `nomic-embed-text` model pulled into ollama. Useful checks: ```bash command -v python3 ``` ```bash curl -sS http://127.0.0.1:11434/api/version ``` ```bash ollama pull nomic-embed-text ``` The setup script does not install any of those. It only checks and reports status. Mechanism 4 needs `python3`, but not ollama. Mechanisms 1 and 2 do not need either. ### What happens when optional deps are missing DragonScale is meant to fail closed or no-op cleanly. If `python3` is missing: - Mechanism 3 cannot run - Mechanism 4 cannot run - Mechanisms 1 and 2 still work If ollama is unreachable, `scripts/tiling-check.py` exits `10`. If ollama is reachable but `nomic-embed-text` is not installed, it exits `11`. `wiki-lint` is expected to treat those as skip conditions for semantic tiling, not as a reason to break the rest of the lint flow. If the boundary helper fails, `/autoresearch` falls back to the normal ask-the-user topic path. It does not force a candidate list and it does not improvise a topic. If DragonScale setup has never been run, `wiki-ingest` and `wiki-lint` keep their non-DragonScale behavior. ## Setup ### Run bin/setup-dragonscale.sh Run: ```bash bash bin/setup-dragonscale.sh ``` The script is idempotent. It is safe to re-run and it does not overwrite the runtime files it already created. Before provisioning state, it verifies: - `scripts/allocate-address.sh` - `scripts/tiling-check.py` - `skills/wiki-fold/SKILL.md` If any of those are missing, setup stops and tells you to reinstall the plugin. What setup does: - makes `scripts/allocate-address.sh` executable - makes `scripts/tiling-check.py` executable - creates `.vault-meta/` if needed - creates address, tiling, and legacy-baseline state files if missing - creates `.raw/.manifest.json` if missing - runs sanity checks at the end What setup does not do: - install ollama - pull `nomic-embed-text` - backfill addresses onto old pages - run a fold - run semantic tiling - rewrite your existing wiki pages ### What files and state it creates Setup provisions a small amount of runtime state. In `.vault-meta/` it creates: - `address-counter.txt` - `tiling-thresholds.json` - `legacy-pages.txt` In `.raw/` it creates: - `.manifest.json` `address-counter.txt` starts at `1`, so the next reserved page address in a brand-new vault will be `c-000001`. `tiling-thresholds.json` is seeded with `error: 0.90`, `review: 0.80`, and `calibrated: false`. These are conservative seed bands, not calibrated truth for your vault. `legacy-pages.txt` gets a rollout marker comment: ```text # rollout: YYYY-MM-DD ``` `wiki-lint` uses that baseline to separate legacy pages from post-rollout pages for address enforcement. `.raw/.manifest.json` starts with empty `sources` and `address_map` objects. The ingest skill maintains that file. The source documents under `.raw/` remain immutable. ### How to verify setup The setup script already performs sanity checks, but it is useful to verify a few things yourself. Check the next address without reserving one: ```bash ./scripts/allocate-address.sh --peek ``` Check that runtime state exists: ```bash ls -1 .vault-meta ``` Check tiling readiness without computing embeddings: ```bash python3 ./scripts/tiling-check.py --peek ``` Check the boundary helper: ```bash python3 ./scripts/boundary-score.py --top 5 ``` If your vault is small or tightly integrated, the boundary helper may report no positive-score frontier pages. That is still a valid run. ## Mechanism 1: Fold Operator ### What it does The fold operator is a log rollup. It reads the most recent `2^k` entries from `wiki/log.md` and produces an extractive fold page under `wiki/folds/`. The fold is additive. It does not delete, move, or rewrite the child entries. The fold is extractive. Every outcome and theme in the output must be traceable to a child log entry. The current shipped skill is intentionally narrow. It supports a flat fold over raw log entries. Hierarchical fold-of-folds behavior remains outside the scope of the current skill even though the concept spec discusses stacked folds. The fold ID is deterministic for a given range: ```text fold-k{K}-from-{EARLIEST-DATE}-to-{LATEST-DATE}-n{COUNT} ``` That gives structural idempotency. If the exact fold already exists, the skill should stop instead of writing a duplicate. ### When to use it Use a fold when the log has accumulated a coherent batch of work and you want a checkpoint page that is easier to scan than a raw run of entries. Typical cases: - after several ingests on one theme - after a burst of research sessions - before a long flat `wiki/log.md` gets harder to use Do not treat folds as garbage collection. They summarize. They do not compact by deletion. Example command: ```text fold the log, dry-run k=3 ``` That asks for a dry-run over `2^3 = 8` entries. ### Dry-run vs commit mode Dry-run is the default and it is stdout-only. That matters because the repo has a PostToolUse hook for writes. In dry-run mode: - no file is written - no auto-commit hook is triggered - you get the proposed fold content in the terminal output In commit mode: - the fold page is written to `wiki/folds/` - `wiki/index.md` is updated - `wiki/log.md` gets a new fold entry The skill docs expect three separate write operations in commit mode, so three auto-commits from the hook are normal. Example commit command: ```text fold the log, commit k=3 ``` Run a dry-run first. Commit only if the fold content looks right. To disable Mechanism 1 without uninstalling DragonScale, stop invoking `wiki-fold`. Existing fold pages can remain in the vault, or you can remove them manually if you no longer want them. ## Mechanism 2: Deterministic Page Addresses ### Address format and rollout policy Mechanism 2 assigns stable frontmatter addresses. The shipped format is: ```yaml address: c-000042 ``` `c-` means creation-order counter. The numeric part is zero-padded to six digits. This is not a content hash. The spec is explicit that the shipped address is deterministic and stable, but not content-addressable. The rollout baseline is `2026-04-23`. After DragonScale adoption, post-rollout non-meta pages are expected to have addresses. Legacy pages are exempt until you do a deliberate backfill. The helper has three real modes: ```bash ./scripts/allocate-address.sh ``` ```bash ./scripts/allocate-address.sh --peek ``` ```bash ./scripts/allocate-address.sh --rebuild ``` The default mode reserves and prints the next address. `--peek` is read-only. `--rebuild` recomputes the counter from the highest observed `c-NNNNNN`. Example command: ```bash ./scripts/allocate-address.sh --peek ``` ### How ingest and lint use it `wiki-ingest` enables address assignment only when `./scripts/allocate-address.sh` is executable and `./.vault-meta` exists. If both conditions are true, new non-meta pages get `address:` in frontmatter. If not, ingest proceeds without addresses. `wiki-lint` enables address validation only when `./scripts/allocate-address.sh` is executable and `./.vault-meta/address-counter.txt` exists. If those conditions are true, lint checks address format, uniqueness, counter consistency against `--peek`, missing addresses on post-rollout pages, and `address_map` consistency in `.raw/.manifest.json`. The single-writer rule matters here. The allocator uses `flock`, but the ingest skill still says Phase 2 is single-writer only. Do not run parallel ingests from multiple sessions or sub-agents that assign addresses. One hard rule from the skill docs is worth repeating. Never edit `.vault-meta/address-counter.txt` directly. Only mutate it through `scripts/allocate-address.sh`. To disable Mechanism 2 without uninstalling: 1. stop running ingests that depend on address assignment 2. remove `.vault-meta/` if you want feature detection to turn off 3. stop using `./scripts/allocate-address.sh` Existing `address:` fields can stay on pages. They become inert metadata if the feature is disabled. ## Mechanism 3: Semantic Tiling Lint ### What it checks Mechanism 3 is an embedding-based duplicate-page detector. It scans markdown files under `wiki/` and excludes: - `wiki/folds/` - `wiki/meta/` - common meta filenames such as `index.md`, `log.md`, `hot.md`, `overview.md`, `dashboard.md`, `Wiki Map.md`, and `getting-started.md` - files with `type: meta` - files with `type: fold` - symlinks or paths that escape the vault root It computes one embedding per included page, compares pairs by cosine similarity, and emits candidate overlap in bands. Default bands: - `>= 0.90` as error - `0.80 - 0.90` as review - `< 0.80` as pass The helper never auto-merges pages. It only reports candidates for review. Example command: ```bash python3 ./scripts/tiling-check.py --peek ``` That gives structured diagnostics without computing embeddings. ### Local embeddings requirement By default, the helper only trusts a local ollama endpoint at `http://127.0.0.1:11434`. Remote ollama endpoints require an explicit override flag because page bodies are sent as embedding input. Remote override example: ```bash python3 ./scripts/tiling-check.py --allow-remote-ollama --peek ``` The normal ready path is local: 1. `python3` is installed 2. ollama is reachable on localhost 3. `nomic-embed-text` is installed in ollama Important exit codes: - `0` success - `10` ollama unreachable - `11` model missing `wiki-lint` is written to treat those as skip conditions. ### Calibration and no-op behavior The shipped thresholds are conservative seeds, not calibrated truth. The skill docs call for a manual one-time calibration pass per vault. Until you do that, expect both false negatives and false positives. The helper also has intentional no-op behavior. If ollama or the model is missing, it exits with the skip code. It does not fake results. Useful commands: ```bash python3 ./scripts/tiling-check.py --peek ``` ```bash python3 ./scripts/tiling-check.py --rebuild-cache ``` ```bash python3 ./scripts/tiling-check.py --report wiki/meta/tiling-report-YYYY-MM-DD.md ``` `--report` is real and path-confined to the vault. Use it when you want a saved report. Use `--peek` when you only want readiness and diagnostics. To disable Mechanism 3 without uninstalling: 1. stop running `python3 ./scripts/tiling-check.py` 2. stop using the semantic-tiling path in `wiki-lint` 3. do not provision ollama or the model if you do not need them Note that `.vault-meta/` is a shared gate for Mechanisms 2, 3, and 4. Do not remove it to disable Mechanism 3 alone, or you will also turn off address allocation and boundary-first autoresearch. The tiling cache lives under `.vault-meta/` but is inert when the helper is not invoked. ## Mechanism 4: Boundary-First Autoresearch ### What it does Mechanism 4 scores frontier pages in the wiki graph. The shipped formula is: ```text boundary_score(p) = (out_degree(p) - in_degree(p)) * recency_weight(p) ``` In practice, high-score pages point outward to many scoreable pages, receive relatively fewer inbound links, and were updated recently enough to still be frontier-like. The helper reads `wiki/**/*.md`, builds a wikilink graph, and emits ranked results to stdout or JSON. It is intentionally stdout-only. Unlike the tiling helper, it has no `--report PATH` mode. Example command: ```bash python3 ./scripts/boundary-score.py --json --top 5 ``` That is the exact command the autoresearch skill uses for candidate generation. ### Agenda-control caveat This caveat is explicit in both the spec and the skill docs. This is agenda control, not pure memory. Mechanism 4 does not just describe the vault. It influences what the agent is likely to research next. That crosses the memory and planning boundary. The project keeps it opt-in and labels it honestly. If you want the strict memory-layer subset only, omit this path. Do not use `/autoresearch` without a topic, or do not set up and invoke the boundary scorer. ### How /autoresearch behaves with and without it With Mechanism 4 available, and only when `/autoresearch` is invoked without a topic, the skill: 1. checks for `scripts/boundary-score.py` 2. checks for `./.vault-meta` 3. checks for `python3` 4. runs `./scripts/boundary-score.py --json --top 5` 5. presents the top frontier pages as candidate topics 6. lets the user pick, override with free text, or decline If the helper exits non-zero, returns invalid JSON, or returns an empty `results` array, the skill falls back. Without Mechanism 4, or after fallback, `/autoresearch` simply asks: ```text What topic should I research? ``` The helper suggests. The user still decides. To disable Mechanism 4 without uninstalling: 1. stop running `python3 ./scripts/boundary-score.py` 2. use `/autoresearch [topic]` with an explicit topic 3. avoid the no-topic `/autoresearch` path if you do not want frontier suggestions Note that `.vault-meta/` is a shared gate for Mechanisms 2, 3, and 4. Do not remove it to disable Mechanism 4 alone. The scorer itself is read-only and uses no shared state; disabling it just means not invoking it. ## Operational Policies ### Single-writer rule DragonScale assumes a single writer for the address-assignment path. The allocator is flock-guarded, which protects the counter from simple races. It does not turn the whole wiki into a safe multi-writer system. The ingest skill is explicit here. Do not run parallel ingests from multiple Claude sessions or sub-agents that assign addresses. The safe operating policy is: - one active ingest writer at a time - one address allocator path at a time - no direct manual edits to counter state Mechanism 1 is human-invoked and easy to serialize. Mechanism 3 uses a lock for cache I/O. Mechanism 4 is read-only. ### Feature detection and graceful fallback DragonScale is meant to be feature-detected, not assumed. `wiki-ingest` only assigns addresses when the allocator is executable and `.vault-meta/` exists. `wiki-lint` only validates addresses when the allocator exists and `.vault-meta/address-counter.txt` exists. `wiki-lint` only runs semantic tiling when the helper exists and `python3` is available, then interprets readiness from `--peek`. `autoresearch` only uses boundary-first selection when the helper exists, `.vault-meta/` exists, and `python3` is present. When those conditions are not met, the repo falls back to earlier behavior. That is the intended operational posture. ## Troubleshooting ### Missing flock If `flock` is missing, fix that first. Symptoms can include an unsafe address-allocation path or a tiling cache path that cannot lock correctly. Check: ```bash command -v flock ``` If it is absent, install the package that provides it for your system, then rerun: ```bash bash bin/setup-dragonscale.sh ``` Do not work around this by editing `.vault-meta/address-counter.txt` directly. ### Missing ollama or model This only blocks Mechanism 3. It does not block the rest of DragonScale. Check ollama reachability: ```bash curl -sS http://127.0.0.1:11434/api/version ``` Check tiling readiness: ```bash python3 ./scripts/tiling-check.py --peek ``` If the helper exits `10`, ollama is not reachable. If it exits `11`, pull the model: ```bash ollama pull nomic-embed-text ``` Then rerun: ```bash python3 ./scripts/tiling-check.py --peek ``` Remember that Mechanism 4 does not need ollama. If you only want boundary-first autoresearch, `python3` is enough. ### Safe rollback / disable path You do not need to uninstall the repo to turn DragonScale off. Use the smallest rollback that fits what you want: - Mechanism 1: stop invoking `wiki-fold`. It uses no shared state. - Mechanism 2: stop using `./scripts/allocate-address.sh`. Existing `address:` frontmatter fields remain as plain content. - Mechanism 3: stop running `python3 ./scripts/tiling-check.py` and stop invoking the semantic-tiling path in `wiki-lint`. Cache under `.vault-meta/` is inert when not used. - Mechanism 4: stop running `python3 ./scripts/boundary-score.py` and avoid the no-topic `/autoresearch` path. The scorer is read-only; disabling is not invoking it. `.vault-meta/` is a shared gate for Mechanisms 2, 3, and 4. Removing it disables all three together, not just one. If you want to disable DragonScale feature detection across the setup-based mechanisms at once, remove `.vault-meta/`: ```bash rm -rf .vault-meta ``` Then stop invoking the DragonScale-specific helpers and skills. This leaves your normal wiki content intact. It does not remove fold pages, and it does not strip existing `address:` fields from frontmatter. Those remain as plain content unless you choose to clean them up manually. If you later want DragonScale back, rerun: ```bash bash bin/setup-dragonscale.sh ```